Ошибка исчерпания памяти PHP с простыми дробями

я использую эта библиотека работать с дробями в 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) чтобы отслеживать память, которую использует скрипт. Общее количество используемой памяти составляло всего 2 МБ, пока я не добавил строку, которая создает новую дробь.

Может ли кто-нибудь, пожалуйста, подскажите мне, как избавиться от этой ошибки. Я просмотрел код библиотеки, размещенной на GitHub Вот но не могу понять, как избавиться от ошибки исчерпанной памяти. Это из-за static ключевое слово? Я новичок, поэтому я не совсем уверен, что происходит.

Код библиотеки составляет около 100 строк после удаления пустых строк и комментариев. Любая помощь будет высоко оценен.

ОБНОВИТЬ:

  1. Скрипт исчерпывает свою память, даже если я использую только этот блок кода и ничего больше. Я точно знаю, что создание нового объекта Fraction — причина истощения памяти.
  2. Я думал, что нет необходимости unset() что-нибудь, потому что одна и та же переменная для хранения нового дробного значения снова и снова.
  3. Это заставляет меня думать, что всякий раз, когда я создаю новый Fraction что-то еще происходит, что в коде библиотеки, который занимает память, которая не освобождается при перезаписи значения в $c_diff переменная.
  4. Я не очень хорош в этом, поэтому я думал, что это как-то связано с static Ключевое слово используется в нескольких местах. Может ли кто-нибудь подтвердить это для меня?

Если эта проблема действительно может быть решена с помощью unset()Должен ли я поместить его в конце цикла?

1

Решение

У тебя есть 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 действительные значения. сэкономить 80% + переработка внутренних работ for петли.

 $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;

Предоставление:

  р, з

10,14
11,13
12,12
13,11
14,10

Так для этого for Если цикл уже выполнен, вам может потребоваться всего лишь ~ 10% от общей работы по циклу внутренних циклов.


Прекратите создавать новые классы. Создание класса стоит дорого. Инстад вы создаете один класс и просто вставляете значения в:

Пример:

$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;
}

Взято из этот отличный ответ.

Пример:

for(...){
for(...){
$den = ($q + $r + 1000) - ($p + $s);
$num = $x + $y;
$value = $num/den;

$c_diff = float2frac($value);
unset($value,den,$num);
}
}

Если вам нужно больше точности, вы можете прочитайте этот вопрос и обновите PHP.ini по мере необходимости, но лично я бы порекомендовал вам использовать более специализированные математические языки, такие как Matlab или же 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);
}
}
}
}
  • Это должно сделать только небольшой процент от первоначального зацикливания.
3

Другие решения

Я думаю, это не весь блок кода, который вы используете.

Этот цикл создает 50 * 50 * 10 * 10 * 10 * 10 = 25.000.000 объектов фракции. Рассмотрите возможность использования PHP unset() очистить память, так как вы выделяете память для создания объектов, но никогда не освобождаете ее.

редактирование для уточнения

Когда вы создаете что-либо в PHP, будь то переменная, массив, объект и т. Д. PHP выделяет память для ее хранения и обычно выделенная память освобождается по окончании выполнения скрипта.

unset() это способ сказать PHP: «Эй, мне это больше не нужно. Можете ли вы, пожалуйста, освободить память, которая для этого требуется?». PHP учитывает это и освобождает память при запуске сборщика мусора.

Лучше предотвратить исчерпание памяти, чем наполнять ваш скрипт большим объемом памяти.

1

Допустимый объем памяти 134217728 байт исчерпан

134217728 байт = 134,218 мегабайт

Вы можете попробовать это?

ini_set('memory_limit', '140M')
/* loop code below */
0
По вопросам рекламы [email protected]