Python 达到k的最小移动量

Python 达到k的最小移动量,python,algorithm,Python,Algorithm,给定两个数字m和n,一步就可以得到两个新的对: m+n,n m,n+m 让我们先设置m=n=1找到最小移动次数,这样至少有一个数字等于k 保证有一个解决方案(即存在一系列导致k的移动) 例如: 给定k=5 使m或n等于k的最小移动次数为3 1, 1 1, 2 3, 2 3, 5 总共3个动作 我已经在python中提出了一个使用递归的解决方案,但它似乎不适用于大数字(即10^6) 如何提高性能使其适用于大数字?在Python中,递归函数的递归限制默认设置为10^4。您可以使用sys模块进行

给定两个数字
m
n
,一步就可以得到两个新的对:

  • m+n,n
  • m,n+m
让我们先设置
m=n=1
找到最小移动次数,这样至少有一个数字等于
k

保证有一个解决方案(即存在一系列导致k的移动)

例如: 给定
k=5
使
m
或n等于
k
的最小移动次数为3

1, 1
1, 2
3, 2
3, 5
总共3个动作

我已经在python中提出了一个使用递归的解决方案,但它似乎不适用于大数字(即10^6)


如何提高性能使其适用于大数字?

在Python中,递归函数的递归限制默认设置为10^4。您可以使用
sys
模块进行更改:

import sys 
sys.setrecursionlimit(10**6) 

这是数论中一个有趣的问题,包括线性丢番图方程。由于在线上有可用的解决方案,我推测您需要帮助自己推导算法

重申这个问题:你从两个数字开始,分别是1*m+0*n,0*m+1*n。使用速记(1,0)和(0,1)。您正在寻找线性丢番图方程任何解的最短路径

a*m + b*n = k
式中,(a,b)由起始值(1,1)a.k.a.((1,0),(0,1))得出

所以。。。从(1,1)开始,您如何描述从二进制增强的各种排列中获得的路径。在每个步骤中,您有两个选择:a+=b或b+=a。您现有的算法已识别此二叉搜索树


这些图形变换(沿晶格的边)可以被描述,在给定的步骤中,可以根据(a,b)对进行描述。这足以让你前进吗?这种特征化是将这个问题转化为接近直接计算的关键。

基于优先级队列的非递归算法(使用堆)

每一步都有两种可能的动作

  • 将第一个数字替换为总和
  • 将第二个数字替换为总和
  • 因此,每个状态生成两个新状态。各国的优先次序如下:

  • 移动:移动次数较少的状态具有较高的优先级
  • 总和:总和越高的州优先级越高
  • 我们使用优先级队列(本例中为堆)按优先级处理状态

    代码

    def calc1(k):
      if k < 1:
        return None, None  # No solution
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves, [(m, n)]
    
      h = []  # Priority queue (heap)
    
      path = [(m, n)]
      sum_ = m + n
      # Python's heapq acts as a min queue.
      # We can order thing by max by using -value rather than value
      # Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
      heappush(h, (moves+1, -sum_, sum_, n, path))
      heappush(h, (moves+1, -sum_, m, sum_, path))
    
      while h:
        # Get state with lowest sum
        moves, sum_, m, n, path = heappop(h)
    
        sum_ = - sum_
    
        if sum_ == k:
          return moves, path  # Found solution
    
        if sum_ < k:
          sum_ = m + n  # new sum
          # Replace first number with sum
          heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
          # Replace second number with sum
          heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
    
        # else:
        #  so just continues since sum_ > k
    
      # Exhausted all options, so no solution
      return None, None
    
    def calc(k):
      if k < 1:
        return None, None
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves
    
      h = []    # Priority queue (heap)
    
      sum_ = m + n
      heappush(h, (moves+1, -sum_, sum_, n))
    
      while h:
        moves, sum_, m, n = heappop(h)
        sum_ = - sum_
    
        if sum_ == k:
          return moves
    
        if sum_ < k:
          sum_ = m + n
          steps = [(sum_, n), (m, sum_)]
          heappush(h, (moves+1, -sum_, *steps[0]))
          if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
            heappush(h, (moves+1, -sum_, *steps[1]))
    
    Tested up to k = 100, 000 which took ~2 minutes.
    
    输出

    性能改进

    以下两项调整可提高性能

  • 不包括路径,仅包括步数(为k=10000提供3倍的加速比
  • 不使用对称对(额外提供2x,k=10000
  • 对称对指的是向前和向后相同的m,n对,如(1,2)和(2,1)。
    我们不需要在这两个方面进行分支,因为它们将提供相同的解决方案步骤计数

    改进的代码

    def calc1(k):
      if k < 1:
        return None, None  # No solution
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves, [(m, n)]
    
      h = []  # Priority queue (heap)
    
      path = [(m, n)]
      sum_ = m + n
      # Python's heapq acts as a min queue.
      # We can order thing by max by using -value rather than value
      # Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
      heappush(h, (moves+1, -sum_, sum_, n, path))
      heappush(h, (moves+1, -sum_, m, sum_, path))
    
      while h:
        # Get state with lowest sum
        moves, sum_, m, n, path = heappop(h)
    
        sum_ = - sum_
    
        if sum_ == k:
          return moves, path  # Found solution
    
        if sum_ < k:
          sum_ = m + n  # new sum
          # Replace first number with sum
          heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
          # Replace second number with sum
          heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
    
        # else:
        #  so just continues since sum_ > k
    
      # Exhausted all options, so no solution
      return None, None
    
    def calc(k):
      if k < 1:
        return None, None
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves
    
      h = []    # Priority queue (heap)
    
      sum_ = m + n
      heappush(h, (moves+1, -sum_, sum_, n))
    
      while h:
        moves, sum_, m, n = heappop(h)
        sum_ = - sum_
    
        if sum_ == k:
          return moves
    
        if sum_ < k:
          sum_ = m + n
          steps = [(sum_, n), (m, sum_)]
          heappush(h, (moves+1, -sum_, *steps[0]))
          if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
            heappush(h, (moves+1, -sum_, *steps[1]))
    
    Tested up to k = 100, 000 which took ~2 minutes.
    
    更新

    def calc1(k):
      if k < 1:
        return None, None  # No solution
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves, [(m, n)]
    
      h = []  # Priority queue (heap)
    
      path = [(m, n)]
      sum_ = m + n
      # Python's heapq acts as a min queue.
      # We can order thing by max by using -value rather than value
      # Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
      heappush(h, (moves+1, -sum_, sum_, n, path))
      heappush(h, (moves+1, -sum_, m, sum_, path))
    
      while h:
        # Get state with lowest sum
        moves, sum_, m, n, path = heappop(h)
    
        sum_ = - sum_
    
        if sum_ == k:
          return moves, path  # Found solution
    
        if sum_ < k:
          sum_ = m + n  # new sum
          # Replace first number with sum
          heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
          # Replace second number with sum
          heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
    
        # else:
        #  so just continues since sum_ > k
    
      # Exhausted all options, so no solution
      return None, None
    
    def calc(k):
      if k < 1:
        return None, None
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves
    
      h = []    # Priority queue (heap)
    
      sum_ = m + n
      heappush(h, (moves+1, -sum_, sum_, n))
    
      while h:
        moves, sum_, m, n = heappop(h)
        sum_ = - sum_
    
        if sum_ == k:
          return moves
    
        if sum_ < k:
          sum_ = m + n
          steps = [(sum_, n), (m, sum_)]
          heappush(h, (moves+1, -sum_, *steps[0]))
          if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
            heappush(h, (moves+1, -sum_, *steps[1]))
    
    Tested up to k = 100, 000 which took ~2 minutes.
    
    通过@•㪞דבקן将解决方案从JavaScript转换为Python进行测试

    def g(m, n, memo):
      key = (m, n)
    
      if key in memo:
        return memo[key]
    
      if m == 1 or n == 1:
        memo[key] = max(m, n) - 1
    
      elif m == 0 or n == 0:
        memo[key] = float("inf")
    
      elif m > n:
        memo[key]  = (m // n) + g(m % n, n, memo)
    
      else:
        memo[key]  = (n // m) + g(m, n % m, memo)
    
      return memo[key] 
    
    def f(k, memo={}):
      if k == 1:
        return 0
    
      return min(g(k, n, memo) for n in range((k // 2) + 1))
    
    代码的性能

    def calc1(k):
      if k < 1:
        return None, None  # No solution
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves, [(m, n)]
    
      h = []  # Priority queue (heap)
    
      path = [(m, n)]
      sum_ = m + n
      # Python's heapq acts as a min queue.
      # We can order thing by max by using -value rather than value
      # Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
      heappush(h, (moves+1, -sum_, sum_, n, path))
      heappush(h, (moves+1, -sum_, m, sum_, path))
    
      while h:
        # Get state with lowest sum
        moves, sum_, m, n, path = heappop(h)
    
        sum_ = - sum_
    
        if sum_ == k:
          return moves, path  # Found solution
    
        if sum_ < k:
          sum_ = m + n  # new sum
          # Replace first number with sum
          heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
          # Replace second number with sum
          heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
    
        # else:
        #  so just continues since sum_ > k
    
      # Exhausted all options, so no solution
      return None, None
    
    def calc(k):
      if k < 1:
        return None, None
    
      m, n, moves = 1, 1, 0
      if m == k or n == k:
        return moves
    
      h = []    # Priority queue (heap)
    
      sum_ = m + n
      heappush(h, (moves+1, -sum_, sum_, n))
    
      while h:
        moves, sum_, m, n = heappop(h)
        sum_ = - sum_
    
        if sum_ == k:
          return moves
    
        if sum_ < k:
          sum_ = m + n
          steps = [(sum_, n), (m, sum_)]
          heappush(h, (moves+1, -sum_, *steps[0]))
          if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
            heappush(h, (moves+1, -sum_, *steps[1]))
    
    Tested up to k = 100, 000 which took ~2 minutes.
    

    即使使用蛮力,我们也可以比队列做得更好,在将
    m
    设置为
    k
    时,尝试每种可能的
    n
    。下面是JavaScript代码,非常接近Python语法:

    功能g(m、n、备忘录){
    常数键=m+','+n;
    如果(备注[键])
    返回备忘录[键];
    如果(m==1 | | n==1)
    返回数学最大值(m,n)-1;
    如果(m==0 | | n==0)
    返回无穷大;
    让我们回答;
    如果(m>n)
    答案=数学楼层(m/n)+g(m%n,n,备忘录);
    其他的
    答案=数学楼层(n/m)+g(m,n%m,备忘录);
    备忘录[键]=答案;
    返回答案;
    }
    函数f(k,memo={}){
    如果(k==1)
    返回0;
    让最好=无限;
    
    对于(设n=1;n大数字会发生什么情况?这需要很长时间,当我尝试10^6时,我会得到“分段错误”。在深度递归循环期间,如果基本条件长时间不满足(无法准确指出多长时间),堆栈大小可能会影响可用资源并返回错误或崩溃。在这种情况下,您也可以使用迭代而不是递归。这可能会有所帮助。添加的答案似乎比接受的答案快数千倍。我添加了,但对于大数仍然无效。请尝试使用k=10^6运行代码。我认为我的答案是回答可能会让你感兴趣。它似乎比队列快数千倍:@1490㪞דברקן--将代码转换为Python,速度快了约120倍,因此将其添加到我的答案中。很好--到目前为止是一个更快的解决方案。