【问题标题】:Find the minimum number of operations required to compute a number using a specified range of numbers使用指定的数字范围查找计算数字所需的最小操作次数
【发布时间】:2012-02-15 20:07:30
【问题描述】:

让我从一个例子开始 - 我有一个从 1 到 9 的数字范围。假设我想要的目标数字是 29。 在这种情况下,所需的最小操作数将是 (9*3)+2 = 2 次操作。同样,对于 18,最小操作数为 1 (9*2=18)。 我可以使用 4 种算术运算符中的任何一种 - +、-、/ 和 *。

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

澄清:仅整数,计算中不允许小数。即以下无效(来自下面的 cmets): ((9/2) + 1) * 4 == 22 我必须承认我没有彻底考虑这一点,但就我的目的而言,十进制数字是否出现在计算中间并不重要。 ((9/2) + 1) * 4 == 22 是有效的。很抱歉造成混乱。

【问题讨论】:

  • 有一个简单的回溯解决方案,但它是指数级的。你有时间复杂度限制吗?
  • @KennyTM - tnx。我应该用一个更好的例子。
  • @amit - 没有时间复杂度限制。
  • 仅整数算术,我想?即 ((9/2) + 1) * 4 == 22 怎么样? 9/2 允许吗?澄清问题。
  • 其次,“仅整数”究竟是什么意思?这是否意味着完全禁止9/2?或者这是否意味着9/2 被假定为4 而不是4.5

标签: algorithm math


【解决方案1】:

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

  • n
  • n
  • 否则:删除在 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

【讨论】:

    【解决方案2】:

    基本思想是使用k 操作测试所有可能性,对于从0 开始的k。想象一下,您创建了一个高度为 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
    

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

    【讨论】:

    • 螺丝中缀。使用堆栈。 Paren 和 operator 优先级无关紧要,您可以翻译回最终答案。这种技术绝对是要走的路。
    【解决方案3】:

    真的很酷的问题:)

    请注意,您可以从头开始!从您的示例中 (9*3)+2 = 29 相当于说 (29-2)/3=9。这样我们就可以避免 cyborg 回答中的双重循环。这表明集合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

    【讨论】:

      【解决方案4】:

      我想我的想法类似于 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 次操作。也许我以后可以调查一下。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-09
        • 2020-11-28
        • 1970-01-01
        • 1970-01-01
        • 2019-03-05
        相关资源
        最近更新 更多