PHP 简单分数的内存耗尽错误
PHP Memory Exhausted Error with simple fractions
我正在使用 this library 处理 PHP 中的分数。这工作正常,但有时,我必须循环很多值,这会导致以下错误:
Allowed memory size of 134217728 bytes exhausted
我可以使用 PHP ini 分配更多内存,但这是一个滑坡。在某些时候,当循环足够大时,我将 运行 内存不足。
这是我当前的代码:
for($q = 10; $q <= 20; $q++) {
for($r= 10; $r <= 20; $r++) {
for($p = 10; $p <= 20; $p++) {
for($s = 10; $s <= 20; $s++) {
for($x = 50; $x <= 100; $x++) {
for($y = 50; $y <= 100; $y++) {
$den = ($q + $r + 1000) - ($p + $s);
$num = $x + $y;
$c_diff = new Fraction($num, $den);
}
}
}
}
}
}
我使用 memory_get_peak_usage(true)/(1024*1024)
来跟踪脚本使用的内存。在我添加创建新分数的行之前,使用的总内存仅为 2MB。
任何人都可以指导我如何摆脱这个错误。我浏览了 GitHub here 上发布的库代码,但无法弄清楚如何摆脱内存耗尽错误。这是因为 static
关键字吗?我是初学者,所以我不完全确定发生了什么。
删除空行和注释后,库代码大约有 100 行。任何帮助将不胜感激。
更新:
- 即使我只使用这段代码,脚本也会耗尽它的内存。我肯定知道创建一个新的 Fraction 对象是耗尽内存的原因。
- 我认为不需要
unset()
任何东西,因为同一个变量一遍又一遍地存储新的小数值。
- 这让我想到,每当我创建一个新的
Fraction
对象时,库代码中会发生其他事情,它会占用内存,而在重写 $c_diff
变量中的值时不会释放该内存.
- 我不太擅长这个,所以我认为这与在几个地方使用的
static
关键字有关。谁能帮我确认一下?
如果这个问题确实可以用unset()
解决,我应该把它放在循环的末尾吗?
Allowed memory size of 134217728 bytes exhausted
134217728 字节 = 134.218 兆字节
你能试试这个吗?
ini_set('memory_limit', '140M')
/* loop code below */
我想这不是您正在使用的整个代码块。
这个循环创建了 50*50*10*10*10*10 = 25.000.000 个分数对象。考虑使用 PHP 的 unset()
来清理内存,因为您正在分配内存来创建对象,但您永远不会释放它。
编辑澄清
当您在 PHP 中创建任何内容时,无论是变量、数组、对象等。PHP 都会分配内存来存储它,通常,分配的内存会在脚本执行结束时释放。
unset()
就是告诉PHP、"hey, I don't need this anymore. Can you, pretty please, free up the memory it takes?"的方法。 PHP 考虑到这一点并在其垃圾收集器运行时释放内存。
最好防止内存耗尽,而不是为脚本提供更多内存。
各种可能的修复和效率:
您有 6 for
个循环,每个循环在不同范围内循环一个整数值。
但是您的计算仅使用 3 个值,因此 $p = 10; $s = 14;
或 $p = 13; $s = 11;
在计算中是否完全等价并不重要。
你只需要总和;因此,一旦您发现值 24
有效;你可以找到符合该值的所有部分(超过最小值10):即(24 (sum) - 10 (min) = 14)
,然后收集范围内的值;所以有 10,14
、11,13
、12,12
、13,11
、14,10
有效值。在内部 for
循环上为自己节省 80% 以上的处理工作。
$pairs = "p,s<BR>"; //the set of paired values found
$other = $sum - $min;
if($other > $max){
$other = $sum - $max;
}
$hardMin = $min;
while ($other >= $hardMin && $min >= $hardMin && $min <= $max){
$pairs .= $min.", ".$other."<BR>";
$other--; // -1
$min++; // +1
}
print $pairs;
给予:
p,s
10,14
11,13
12,12
13,11
14,10
因此对于这个 for
循环,您可能只需要完成循环内部循环总工作量的 ~10%。
停止实例化新的 classes。创建 class 很昂贵。 Instad 你创建一个 class 并简单地插入值:
示例:
$c_diff = new Fraction();
for(...){
for(...){
$c_diff->checkValuesOrWhateverMethod($num, $den)
}
}
这将为您节省大量开销(取决于 class 的结构)
您在 GitHub 上链接的代码只是将值转换为分数,似乎效率很低。
你只需要这个:
function float2frac($n, $tolerance = 1.e-6) {
$h1=1; $h2=0;
$k1=0; $k2=1;
$b = 1/$n;
do {
$b = 1/$b;
$a = floor($b);
$aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
$aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
$b = $b-$a;
} while (abs($n-$h1/$k1) > $n*$tolerance);
return $h1."/".$k1;
}
摘自 this excellent answer.
示例:
for(...){
for(...){
$den = ($q + $r + 1000) - ($p + $s);
$num = $x + $y;
$value = $num/den;
$c_diff = float2frac($value);
unset($value,den,$num);
}
}
如果您需要更高的精度,您可以 read this question and update PHP.ini as appropriate, but personally I would recommend you use more specialist maths languages such as Matlab or Haskell。
综合起来:
- 您想检查三个值,然后找到每个值的等价部分。
- 您想简单地找到最小公分母分数(我认为)。
所以:
/***
* to generate a fraction with Lowest Common Denominator
***/
function float2frac($n, $tolerance = 1.e-6) {
$h1=1; $h2=0;
$k1=0; $k2=1;
$b = 1/$n;
do {
$b = 1/$b;
$a = floor($b);
$aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
$aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
$b = $b-$a;
} while (abs($n-$h1/$k1) > $n*$tolerance);
return $h1."/".$k1;
}
/***
* To find equivilants
***/
function find_equivs($sum = 1, $min = 1, $max = 2){
$value_A = $sum - $min;
$value_B = $min;
if($value_A > $max){
$value_B = $sum - $max;
$value_A = $max;
}
$output = "";
while ($value_A >= $min && $value_B <= $max){
if($value_A + $value_B == $sum){
$output .= $value_A . ", " . $value_B . "<BR>";
}
$value_A--; // -1
$value_B++; // +1
}
return $output;
}
/***
* Script...
***/
$c_diff = []; // an array of results.
for($qr = 20; $qr <= 40; $qr++) {
for($ps = 20; $ps <= 40; $ps++) {
for($xy = 100; $x <= 200; $xy++) {
$den = ($qr + 1000) - $ps;
$num = $xy;
$value = $num/$den; // decimalised
$c_diff[] = float2frac($num, $den);
/***
What is your criteria for success?
***/
if(success){
$qr_text = "Q,R<BR>";
$qr_text .= find_equivs($qr,10,20);
$sp_text = "S,P<BR>";
$sp_text .= find_equivs($sp,10,20);
$xy_text = "X,Y<BR>";
$xy_text .= find_equivs($sp,50,100);
}
}
}
}
- 这应该只执行原始循环的一小部分。
我正在使用 this library 处理 PHP 中的分数。这工作正常,但有时,我必须循环很多值,这会导致以下错误:
Allowed memory size of 134217728 bytes exhausted
我可以使用 PHP ini 分配更多内存,但这是一个滑坡。在某些时候,当循环足够大时,我将 运行 内存不足。
这是我当前的代码:
for($q = 10; $q <= 20; $q++) {
for($r= 10; $r <= 20; $r++) {
for($p = 10; $p <= 20; $p++) {
for($s = 10; $s <= 20; $s++) {
for($x = 50; $x <= 100; $x++) {
for($y = 50; $y <= 100; $y++) {
$den = ($q + $r + 1000) - ($p + $s);
$num = $x + $y;
$c_diff = new Fraction($num, $den);
}
}
}
}
}
}
我使用 memory_get_peak_usage(true)/(1024*1024)
来跟踪脚本使用的内存。在我添加创建新分数的行之前,使用的总内存仅为 2MB。
任何人都可以指导我如何摆脱这个错误。我浏览了 GitHub here 上发布的库代码,但无法弄清楚如何摆脱内存耗尽错误。这是因为 static
关键字吗?我是初学者,所以我不完全确定发生了什么。
删除空行和注释后,库代码大约有 100 行。任何帮助将不胜感激。
更新:
- 即使我只使用这段代码,脚本也会耗尽它的内存。我肯定知道创建一个新的 Fraction 对象是耗尽内存的原因。
- 我认为不需要
unset()
任何东西,因为同一个变量一遍又一遍地存储新的小数值。 - 这让我想到,每当我创建一个新的
Fraction
对象时,库代码中会发生其他事情,它会占用内存,而在重写$c_diff
变量中的值时不会释放该内存. - 我不太擅长这个,所以我认为这与在几个地方使用的
static
关键字有关。谁能帮我确认一下?
如果这个问题确实可以用unset()
解决,我应该把它放在循环的末尾吗?
Allowed memory size of 134217728 bytes exhausted
134217728 字节 = 134.218 兆字节
你能试试这个吗?
ini_set('memory_limit', '140M')
/* loop code below */
我想这不是您正在使用的整个代码块。
这个循环创建了 50*50*10*10*10*10 = 25.000.000 个分数对象。考虑使用 PHP 的 unset()
来清理内存,因为您正在分配内存来创建对象,但您永远不会释放它。
编辑澄清
当您在 PHP 中创建任何内容时,无论是变量、数组、对象等。PHP 都会分配内存来存储它,通常,分配的内存会在脚本执行结束时释放。
unset()
就是告诉PHP、"hey, I don't need this anymore. Can you, pretty please, free up the memory it takes?"的方法。 PHP 考虑到这一点并在其垃圾收集器运行时释放内存。
最好防止内存耗尽,而不是为脚本提供更多内存。
各种可能的修复和效率:
您有 6 for
个循环,每个循环在不同范围内循环一个整数值。
但是您的计算仅使用 3 个值,因此 $p = 10; $s = 14;
或 $p = 13; $s = 11;
在计算中是否完全等价并不重要。
你只需要总和;因此,一旦您发现值 24
有效;你可以找到符合该值的所有部分(超过最小值10):即(24 (sum) - 10 (min) = 14)
,然后收集范围内的值;所以有 10,14
、11,13
、12,12
、13,11
、14,10
有效值。在内部 for
循环上为自己节省 80% 以上的处理工作。
$pairs = "p,s<BR>"; //the set of paired values found
$other = $sum - $min;
if($other > $max){
$other = $sum - $max;
}
$hardMin = $min;
while ($other >= $hardMin && $min >= $hardMin && $min <= $max){
$pairs .= $min.", ".$other."<BR>";
$other--; // -1
$min++; // +1
}
print $pairs;
给予:
p,s
10,14
11,13
12,12
13,11
14,10
因此对于这个 for
循环,您可能只需要完成循环内部循环总工作量的 ~10%。
停止实例化新的 classes。创建 class 很昂贵。 Instad 你创建一个 class 并简单地插入值:
示例:
$c_diff = new Fraction();
for(...){
for(...){
$c_diff->checkValuesOrWhateverMethod($num, $den)
}
}
这将为您节省大量开销(取决于 class 的结构)
您在 GitHub 上链接的代码只是将值转换为分数,似乎效率很低。
你只需要这个:
function float2frac($n, $tolerance = 1.e-6) {
$h1=1; $h2=0;
$k1=0; $k2=1;
$b = 1/$n;
do {
$b = 1/$b;
$a = floor($b);
$aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
$aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
$b = $b-$a;
} while (abs($n-$h1/$k1) > $n*$tolerance);
return $h1."/".$k1;
}
摘自 this excellent answer.
示例:
for(...){
for(...){
$den = ($q + $r + 1000) - ($p + $s);
$num = $x + $y;
$value = $num/den;
$c_diff = float2frac($value);
unset($value,den,$num);
}
}
如果您需要更高的精度,您可以 read this question and update PHP.ini as appropriate, but personally I would recommend you use more specialist maths languages such as Matlab or Haskell。
综合起来:
- 您想检查三个值,然后找到每个值的等价部分。
- 您想简单地找到最小公分母分数(我认为)。
所以:
/***
* to generate a fraction with Lowest Common Denominator
***/
function float2frac($n, $tolerance = 1.e-6) {
$h1=1; $h2=0;
$k1=0; $k2=1;
$b = 1/$n;
do {
$b = 1/$b;
$a = floor($b);
$aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux;
$aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux;
$b = $b-$a;
} while (abs($n-$h1/$k1) > $n*$tolerance);
return $h1."/".$k1;
}
/***
* To find equivilants
***/
function find_equivs($sum = 1, $min = 1, $max = 2){
$value_A = $sum - $min;
$value_B = $min;
if($value_A > $max){
$value_B = $sum - $max;
$value_A = $max;
}
$output = "";
while ($value_A >= $min && $value_B <= $max){
if($value_A + $value_B == $sum){
$output .= $value_A . ", " . $value_B . "<BR>";
}
$value_A--; // -1
$value_B++; // +1
}
return $output;
}
/***
* Script...
***/
$c_diff = []; // an array of results.
for($qr = 20; $qr <= 40; $qr++) {
for($ps = 20; $ps <= 40; $ps++) {
for($xy = 100; $x <= 200; $xy++) {
$den = ($qr + 1000) - $ps;
$num = $xy;
$value = $num/$den; // decimalised
$c_diff[] = float2frac($num, $den);
/***
What is your criteria for success?
***/
if(success){
$qr_text = "Q,R<BR>";
$qr_text .= find_equivs($qr,10,20);
$sp_text = "S,P<BR>";
$sp_text .= find_equivs($sp,10,20);
$xy_text = "X,Y<BR>";
$xy_text .= find_equivs($sp,50,100);
}
}
}
}
- 这应该只执行原始循环的一小部分。