Оптимизировать функцию

144
30 января 2020, 08:40

Выполняю код

<?php
$arr = file('arr.txt');
$sum = 0;
foreach ($arr as $key => $value) {
            $sum = array_sum(array_slice($arr,$key,30));
            if($sum > 50) {
                echo $key . ' ' . $sum . "<br>";
            }
        }
?>

То есть, поясняю:

Суммирую по порядку диапазоны: 
Сумма значений с 0 по 30
Сумма значений с 1 по 31
Сумма значений с 2 по 32
Сумма значений с 3 по 33
Сумма значений с 4 по 34
..
И так до конца. 

Получаю ошибку:

Fatal error: Maximum execution time of 180 seconds exceeded in E:\OSPanel\domains\arr\index.php on line 6

Файл arr.txt - это 200к целых чисел (0-100 примерно)

можно оптимизировать, ускорить функцию? Желательно без усложнений кода - итак с головы выпадает всё. если нельзя - как дать время на выполнение?

Answer 1

В общем говоря, при наличии достаточно большого файла, каким бы оптимальным ни был алгоритм, вы все равно будете упираться в таймлимит, Для заведомо больших задач его отключают.

Давайте посмотрим, что и как влияет на производительность. Я сгенерировал массив из 200к значений и записал его в файл. На момент конца генерации объем использованной памяти (memory_get_usage()) составил 29 Мбайт.
Далее я отключил тайм лимит (set_time_limit(0)) и запустил ваш скрипт, дабы проверить, сколько же он будет выполняться, и завершился тон через 770 секунд.

Предполагаем, что основная причина долгой обработки в том, что 200к раз выполняется slice (долгий не сам по себе слайс, а вероятно то, что менеджер памяти постоянно что-то выделяет, а коллектор уничтожает). Если заменить array_sum(array_slice( ... )) на функцию, считающую сумму элементов вручную

$rangeSum = function($data, $s) use ($count){
    $sum = 0;
    $end = min($s+30, $count);
    while($s < $end){
         $sum += $data[$s++];
    }
    return $sum;
};

время работы - 1.4 секунды.

Следующий вариант - посчитать сумму первых 30 элементов. А дальше двигаться по массиву. Вычесть первый, добавить следующий:

$i = 0;
while($i < 30) $sum += $arr[$i++];
$x = $sum > 50 ? 1 : 0;
for($i = 30; $i < $count; $i++){<-->
    $sum += -$arr[$i-30] + $arr[$i];
    if($sum > 50) $x++;
}

Тут я немного пренебрег концом массива, и фактически последние 30 сумм не считает, но результат - 0.25 сек.

Есть еще вариант - читать файл построчно. Но т.к. вам потребуется хранить текущие 30 элементов в массиве и каждый раз делать shift/push (чтобы хранить этот -30 элемент), то его производительность будет ниже ~0.35сек, зато вы всегда храните только 30 значений, а не 200тыс, поэтому потребление памяти будет минимально.

while( ($v = fgets($f)) !== false){
    $m = array_shift($values);
    $sum += -$m + $v;
    $values[] = $v;
    if($sum > 50) $x++;
}

предварительно мы тут уже считали первые 30 строк и просуммировали.

зы: весь приведенный код вырван из контекста и не полон, просто для передачи смысла

Answer 2

Ну, если ничего не перепутал, то можно так -

<?php
$arr = file('arr.txt');
$sum = array_sum(array_slice($arr,0,30));
for($i = 1; $i<count($arr); $i++) {
    if($sum > 50) {
       echo ($i-1) . ' ' . $sum . "<br>";
    }
    $sum = $sum + $arr[$i] - $arr[$i-1] ;
}
?>
READ ALSO
Безопасная загрузка WORD файла. Теория. PHP

Безопасная загрузка WORD файла. Теория. PHP

мой вопрос теоретическийВеб-приложение будет работать только с Word файлами (буду делать в учебных для меня целях СЭД) и загрузить-то с проверкой...

125
Отправка писем через PHPMailer и AJAX

Отправка писем через PHPMailer и AJAX

Всем привет! Не получается сделать отправку писем на сайте через AJAXНажимаю на кнопку отправить и ничего не происходит

128
Преобразовать объект в массив PHP

Преобразовать объект в массив PHP

Хотел бы поинтересоваться, как преобразовать объект, получаемый от АПИ ТелеграммаНиже моя попытка:

163
Найти точное значение в строке

Найти точное значение в строке

Есть строка с числамиКаждое значение разделено запятыми

155