Оптимизировать код или как выделить всю вычислительную мощность пк на его выполнение?

Есть массив целых. Задача:

Представить все значения как диапазоны: значение .. (значение + 50)
Взять первое значение (диапазон)
отсечь все пересекающиеся с ним диапазоны ниже
взять второе
отсечь пересекающиеся
следующий
и так далее

Сохранение порядка обязательно.

Код:
<?php
$arr = [100,125,75,175,25,300,275,325,375];
$step = 50;

for ($i = 0; $i <= count($arr)-1; $i++) {
    $value = $arr[$i];
    $arr = array_values(array_filter($arr, function($v) use ($value,$step) {
        return ($value+$step < $v or $v+$step < $value or $v == $value);
    }));
}

var_dump($arr); // [100, 175, 25, 300, 375]
?>


100к чисел этим кодом обрабатывать сутки.
Перебирать без функций "вручную"?
Или что ещё можно сделать?
  • Вопрос задан
  • 947 просмотров
Решения вопроса 1
dollar
@dollar
Делай добро и бросай его в воду.
Первое, что бросается в глаза - это многократное копирование массива. Представьте, что при сортировке мы бы после каждых двух-трех перестановок делали бы полный дубликат массива. Это же ужас! И это слабое место, постоянное перевыделение памяти больших размеров.

Второе, что тоже важно - это сложность O(N*N). В вашем случае это критично, потому что много элементов в исходном массиве.

Предлагаю немного изменить алгоритм. Делаем одно прохождение, но немного увеличиваем потребление памяти, в которой храним интервалы. Таким образом, мы избавляемся от постоянного копирования массива, а также уменьшаем сложность примерно до O(N).

И маленькая оптимизационная хитрость - поиск интервала происходит по индексу, то есть O(1). Нужно немного поразмыслить, чтобы до этого догадаться, но в целом всё просто.
Код
<?php
$arr = [100,125,75,175,25,300,275,325,375];
$step = 50;

$b = []; //-1 - deny, 0 - not set, 1 - has interval
$int = []; //intervals if necessary 
$step2 = intdiv($step,2);
$arr = array_values(array_filter($arr, function($v) use ($step2,&$b,&$int) {
    $i = intdiv($v,$step2);
    $mod = $v % $step2;
    $res = true;
    if (isset($b[$i])) {
        if ($b[$i] === -1) $res = false;
        elseif ($mod < $int[$i][0] or $mod > $int[$i][1]) $res = false;
    }
    $b[$i] = -1;
    $b[$i+1] = -1;
    $b[$i-1] = -1;
    if (!isset($b[$i+2])) {
        $b[$i+2] = 1;
        $int[$i+2] = [$mod,$step2];
    } elseif ($b[$i+2] === 1) {
        if ($int[$i+2][0] < $mod) {
            $int[$i+2][0] = $mod;
            if ($int[$i+2][0] >= $int[$i+2][1]) $b[$i+2] = -1;
        }
    }
    if (!isset($b[$i-2])) {
        $b[$i-2] = 1;
        $int[$i-2] = [0,$mod];
    } elseif ($b[$i-2] === 1) {
        if ($int[$i-2][1] > $mod) {
            $int[$i-2][1] = $mod;
            if ($int[$i-2][0] >= $int[$i-2][1]) $b[$i-2] = -1;
        }
    }
    return $res;
}));

var_dump($arr); // [100, 175, 25, 300, 375]
?>

Переписав алгоритм на С++, получите дополнительно 50-кратное увеличение скорости.
Ответ написан
Пригласить эксперта
Ответы на вопрос 5
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Скопировать исходный массив A в массив B, дополнить каждый элемент индексом в исходном массиве, отсортировать по значению элементов. Завести массив C такого же размера как исходный, заполнить его нулями.
Сделать текущим первый элемент А.

Начало цикла.
Отметить в C текущий элемент. Найти в массиве B текущий элемент элемент. Просмотреть соседние элементы B и отметить в С как отсечённые все те, диапазоны которых пересекаются с текущим. Найти следующий не отмеченный элемент С, сделать его текущим, перейти к началу цикла, если все элементы C отмечены -- закончить.

Количество действий зависит от количества пересекающихся отрезков, оно будет больше O(N log N) но меньше N^2.
Ответ написан
Комментировать
tumbler
@tumbler
бекенд-разработчик на python
Стоит задача выделил из каждого видео 2 динамичные минуты.
VLC плеер считает и записывает кол-во изменений в соседних кадрах.

Вариант 1: нужен один двухминутный отрезок
Cамыми динамичными будут 120 секунд видео, имеющие максимальную сумму изменений. Соответственно, берем окно шириной 120 секунд, и проходом по всему массиву считаем сумму чисел (на каждой итерации +1 новое и -1 старое значение). По дороге отмечаем таймстепм максимума.
Вариант 2: нужно top-120 самых динамичных секунд
Линейным проходом по всему массиву поддерживаем этот топ в актуальном состоянии (вставка в отсортированный список, отрезание самого меньшего значения), по окончании прохода - пересортируем топ по таймстемпам секунд.
Ответ написан
Stalker_RED
@Stalker_RED
найти максимальное значение
если их несколько одинаковых - посчитать сколько. это все за один проход.

записать в результат их в виде диапазонов. все.
Ответ написан
ThunderCat
@ThunderCat Куратор тега PHP
{PHP, MySql, HTML, JS, CSS} developer
Стоит задача выделил из каждого видео 2 динамичные минуты. ...
Суммирую кол-во изменений в каждой секунде сдвигая по кадру:
сумма 0-30...

может сумма 0 - 120*30 и далее?
или вам нарезка из секундных кусков нужна?
или 2 минуты подряд?
Ответ написан
SagePtr
@SagePtr
Еда - это святое
Жадное решение за 120*(n+30) итераций:
1) Пробегаем по массиву окном в 30 кадров, находим максимальную сумму, запоминаем (кладём в массив результата).
2) Полученным 30 кадрам присваиваем отрицательный вес (например, равный -30 * (макс.значение в массиве), дабы точно не попал в следующую выборку).
3) Повторяем п 1-2 120 раз, пока не получим 120 не пересекающихся диапазонов (если отрицательное значение в пункте 2 выбрано правильно), отсортированных по убыванию веса. Естественно, если мы храним их как пару (время, вес), то можем потом обратно отсортировать по времени.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы