【问题标题】:Best way to find differences between two large arrays in PHP在 PHP 中查找两个大型数组之间差异的最佳方法
【发布时间】:2012-02-08 06:13:59
【问题描述】:

我有 2 个非常大的数组(大小约为 2,500,000)。我需要找到这些数组之间的区别。 我的意思是我需要一个结果数组,其值在数组 1 中但不在数组 2 中。我使用了 array_diff() 但需要半个多小时!

第一个数组来自一个数据库,第二个数组来自另一个数据库。它们不在同一个数据库服务器上。数组的大小不同。我正在处理大量的手机号码。我需要找出那些在一个列表中但不在另一个列表中的手机号码

数组是带有数字键的普通数组。 diff代码如下:

$numbers_list = array_diff($numbers_list, $some_other_list);

有没有更好的方法来做到这一点?请帮忙。

【问题讨论】:

  • 虽然我不熟悉 PHP 的内部结构,但 array_diff() 似乎已经经历了几轮优化,并且可能会比您使用的任何通用数组差分函数更快自己滚。如果您对数据的结构有更好的了解,可能会有一种更快的方法来计算您正在查看的特定数组的差异。或者,正如之前的评论者所说,您可能希望先从数据库中获取差异。
  • 如果它的数字(实数,即可以解释为整数)你应该能够sort他们两个,并循环通过数组1,在数组2中用next推进指针只要如 value-in-2
  • 在这种情况下,将每个数据库中的数据转储到文本文件中,然后使用 diff 之类的命令行工具进行比较,而不是在 PHP 中尝试,可能会更容易;或者从一个数据库进行转储,在第二个数据库上加载到临时表,然后使用 SQL 进行比较。
  • 如果您只需要执行一次,则将其中一个表复制到另一个数据库中的临时表,执行为您提供差异的查询,然后完成它。
  • 可以使用联合表吗?这样您就可以将两个表连接到一个数据库中,并以不同的方式调用结果。

标签: php arrays performance algorithm


【解决方案1】:

这是简单的算法。

  1. 翻转第一个数组。值将成为键。所以重复的值将被丢弃。
  2. 翻转第二个数组(可选)
  3. 检查第二个数组中的每个元素是否存在于第一个数组中。

当您使用非常大的数组时,它会消耗大量内存

这是我的实现,

$a = file("l.a"); // l.a is a file contains 2,500,000 lines
$b = file("l.b");

function large_array_diff($b, $a){
    // Flipping 
    $at = array_flip($a);
    $bt = array_flip($b); 
    // checking
    $d = array_diff_key($bt, $at);

    return array_keys($d);   
}

我使用 4G 内存限制运行它。 3G也可以。刚刚测试过。

$ time php -d memory_limit=4G diff_la.php

大约需要 11 秒!

real    0m10.612s
user    0m8.940s
sys     0m1.460s

更新

以下代码的运行速度比上述 large_array_diff 函数快 2 倍。

function flip_isset_diff($b, $a) {
    $at = array_flip($a);
    $d = array();
    foreach ($b as $i)
        if (!isset($at[$i])) 
            $d[] = $i;

    return $d;
}

因为它不调用array_flip(1次)、array_diff_keyarray_keys。因此节省了大量的 CPU 周期。

【讨论】:

  • 这些数组中没有重复项。
  • 即使没有重复,转置也可以使用 hash 进行搜索。
  • 嗯,翻转它 & array_diff_key 比我在另一个答案中的方法快大约 5 倍,比这里测试中的普通差异快大约 30 倍......
  • 我确实尝试了上述步骤,但花费的时间超过 20 分钟,所以我关闭了网页以尝试其他替代方案。
  • 不要在网页中运行,在控制台中运行。
【解决方案2】:

好的,更新了,转换为字符串以获得更好的度量(如果我们可以使用整数而不是字符串,这会产生很大的不同......):

<?php
$start = microtime(true);
echo 'creating' . PHP_EOL;
$array1 = array();
$array2 = array();
for ($i = 0;$i < 2000000;$i++) {
    $array1[] = (string)rand(100000000, 999999999);
    $array2[] = (string)rand(100000000, 999999999);
}
echo (microtime(true) - $start) . PHP_EOL;
$start = microtime(true);
echo 'key diff flip' . PHP_EOL;
$array1f = array_flip($array1);
$array2f = array_flip($array2);
echo (microtime(true) - $start) . PHP_EOL;
$start = microtime(true);
echo 'key diff' . PHP_EOL;
$diff = array_diff_key($array1f, $array2f);
echo (microtime(true) - $start) . PHP_EOL;
$start = microtime(true);
echo 'sorting' . PHP_EOL;
$start = microtime(true);
sort($array1);
sort($array2);
$notin2 = array();
reset($array2);
echo (microtime(true) - $start) . PHP_EOL;
echo 'comparing' . PHP_EOL;
$start = microtime(true);
foreach ($array1 as $value) {
    while (current($array2) < $value) {
        if (next($array2) == false) break;
    }
    if (current($array2) != $value) $notin2[] = $value;
}
echo (microtime(true) - $start) . PHP_EOL;
echo 'plain diff' . PHP_EOL;
$start = microtime(true);
$diff = array_diff($array1, $array2);
echo (microtime(true) - $start) . PHP_EOL;
?>

字符串

creating
8.66658186913
key diff flip
3.07359004021
key diff
1.48775887489
sorting
48.4047858715
comparing
9.41756916046
plain diff
19.21976614

real    1m37.574s
user    1m34.970s
sys     0m1.676s

整数 (删除(string)):

creating
4.69975805283
key diff flip
2.5843539238
key diff
1.4868490696
sorting
15.2628200054
comparing
5.62516498566
plain diff
101.688895941

real    2m18.090s
user    2m15.112s
sys     0m1.356s

令我惊讶的是......array_diff 似乎讨厌整数......

【讨论】:

  • 是的,无论如何,编辑它以适合您所需的制表符宽度和空白使用。吉兹。
猜你喜欢
  • 2014-01-05
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
相关资源
最近更新 更多