[英]Python recursive algorithm doesn't work for large values - C program works
我想为这个问题编写一个回溯解决方案,要求找到总结给定n
的最明显的奇数。
我把这个Python代码整合在一起:
import sys
sys.setrecursionlimit(10000)
stop = False
def solve(n, used, current_sum):
global stop
if stop:
return
if current_sum == n:
print(used)
stop = True
return
start = 1 if len(used) == 0 else (used[-1] + 2)
for i in range(start, n + 1, 2):
if current_sum + i <= n and not stop:
used.append(i)
solve(n, used, current_sum + i)
used.pop()
else:
return
solve(100000000, [], 0)
遗憾的是,它不会为我打印任何内容。 据我所知, if
有条件,它永远不会达到。 如果我在每一步打印current_sum
,当整个程序退出没有错误时,它似乎停在16000000
左右。
我试过增加递归限制,没有运气。
我在Windows 8.1下在Idle和Eclipse上测试了它,在Python 3.4中,64位。 我有16 GB的RAM。
如果我减少n
,那么我得到一个解决方案(例如删除一个零)。
这对我来说没有意义,所以我想看看我是否有更好的运气C.我在C中攻击了这个:
int sol[100000];
bool done = false;
void solve(int n, int k, int sum)
{
if (done)
return;
if (sum == n)
{
done = true;
for (int i = 0; i < k; ++i)
{
printf("%d ", sol[i]);
}
printf("\n");
return;
}
int start = 1;
if (k > 0)
start = sol[k - 1] + 2;
for (int i = start; i <= n; i += 2)
if (sum + i <= n && !done)
{
sol[k] = i;
solve(n, k + 1, sum + i);
}
else
return;
}
int main()
{
solve(100000000, 0, 0);
return 0;
}
即使我添加另一个零,哪个效果很好!
与Python有什么关系?我如何才能使这个值适用于大值?
较低值的执行时间与C代码相当,它只是让我退出更高的值。
与Python有什么关系?我如何才能使这个值适用于大值?
我重写了你的代码以使它工作。 增加n
参数时,需要调整递归深度。 我使用Python 2.7.6。 我们的想法是和你编写的C代码一样,传递的第二个参数是整数,而不是列表。
import sys
sys.setrecursionlimit(100000)
sol = []
stop = False
def solve(n, k, current_sum):
global stop
if stop:
return
if current_sum == n:
stop = True
for i in xrange(0, k, 1):
print(sol[i]),
print
return
start = 1 if len(sol) == 0 else (sol[k-1] + 2)
for i in xrange(start, n + 1, 2):
if current_sum + i <= n and not stop:
sol.append(0)
sol[k] = i
solve(n, k + 1, current_sum + i)
else:
return
solve(100000000, 0, 0)
我试着读你编写的python代码的内存使用情况。 我必须设置n = 100.000
才能获得370 MB的结果。 添加0
使我的操作系统终止程序。 (在Mac OS XI上收到内存错误)。
这是我在Linux上使用的代码:
import os
import sys
sys.setrecursionlimit(100000)
_proc_status = '/proc/%d/status' % os.getpid()
_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
'KB': 1024.0, 'MB': 1024.0*1024.0}
def _VmB(VmKey):
'''Private.
'''
global _proc_status, _scale
# get pseudo file /proc/<pid>/status
try:
t = open(_proc_status)
v = t.read()
t.close()
except:
return 0.0 # non-Linux?
# get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
i = v.index(VmKey)
v = v[i:].split(None, 3) # whitespace
if len(v) < 3:
return 0.0 # invalid format?
# convert Vm value to bytes
return float(v[1]) * _scale[v[2]]
def memory(since=0.0):
'''Return memory usage in bytes.
'''
return _VmB('VmSize:') - since
stop = False
def solve(n, used, current_sum):
global stop
if stop:
return
if current_sum == n:
print(used)
stop = True
return
start = 1 if len(used) == 0 else (used[-1] + 2)
for i in range(start, n + 1, 2):
if current_sum + i <= n and not stop:
used.append(i)
solve(n, used, current_sum + i)
used.pop()
else:
return
m0 = memory()
solve(100000, [], 0)
m1 = memory(m0)
print(m1/(1024*1024))
与此结果相比,我编写的改进(更正)代码仅使用4 MB
,参数n
设置为100.000.000
。 这确实是一个巨大的差异。
我不确定为什么会这样。 特别是你有一个包含递归调用的循环(所以你可以从同一个分支中多次递归调用)。
如果你坚持使用递归调用,那么也许你想重新设计你的程序。 具有记忆功能的递归调用可能比情况下的循环更快。 例如,请参阅此链接 。
我可以使用您的代码在Windows 7上使用Python3.4 64位进行一些测试。
它打破了同样的方式:
我在FreeBSD 10.1 32位虚拟机下尝试了它,只有512 Mb的内存,8000次迭代后我的分段故障很少
我认为这是CPython解释器中的一个带有深度递归的错误。 因为我添加了一些痕迹,并且在我的所有测试中,它在到达总和之前将元素添加到列表的初始阶段中断了。
我会接受任何关于滥用递归的错误(确实是:-)),但是分段错误确实很糟糕:看起来Python本身不能控制绑定。
一旦我们注意到n个第一个奇数的总和是n 2 (数学上是平凡的),就很容易立即在最终解决方案附近开始。 这个例子就足够了,因为100000000 == 10000 2 。 但是在一般情况下,随着used
列表的最后数量变化,我们将current_sum
步长为2,因此我们仍然缺少每一个数字。 但是,如果我们前一步并从那里开始,我们再次将current_sum提高2,但是在其他数字上。
所以这里有一个原始代码的略有变化:
import sys
sys.setrecursionlimit(100000)
stop = False
def solve(n, used, current_sum):
global stop
if stop:
return
# TRACES
## print (len(used), used[-1] if len(used) > 0 else '', end=' ')
## if (len(used) % 10) == 0:
## print('')
if current_sum == n:
print(used)
global stop
stop = True
return
if current_sum > n: # simple optimisation, no need to go further
return
# the trick : sum of n first numbers is n*n, and we must start 2 steps before
if current_sum == 0:
import math
l = int(math.sqrt(n)) - 2
current_sum = l * l
used = list(range(1, l*2, 2))
solve(n, used, current_sum)
return
start = 1 if len(used) == 0 else (used[-1] + 2)
for i in range(start, n + 1, 2):
if current_sum + i <= n and not stop:
used.append(i)
solve(n, used, current_sum + i)
used.pop()
else:
return
solve(100000000, [], 0)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.