【问题标题】:Selecting every nth item from an array从数组中选择每第 n 个项目
【发布时间】:2009-12-21 11:30:07
【问题描述】:

从大数组中选择每第 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++;
}

这些方法可能会很慢,有什么改进的方法吗?还是我在这里扯皮?

编辑

很好的答案到处都有适当的解释,试图选择最合适的答案作为公认的答案。谢谢!

【问题讨论】:

  • 这在我看来是合理的——你确定代码会导致瓶颈吗?如果没有,请剖析一下!需要多长时间?
  • @Dominic,这不是瓶颈,只是一个有趣的问题,我找不到合适的解决方案。不要认为“正确”的答案会减少超过几毫秒的执行时间,但很高兴知道。 :)

标签: php arrays


【解决方案1】:

基于比较测试,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 的优化程度。它可能表现得非常糟糕。

【讨论】:

  • 很好的解释,实际上是一种更快的方法来完成我试图做的事情(只是轻微,但无论如何更快)。谢谢!
  • @Tatu,在对 array_push 和 [] 进行快速比较测试后,我发现 [] 的运行速度大约是 200,000 个项目的两倍。我修改了我的答案,我建议您按照最初的方式使用作业。
  • @Tatu,我很快对上述两种方法进行了基准测试,发现当起始数组平均大于 50,000 个结果时,第二种方法更快。但是,我只有时间从 array_fill() 和 range() 生成的数字索引数组开始进行测试。
  • @CoreyBallou 这是最好的答案。但是,有没有可能显示row号码的详细信息?
【解决方案2】:

试试ArrayIterator::seek()

另外,使用新的Spl datastructures 可能会比使用普通数组产生更好的结果。

【讨论】:

  • 将对此进行调查,我不接受这个作为正确答案的唯一原因是我必须大幅更改我的代码库以有效地支持 ArrayObjects。但注意到并加分。
【解决方案3】:

我推荐使用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];
}

【讨论】:

  • 至少语法清晰了很多,有什么想法会真正提高性能吗?
  • For 循环很慢。你的 count() 也慢得离谱。
  • 将计数排除在循环之外确实会大大加快速度。
  • @cballou,但请注意,此方法只需要执行 130000 / 205 次循环,而 foreach 必须遍历所有 130 000 个项目。我必须对其进行测试,我担心 array_slice 的性能,因为它可能会在内部从 0 循环到 $i。
  • 这在较大的阵列上真的很慢。如果您将 $result=array_slice(...) 更改为 $result[] = $array[$i] 它比接受的解决方案更快。
【解决方案4】:

我认为这个问题的解决方案不在于任何 PHP 语法,而在于你的代码设计。

您可以使数组按数字索引(对于您的应用程序可能不合理),跟踪每 205 项,或者只搜索一次数组(缓存每个第 205 项的列表)。

在我看来,跟踪每 205 个项目会更容易实现。您只需对数据库或其他内容中的所有项目进行计数,每次添加项目时,检查计数的模数。如果您还有第 205 个项目,请将其添加到数组中。但是,当项目被删除时,这将更加棘手。您可能需要重新检查整个数组以重新对齐所有第 205 个项目。

如果您可以从已删除的项目开始并向前移动,这样做会更简单,但这仅适用于数字索引数组 - 如果这是真的,您根本不必向前移动,您只需做一些数学运算即可重新计算出来。

  • 数字索引 -- 更好的长期解决方案,但更难实施
  • 跟踪 - 更易于实施,但删除项目时必须再次弄脏
  • 缓存项目 - 您可能也应该对其他两个解决方案执行此操作,但就其本身而言,在修改数组之前会很快,在这种情况下您可能必须重新执行此操作。李>

【讨论】:

    【解决方案5】:

    如果这确实是一个瓶颈,您可能需要考虑重新考虑您的设计,以便对其进行数字索引。

    编辑:或者创建和维护一个单独的数组,其中只有第 205 个项目(在插入时更新或类似的东西)。

    【讨论】:

      【解决方案6】:
      • 创建二维数组[205][N]
      • 将数据加载到数组中
      • 访问每 N 个元素的第 205 个元素

      可能听起来很傻,但根据定义,它是最快的,因为您直接访问内存位置并且不执行任何比较。

      【讨论】:

        【解决方案7】:

        您似乎不能一次多次移动数组指针。我个人会使用这个:

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

        如果有人能找到一个函数,它可以一次移动不止一步的数组指针,你会有一个更好的答案。不过我觉得这很好。

        【讨论】:

        • 你是对的,一次将指针移动 205 个位置是解决方案,否则我想这些都是相同的东西,但语法不同。
        • 看我的回答。 ArrayIterator::seek 可以将指针移动到位置
        • 如果有些值的计算结果不是 true 怎么办?
        • 当 $next 为 false 时,我们已经到达数组的末尾
        • @Gausie: … 或者如果下一个元素不能被评估为 true:“返回内部数组指针指向的下一个位置的数组值,或者如果没有更多元素,则为 FALSE。”
        【解决方案8】:

        您可以使用array_keys 仅处理数组键。

        $keys = array_keys($array);
        for ($i=0, $n=min(count($keys), 130000); $i<$n; $i += 205) {
            $result[] = $array[$keys[$i]];
        }
        

        【讨论】:

        • 如果数组遍历是瓶颈,是不是所有key的取回都是?
        • @xtofl:PHP 中的数组不像其他语言那样是真正的数组。它们是用哈希表实现的。并且密钥是分开存储的,可能在一个链表中。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-03
        • 1970-01-01
        • 2018-08-17
        • 1970-01-01
        • 2022-12-12
        相关资源
        最近更新 更多