繁体   English   中英

Python递归算法不适用于大值 - C程序有效

[英]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位进行一些测试。

它打破了同样的方式:

  • 在IDLE下,我得到一个RESTART行,说Python解释器必须重启,在4000次迭代之前
  • 当直接在cmd.exe窗口中运行时,我得到一个系统弹出窗口,说像Python解释器停止了 (在法语Python.exe一个cessédefonctionner中 ),在4000次迭代后很少

我在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM