使两个数相等的最小操作数

时间:2019-06-17 15:34:27

标签: algorithm recursion

我接受了采访,无法想到针对此问题的清晰/最佳解决方案。

给出2个数字A和B,我们需要通过最少的以下操作次数将数字A转换为B:

  • 减去1
  • 加1
  • 乘以2
  • 除法2
  • 乘以3
  • 除法3

例如:如果a = 3和b = 7,则程序应输出2。

第一次操作:* 2-> 3 * 2 = 6。

第二次操作:+1-> 6 + 1 = 7。

例如:如果a = 10且b = 60,则程序应输出2。

第一次操作:* 2-> 10 * 2 = 20。

第二次操作:* 3-> 20 * 3 = 60

由于我们可以在2次操作后将m(10)更改为n(60),答案是2。

试图使用动态编程和递归方法,但无济于事。有提示吗?

3 个答案:

答案 0 :(得分:3)

如其他答案中所述,可以使用BFS在图中其节点与数字相对应,其边沿与运算相对应的图中来实现。

有趣的是,有时,最佳路径需要包含相当大的数字(大于3 * max(A,B))。 以下是其中包含如此大量数字的最佳路径的示例:

a = 82, b = 73

optimal path: 
   [82, 164, 328, 656, 657, 219, 73] (6 operations) 

optimal path if paths with values larger than 3 * max(a, b) are discarded: 
   [82, 81, 162, 54, 108, 216, 72, 73] (7 operations)

以下是此BFS解决方案的python实现:

def solve(a, b, max_n=None):
    # the bfs queue
    queue = []

    # length[i] = length of the shortest
    # path to get from `a' to `i'
    length = {}

    # previous[i] = previous value reached
    # in the shortest path from `a' to `i'
    previous = {}

    # node with value `a' is the first in the path
    queue.append(a)
    length[a] = 0
    previous[a] = None

    while True:
        val = queue.pop(0)

        # add an element to the queue (if it was not
        # already visited, and eventually not above
        # some limit)
        def try_add(next_val):
            if max_n is not None and next_val > max_n:
                return
            if next_val in length:
                return
            queue.append(next_val)
            length[next_val] = length[val] + 1
            previous[next_val] = val

        try_add(val + 1)
        try_add(val - 1)

        try_add(val * 2)
        if val % 2 == 0:
            try_add(val // 2)

        try_add(val * 3)
        if val % 3 == 0:
            try_add(val // 3)

        # check whether we already have a solution
        if b in length:
            break

    path = [b]
    while True:
        if path[-1] == a:
            break
        else:
            path.append(previous[path[-1]])

    path.reverse()
    return path


if __name__ == '__main__':
    a = 82
    b = 73
    path = solve(a, b)
    print(len(path), ': ',  path)

    path = solve(a, b, 3 * max(a, b))
    print(len(path), ': ',  path)

答案 1 :(得分:1)

处理数字作为图形的节点,操作作为边。使用BFS查找从A到B的最短路径。

我认为您可以将节点的上限设置为A和B的绝对值的3倍,以最大程度地减少步数,但这不是必需的。

空间和时间复杂度与答案成正比,例如如果答案是2,则在最坏的情况下,我们必须访问6 * 2 = 12个节点。

答案 2 :(得分:1)

这是BFS Javascript解决方案:

const findPath = (ops) => (A, B) => {
  const queue = new Set() .add ( [A, []] )
  const paths = new Map()
  while (queue .size !== 0 && !paths .has (B)) {
    const next = [...queue] [0]
    const [n, p] = next
    ops.forEach((fn) => {
      const m = fn(n);
      if (Number.isInteger(m)) {
        if (!paths.has(m)) {
          queue.add([m, [...p, n]])
          paths.set(m, [...p, n])
        }
        queue.delete(next)
      }
    })
  }
  return paths.get(B)
}

const ops = [n => n + 1, n => n - 1, n => 2 * n, n => 3 * n, n => n / 2, n => n / 3]

console .log (
  findPath (ops) (82, 73)
)

我们仍然要处理数字队列,并记录每个找到的数字的路径的字典,并继续对其进行测试,直到队列为空(这些操作不会发生,但是其他操作可能会让我们耗尽它)或我们找到了目标。对于每个数字,我们运行每个操作,对于整数结果,如果尚未找到,请将其添加到我们的结构中。

这里没有什么可以阻止链条失去控制的。目前尚不清楚我们将如何做。而且显然可以通过不同的操作来实现:如果我们有add 2subtract 2double,我们将永远无法从2过渡到3。这种算法永远不会停止。

虽然可以将其当然地转换为递归算法,但朴素的递归不太可能成功,因为它将首先进行深度优先并且通常会丢失该值并且永不停止。