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.
输出
性能改进
以下两项调整可提高性能
我们不需要在这两个方面进行分支,因为它们将提供相同的解决方案步骤计数 改进的代码
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倍,因此将其添加到我的答案中。很好--到目前为止是一个更快的解决方案。