查找使用指定数字范围计算数字所需的最小操作数

时间:2012-01-16 20:14:13

标签: algorithm math

让我先举一个例子 - 我有一系列从1到9的数字。让我们说我想要的目标数是29。 在这种情况下,所需的最小操作数将是(9 * 3)+2 = 2次操作。类似地,对于18,最小操作数是1(9 * 2 = 18)。 我可以使用4个算术运算符中的任何一个 - +, - ,/和*。

如何以编程方式找出所需的最少操作数? 提前感谢您提供的任何帮助。

澄清:仅整数,中间计算不允许小数。即以下内容无效(来自以下评论):(( 9/2 )+ 1)* 4 == 22 我必须承认我没有彻底考虑这个问题,但就我的目的而言,十进制数字是否出现在计算中间并不重要。 ((9/2)+ 1)* 4 == 22有效。对不起,感到困惑。

4 个答案:

答案 0 :(得分:4)

对于设置Y = [1..9]且n> 1的特殊情况。 0:

  • n< = 9:0操作
  • n< = 18:1操作(+)
  • 否则:删除在Y中找到的任何除数。如果这还不够,请对所有偏移量的余数进行递归-9 .. +9。可以跳过偏移0,因为它已经尝试过。

注意在这种情况下如何不需要分割。对于其他Y,这不成立。

该算法在log(n)中呈指数形式。对于那些对代数有更多了解的人来说,准确的分析是一项工作。

要获得更快的速度,请添加修剪以消除对较大数字的搜索。

示例代码:

def findop(n, maxlen=9999):
    # Return a short postfix list of numbers and operations

    # Simple solution to small numbers
    if n<=9: return [n]
    if n<=18: return [9,n-9,'+']

    # Find direct multiply
    x = divlist(n)
    if len(x) > 1:
        mults = len(x)-1
        x[-1:] = findop(x[-1], maxlen-2*mults)
        x.extend(['*'] * mults)
        return x

    shortest = 0

    for o in range(1,10) + range(-1,-10,-1):
        x = divlist(n-o)
        if len(x) == 1: continue
        mults = len(x)-1

        # We spent len(divlist) + mults + 2 fields for offset. 
        # The last number is expanded by the recursion, so it doesn't count. 
        recursion_maxlen = maxlen - len(x) - mults - 2 + 1

        if recursion_maxlen < 1: continue
        x[-1:] = findop(x[-1], recursion_maxlen)
        x.extend(['*'] * mults)
        if o > 0:
            x.extend([o, '+'])
        else:
            x.extend([-o, '-'])
        if shortest == 0 or len(x) < shortest:
            shortest = len(x)
            maxlen = shortest - 1
            solution = x[:]

    if shortest == 0:
        # Fake solution, it will be discarded
        return '#' * (maxlen+1)
    return solution

def divlist(n):
    l = []
    for d in range(9,1,-1):
        while n%d == 0:
            l.append(d)
            n = n/d
    if n>1: l.append(n)
    return l

答案 1 :(得分:2)

基本思路是使用k操作测试所有可能性,k0开始。想象一下,您创建了一个高度为k的树,该树以操作数(每个级别4 * 9个分支)为每个可能的新操作分支。在移动到下一个k之前,您需要遍历并评估每个k树的

我没有测试这个伪代码:

for every k from 0 to infinity
  for every n from 1 to 9
    if compute(n,0,k):
      return k


boolean compute(n,j,k):
  if (j == k):
    return (n == target)
  else:
    for each operator in {+,-,*,/}:
       for every i from 1 to 9:
         if compute((n operator i),j+1,k):
           return true
    return false

它没有考虑算术运算符的优先级和大括号,这需要一些返工。

答案 2 :(得分:2)

非常酷的问题:)

请注意,您可以从头开始!从你的例子(9 * 3)+2 = 29相当于说(29-2)/ 3 = 9。这样我们就可以避免在赛博格的回答中出现双循环。这建议使用以下算法设置Y和结果r

nextleaves = {r}
nops = 0
while(true):
   nops = nops+1
   leaves = nextleaves
   nextleaves = {}
   for leaf in leaves:
      for y in Y:
         if (leaf+y) or (leaf-y) or (leaf*y) or (leaf/y) is in X:
             return(nops)
         else:
             add (leaf+y) and (leaf-y) and (leaf*y) and (leaf/y) to nextleaves

这是基本的想法,性能当然可以得到改善,例如避免“回溯”,例如r+a-ar*a*b/a

答案 3 :(得分:1)

我想我的想法与Peer Sommerlund相似:

对于大数字,通过乘以大密码,你可以快速前进。

Y = 29素数?如果不是,则将其除以(2到9)的最大分频器。 否则你可以减去一个数字,以达到一个可分数。 27很好,因为它可以被9分割,所以

(29-2)/9=3 => 
3*9+2 = 29 

所以也许 - 我没有考虑到这一点:搜索下一个可以被Y下方的9号整除。如果你没有达到一个数字,重复一遍。

公式是相反的步骤。

(我会尝试一些数字。:))

我试过2551,这是

echo $((((3*9+4)*9+4)*9+4))

但我没有测试每个中间结果是否是素数。 但是

echo $((8*8*8*5-9)) 

少了2次操作。也许我可以稍后调查一下。