使用简单分数

时间:2018-06-16 03:45:49

标签: php out-of-memory fractions

我正在使用this library来处理PHP中的分数。这工作正常,但有时,我必须循环很多值,这会导致以下错误:

  

允许的内存大小为134217728字节耗尽

我可以使用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行。任何帮助都将受到高度赞赏。

更新:

  1. 即使我只使用这个代码块而没有别的东西,脚本也耗尽了内存。我当然知道创建一个新的Fraction对象是耗尽内存的原因。
  2. 我认为不需要unset()任何东西,因为相同的一个变量会一遍又一遍地存储新的小数值。
  3. 这让我想到,每当我创建一个新的Fraction对象时,会发生一些其他事情发生在库代码中占用的内存在重写$c_diff变量中的值时未释放。 / LI>
  4. 我对此并不擅长,所以我认为它与几个地方使用的static关键字有关。有谁可以帮我确认一下?
  5. 如果使用unset()确实可以解决此问题,我应该将其放在循环的末尾吗?

3 个答案:

答案 0 :(得分:3)

各种可能的修复和效率:

您有 6 for个循环,每个循环在不同范围内循环一个整数值。

但您的计算仅使用 3 值,因此$p = 10; $s = 14;$p = 13; $s = 11;无关紧要。这些在计算中完全相同。

你需要的只是总和;所以一旦你发现值24有效;你可以找到符合该值的所有部分(超过10的最小值):即(24 (sum) - 10 (min) = 14),然后收集范围内的值;所以有10,1411,1312,1213,1114,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%。

停止实例化新课程。创建一个类很昂贵。 Instad您创建一个类,只需将值插入:

示例:

$c_diff = new Fraction();

for(...){
    for(...){
        $c_diff->checkValuesOrWhateverMethod($num, $den)
    }
}

这将为您节省大量开销(取决于类的结构)

您在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并根据需要更新PHP.ini,但我个人建议您使用更专业的数学语言,例如MatlabHaskell

全部放在一起:

  • 您想要检查三个值,然后找到每个值的等效部分。
  • 您想要找到最小公分母分数(我认为)。

所以:

/***   
 * 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);
               }
             }
         }
      }
  • 这应该只占原始循环的一小部分。

答案 1 :(得分:1)

我想这不是你正在使用的整个代码块。

此循环创建50 * 50 * 10 * 10 * 10 * 10 = 25.000.000 Fraction对象。考虑使用PHP的unset()来清理内存,因为你正在分配内存来创建对象,但你永远不会释放它。

编辑以澄清

当你在PHP中创建任何东西时,无论是变量,数组,对象等.PHP分配内存来存储它,通常,当脚本执行结束时释放分配的内存。

unset()是告诉PHP的方式,“嘿,我不再需要这个了。你可以,请你释放它需要的内存吗?”当垃圾收集器运行时,PHP会考虑这一点并释放内存。

最好是防止内存耗尽,而不是为脚本提供更多内存。

答案 2 :(得分:0)

  

允许的内存大小为134217728字节耗尽

134217728字节= 134.218兆字节

你能试试吗?

ini_set('memory_limit', '140M')
/* loop code below */