为什么计算 1/(n * log(n) - n) 会导致电脑崩溃?

8
最近我遇到了这个问题:Wolfram说总和发散,但Mathematica给出了一个数值。它说1/(n * log(n)-n)不可加和(或者在数学上不收敛)。有趣的是,尽管它“不可加和”,我们仍然可以尝试进行数值计算。Mathematica给出的答案约为6.1
好的。 我认为,让我们试着在PHP脚本中复制该数字(或类似的数字)的系列求和。 我的代码如下:
$formula = function ($n) {return 1/($n * log($n) - $n);};
$n=2;
$sum=0;

while(true) {
    $term_n = $formula($n);
    $sum += $term_n;
    if ($n++ % 100000 == 0) {
        if ($sum > 5.8)
            usleep(1000);
        echo "n=".number_format($n-1)."; sum={$sum}; error={$term_n}\n";
    }
}

我的算法计算到5.866就出现了两种情况之一:

  1. Ubuntu崩溃/冻结了
  2. 或者Linux杀死了我的计算脚本进程

这发生在大约3400万次迭代后。

后来,我检查了CPU负载如何随着计算更多级数而变化。

现在,有趣的部分: 在约2200万次迭代时,核心在彼此之间切换任务时出现了困难: enter image description here

后来,在约3300万次迭代时,核心达到了无法回头的地步-它们拒绝工作了: enter image description here

问题是- 为什么和为5.866会导致电脑崩溃? -考虑到迭代次数N不是很大(只有3400万),第N项也不是很小(只有1.7E-9)-所以没有奇点的原因。


3
那么我可能不应该测试这段代码? - user10226920
2
看起来像是一个无限循环。 - President James K. Polk
2
我认为问题是由PHP中的内存泄漏引起的。我在Python中复制了您的代码,并且它运行得非常顺利。 - r3mainer
1
我刚在 PHP 7.1.10 上运行了它(命令行,而不是在 Apache 中),我让它一直运行到 n=1,134,300,000; sum=6.0596079176871; error=4.4414755508302E-11。它没有崩溃,系统几乎没有变慢。我有4个 CPU。我运行的是 Mint 18.3 (4.15.0-38-generic #41~16.04.1-Ubuntu SMP Wed Oct 10 20:16:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux)。我可以看到很多 CPU 之间的切换,但总平均值没有超过大约60%的总量。至于为什么它在你的系统上这样做,我无法评论。 - Nic3500
1
根据您提供的源代码和描述,也许您遇到了特定PHP版本中的一个错误?为了开始诊断,我建议将$formula($n)替换为同样的内联表达式,并查看您的代码是否继续出错。 - Mike Robinson
显示剩余3条评论
2个回答

2

仅供参考,但这听起来像是计算机内存不足。您的脚本没有显式分配任何内存,但也许您的 PHP 版本存在漏洞并以某种方式泄露内存。

如果空闲内存耗尽,这确实会导致运行中的操作系统崩溃,或者提示它杀死您的脚本以保护自己。

如果系统开始大量使用交换空间进行补偿,那么这将短暂地使您的脚本停顿,从而导致您观察到的 CPU 图表波动。


谢谢,确实是内存泄漏(脚本已经占用了100%的RAM,后来当交换使用率达到100%时-一切都崩溃了)。但似乎memleak不在PHP核心中,因为同样的php版本“PHP 7.2.10-0ubuntu0.18.04.1(cli)”在其他机器上执行我的脚本时没有任何问题。所以我理解,在从PHP调用Ubuntu OS时可能存在内存泄漏或其他问题... - Agnius Vasiliauskas
可能是一个已加载的PHP扩展导致了问题。 - Boann

0
我已经将问题缩小到了根源-脚本中执行的函数调用越多,泄漏的内存就越多。而且调用哪个函数(自定义,sin()、log()或min()等)并不重要。因此我怀疑这是 PHP 核心中的一个 bug,在某些特定的条件/操作系统内核下会发生。
测试代码:
define('AMOUNT', 2000000);

if ($argc == 1)
    for ($i=AMOUNT; $i <= 3*AMOUNT; $i+=AMOUNT) 
        shell_exec('php ' . __FILE__ . " {$i}");
else 
    for ($i=0; $i < $argv[1]; $i++)
        sin(1);
    /*
    Change line above into something without a function call to stop memory leak :
    $x++; // for example
    */

生成此内存泄漏图:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接