Выбор каждого n-го элемента из массива - PullRequest
14 голосов
/ 21 декабря 2009

Каков был бы самый эффективный способ выбора каждого n-го элемента из большого массива? Есть ли «умный» способ сделать это или это единственный цикл?

Некоторые моменты для рассмотрения:

  • Массив довольно большой с 130 000 элементов
  • Я должен выбрать каждый 205-й элемент
  • Элементы не пронумерованы, поэтому for($i = 0; $i <= 130000; $i += 205) не будет работать

Пока что это самый эффективный метод, который я придумал:

$result = array();
$i = 0;
foreach($source as $value) {

    if($i >= 205) {
        $i = 0;
    }

    if($i == 0) {
        $result[] = $value;
    }

    $i++;
}

Или то же самое с модулем:

$result = array();
$i = 0;
foreach($source as $value) {
    if($i % 205 == 0) {
        $result[] = $value;
    }
    $i++;
}

Эти методы могут быть довольно медленными, есть ли способ улучшить? Или я просто раскалываю здесь волосы?

EDIT

Хорошие ответы вокруг с правильными объяснениями, пытался выбрать наиболее подходящий в качестве принятого ответа. Спасибо!

Ответы [ 8 ]

15 голосов
/ 21 декабря 2009

Цикл foreach обеспечивает самую быструю итерацию по вашему большому массиву на основе сравнительного тестирования. Я бы придерживался чего-то похожего на то, что у вас есть, если кто-то не захочет решить проблему с развертыванием цикла .

Этот ответ должен выполняться быстрее.

$result = array();
$i = 0;
foreach($source as $value) {
    if ($i++ % 205 == 0) {
        $result[] = $value;
    }
}

У меня нет времени на тестирование, но вы можете использовать вариант решения @ haim, если сначала численно индексировать массив. Стоит попробовать посмотреть, сможете ли вы получить какую-либо прибыль по сравнению с моим предыдущим решением:

$result = array();
$source = array_values($source);
$count = count($source);
for($i = 0; $i < $count; $i += 205) {
    $result[] = $source[$i];
}

Это во многом будет зависеть от того, насколько оптимизирована функция array_values. Это может очень хорошо работать ужасно.

7 голосов
/ 21 декабря 2009

Попробуйте ArrayIterator :: seek ()

Кроме того, использование одной новой структуры данных Spl может дать лучшие результаты, чем использование простых массивов.

6 голосов
/ 21 декабря 2009

Рекомендую к использованию array_slice

$count = count($array) ;
for($i=205;$i<$count;$i+=205){
    $result[] = array_slice($array,$i,1);
}

Если бы ваш массив численно индексировался, это было бы очень быстро:

$count = count($array) ;
for($i=205;$i<$count;$i+=205){
    $result[] = $array[$i];
}
2 голосов
/ 21 декабря 2009

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

Вы можете либо сделать числовой индекс массива (возможно, это не подходит для вашего приложения), отследить каждый 205-й элемент или выполнить поиск в массиве только один раз (кэшировать список каждого 205-го элемента).

На мой взгляд, отслеживать каждый 205-й элемент было бы проще для реализации. Вы просто будете вести подсчет всех элементов в базе данных или что-то в этом роде, и каждый раз, когда элемент добавляется, проверяйте его по модулю. Если у вас есть еще 205-й элемент, добавьте его в массив. Однако, когда элементы удаляются, это будет сложнее. Возможно, вам придется перепроверить весь массив, чтобы выровнять все ваши 205-е элементы.

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

  • Числовые индексы - лучшее долгосрочное решение, но сложнее в реализации
  • Отслеживание - проще реализовать, но вам придется снова испачкаться при удалении элементов
  • Кэширование элементов - вероятно, вы должны сделать это и для двух других решений, но само по себе это будет быстро, пока массив не будет изменен, и в этом случае вам, вероятно, придется сделать это заново.
1 голос
/ 21 декабря 2009

Если это действительно узкое место, вы можете подумать о переосмыслении своего дизайна, чтобы сделать его числовым индексом.

РЕДАКТИРОВАТЬ: Или создать и поддерживать отдельный массив только с 205-ю элементами (который обновляется при вставке или что-то в этом роде).

0 голосов
/ 21 декабря 2009
  • Создать двумерный массив [205] [N]
  • Загрузка данных в массив
  • Доступ к 205-му элементу для каждого N

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

0 голосов
/ 21 декабря 2009

Вы можете использовать array_keys для работы только с ключами массива.

$keys = array_keys($array);
for ($i=0, $n=min(count($keys), 130000); $i<$n; $i += 205) {
    $result[] = $array[$keys[$i]];
}
0 голосов
/ 21 декабря 2009

Кажется, вы не можете перемещать указатель массива более одного раза за раз. Я бы лично использовал это:

reset($source);
$next = true;
while($next === true){
    $result[] = current($source);
    for(i=0;i<205;i++){
        $next = next($source);
    }
}

Если кто-то может найти функцию, которая может перемещать указатель массива более чем на один шаг за раз, у вас будет лучший ответ. Я думаю, что это хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...