仅使用加法,除法和乘法以固定数量的步骤达到数字的算法

Posted

技术标签:

【中文标题】仅使用加法,除法和乘法以固定数量的步骤达到数字的算法【英文标题】:Algorithm to reach a number in a fixed amount of steps using addition, division and multiplication only 【发布时间】:2011-06-27 22:33:00 【问题描述】:

在工作中开发一款游戏,并且在游戏中的某个时刻,玩家被扔进了奖金游戏。他们需要赢得的金额是预先确定的,但是我们想提出一种算法,该算法使用加法、乘法和除法来以 x 步数获得该金额。步数也会提前知道,因此算法只需要弄清楚如何使用该步数来达到这个数字。

您可以使用的唯一计算是 +1 到 +15、x2、x4、/2、/4。 您可以在步骤中超过目标数,但必须在最后一步达到目标数。步长通常在 15 到 30 之间,您总是从 0 开始。

例如: 数量:100,步数:10 == +10、+2、x2、+4、x4、+10、/2、+15、+15、+9

数量:40,步数:12 == +15、+1、+5、+2、+1、/2、*4、+6、+6、/4、+5、*2

我很好奇这样的东西是否已经存在?我确信我们可以想出办法,但如果有一个通用算法可以处理这项工作,我不想重新发明***。


更新:对@FryGuy 的代码进行了一些小改动,使其成为到达目标号码的路线有点随机。他的解决方案按原样工作得很好,但在看到它工作并考虑到@Argote 和@Moron 的 cmets 之后,我意识到它需要有一定程度的随机化才能吸引我们的玩家。在 10 个步骤中添加 +1 以达到 10 的目标数字效果很好,但就我们如何使用它而言,它是“无聊的”。非常感谢所有评论和回答的人。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CR

    class Program
    
        static void Main(string[] args)
        
            while (true)
            
                int targetNumber = 20;
                int steps = 13;
                int[] route = null;
                Boolean routeAcceptable = false;

                // Continue choosing routes until we find one that is acceptable (doesn't average above or target win, but does exceed it at least once)
                while(!routeAcceptable)
                
                    routeAcceptable = CalculateRoute(targetNumber, steps, out route) && route.Average() < targetNumber && route.Max() > targetNumber;
                

                foreach (int i in route.Reverse())
                
                    Console.WriteLine(i);
                
                Console.WriteLine("-----------------------");
                Console.ReadLine();
            
        

        static Boolean CalculateRoute(int targetNumber, int numSteps, out int[] route)
        
            int maxValue = targetNumber * 16;
            bool[,] reachable = new bool[numSteps + 1, maxValue];

            // build up the map
            reachable[0, 0] = true;
            for (int step = 0; step < numSteps; step++)
            
                for (int n = 0; n < maxValue; n++)
                
                    if (reachable[step, n])
                    
                        foreach (int nextNum in ReachableNumbersFrom(n))
                        
                            if (nextNum < maxValue && nextNum > 0)
                            
                                reachable[step + 1, nextNum] = true;
                            
                        
                    
                
            

            // figure out how we got there
            int[] routeTaken = new int[numSteps + 1];
            int current = targetNumber;
            for (int step = numSteps; step >= 0; step--)
            
                routeTaken[step] = current;
                bool good = false;

                // Randomize the reachable numbers enumeration to make the route 'interesting'
                foreach (int prev in RandomizedIEnumerbale(ReachableNumbersFromReverse(current)))
                
                    if (prev < targetNumber * 8)
                    
                        if (reachable[step, prev])
                        
                            current = prev;
                            good = true;

                            // Avoid hitting the same number twice, again to make the route 'interesting'
                            for (int c = numSteps; c >= 0; c--)
                            
                                reachable[c, prev] = false;
                            
                            break;
                        
                    
                

                if (!good)
                
                    route = routeTaken;
                    return false;
                
            

            route = routeTaken;
            return true;
        

        static IEnumerable<int> ReachableNumbersFrom(int n)
        
            // additions
            for (int i = 1; i <= 15; i++)
            
                yield return n + i;
            

            // mults/divides
            yield return n / 2;
            yield return n / 4;
            yield return n * 2;
            yield return n * 4;
        

        static IEnumerable<int> ReachableNumbersFromReverse(int n)
        
            // additions
            for (int i = 1; i <= 15; i++)
            
                if (n - i >= 0)
                    yield return n - i;
            

            // mults/divides
            if (n % 2 == 0)
                yield return n / 2;
            if (n % 4 == 0)
                yield return n / 4;
            yield return n * 2;
            yield return n * 4;
        

        static IEnumerable<int> RandomizedIEnumerbale(IEnumerable<int> enumerbale)
        
            Random random = new Random(System.DateTime.Now.Millisecond);
            return (
                from r in
                    (
                        from num in enumerbale
                        select new  Num = num, Order = random.Next() 
                    )
                orderby r.Order
                select r.Num
                );
        
    

【问题讨论】:

你不能在快速到达目标后用 x2, /2 填充台阶吗?还有其他限制吗? 我认为您希望路线“有趣”而不是可预测的行为模式。我认为不会有真正的现有解决方案,因为算法解决方案往往完全围绕速度/内存而不是“兴趣”。 @Moron - 是的,我们可以,但正如@El Ronnoco 指出的那样,我们希望它有趣。用户不知道他们会赢得什么,所以我们希望它在进行过程中变得情绪化/令人兴奋。如果这有意义? @Wesley:有趣是一个主观词。根据您认为有趣的内容,您可以添加约束(这就是我问您是否有任何约束的原因)。例如,一个约束可能是没有数字重复。所以你不能立即做 x2 然后 /2 等等。只是说你想要一些“有趣”的东西是没有意义的。简而言之:您决定什么是有趣的,并为此添加约束。 @Morno - 是的,你有一个很好的观点。我想我没有想过在我最初列出的内容之外会有什么类型的约束。我很欣赏这种洞察力,它让我思考得更努力了,这是一件好事。 【参考方案1】:

您可以使用深度为 N 级的搜索树来强制执行此操作,其中 N 是步数。这将是 O(m^n),其中 m 是允许的操作数。

可能有更好的算法,但这应该适用于较小的 N 值。

例如,使用 Breadth, Depth-First Search 或 A*。

【讨论】:

@David:很好的例子,我还应该注意,考虑到N 步骤上的M 运算符,这可能会返回从号码X 到号码Y 的所有可能路径。 @David:你会在这里使用什么作为 A* 的可接受启发式? @Matthew:“算术运算使我们最接近期望的分数(不超过)。” @David:嗯,一个可以继续使用除法运算符仍然可以返回,适当的启发式并不那么容易定义。 我同意你的看法。启发式可能很难定义。我只是在问题的限制范围内提出了我能想到的最简单的一个。【参考方案2】:

我会使用动态编程。首先,建立一张地图,标出每一步都可以到达哪些数字,然后回溯以了解您是如何到达那里的:

void CalculateRoute(int targetNumber, int numSteps)

    int maxValue = targetNumber * 16;
    bool[,] reachable = new bool[numSteps + 1, maxValue];

    // build up the map
    reachable[0, 0] = true;
    for (int step = 0; step < numSteps; step++)
    
        for (int n = 0; n < maxValue; n++)
        
            if (reachable[step, n])
            
                foreach (int nextNum in ReachableNumbersFrom(n))
                
                    if (nextNum < maxValue && nextNum >= 0)
                        reachable[step + 1, nextNum] = true;
                
            
        
    

    // figure out how we got there
    int current = targetNumber;
    for (int step = numSteps; step >= 0; step--)
    
        Console.WriteLine(current);

        bool good = false;
        foreach (int prev in ReachableNumbersFromReverse(current))
        
            if (reachable[step, prev])
            
                current = prev;
                good = true;
                break;
            
        

        if (!good)
        
            Console.WriteLine("Unable to proceed");
            break;
        
    


IEnumerable<int> ReachableNumbersFrom(int n)

    // additions
    for (int i = 1; i <= 15; i++)
        yield return n + i;

    // mults/divides
    yield return n / 2;
    yield return n / 4;
    yield return n * 2;
    yield return n * 4;


IEnumerable<int> ReachableNumbersFromReverse(int n)

    // additions
    for (int i = 1; i <= 15; i++)
        yield return n - i;

    // mults/divides
    if (n % 2 == 0)
        yield return n / 2;
    if (n % 4 == 0)
        yield return n / 4;
    yield return n * 2;
    yield return n * 4;

【讨论】:

+1 我的数学很烂,但对于我只是模糊理解的问题,这是一个很好的答案:)【参考方案3】:

从您想要的解决方案向后工作 由于您的除法和乘法仅由 2 和 4 组成,因此很容易知道您何时可以或不能执行这些操作 然后,对于最后的 4-5 步,你可以让它任意返回 0

添加到这个;您可以在初始阶段使用随机操作,检查您没有执行非法操作,您还应该包括检查范围。您不希望得到一个像 400 这样的数字,然后必须在最后一次操作中将它除以 4 多次,才能回到 0。

【讨论】:

以上是关于仅使用加法,除法和乘法以固定数量的步骤达到数字的算法的主要内容,如果未能解决你的问题,请参考以下文章

高精度加法,减法,乘法,除法

请问怎样用加法-移位实现定点乘除法?

仅使用O(lgβ)乘法和除法将β位整数转换为数字数组

MIPS计算器用减法和加法实现除法,避免DIV和REM指令

一个基础而奇怪的问题:算法执行加法乘法除法性能无区别?

加法 减法 乘法 除法计算