我怎样才能比较两组1000个号码?
我必须检查大约1000个数字与其他1000个数字。
我加载和比较他们的服务器端:
foreach( $numbers1 as $n1 ) { foreach( $numbers2 as $n2 ) { if( $n1 == $n2 ) { doBla(); } } }
这花了很长时间,所以我试图用两个隐藏的div
元素做相同的客户端比较。 然后使用JavaScript进行比较。 加载页面仍然需要45秒(使用隐藏的div
元素)。
我不需要加载不相同的数字。
有一个更快的algorithm? 我想比较他们的数据库端,只是加载错误号码,然后做一个Ajax调用其余的非错误号码。 但是MySQL数据库是否足够快?
首先对列表进行sorting。 然后,你可以从一开始就走上这两个名单,比较你走。
循环看起来像这样:
var A = getFirstArray().sort(), B = getSecondArray().sort(); var i = 0, j = 0; while (i < A.length && j < B.length) { if (A[i] === B[j]) { doBla(A[i]); i++; j++; } else if (A[i] < B[j]) { i++; } else j++; }
(这是JavaScript;你也可以在服务器端做,但我不知道PHP。)
编辑 – 为了公平对所有的哈希表粉丝(我当然尊重他们),在JavaScript中这样做很容易:
var map = {}; for (var i = 0; i < B.length; ++i) map[B[i]] = true; // Assume integers. for (var i = 0; i < A.length; ++i) if (map[A[i]]) doBla(A[i]);
或者如果这些数字是或可能是浮动的:
var map = {}; for (var i = 0; i < B.length; ++i) map['' + B[i]] = true; // Assume integers. for (var i = 0; i < A.length; ++i) if (map['' + A[i]]) doBla(A[i]);
由于数字相当便宜哈希(即使在JavaScript中,散列之前转换为string是惊人的便宜),这将是非常快。
-
array_intersect()
返回匹配 -
array_diff()
返回差异
在数据库方面,这可以将1000行连接到另外1000行。 任何现代数据库系统都可以处理。
select x from table1 inner join table2 on table1.x = table2.y
其中table1
和table2
是有关的行,可能是同一个表。
你不应该花这么长时间 – doBla()做什么? 我怀疑是花时间? 用相同的algorithm比较两组1000000个数字根本不需要任何时间。
这很搞笑 – 优化技术的数量作为答案 – 问题不是你的algorithm – 它是doBla()所花费的时间的倍数比任何优化都要多很多倍,可以帮助你。 鉴于套只有1000长,你必须先sorting
也许只是相交的数组值来find两个数组中存在的数字?
$result = array_intersect($numbers1, $numbers2); foreach ($result as $val) doBla();
如果您先对list2进行sorting,然后对list1中的每个数字进行二进制search,则会看到速度的大幅提升。
我不是一个PHP的人,但这应该给你的想法:
sort($numbers2); foreach($numbers1 as $n1) { if (BinarySearch($numbers2, $n1) >= 0) { doBla(); } }
显然不是一个PHP的家伙,我不知道这个库,但我相信sorting和二进制search应该很容易find。
注意:如果您不熟悉二进制search, 你正在sortinglist2,因为二进制search需要对sorting列表进行操作。
先sorting。
我不是一个PHP专家,所以这可能需要一些debugging,但你可以在O(n)时间轻松地做到这一点:
// Load one array into a hashtable, keyed by the number: O(n). $keys1 = []; foreach($numbers1 as $n1) $keys1[$n1] = true; // Find the intersections with the other array: foreach($numbers2 as $n2) { // O(n) if (isset($keys1[$n2]) { // O(1) doBla(); } }
无论如何,路口不是你的时间。 即使是像现在这样糟糕的O(n ^ 2)实现,也应该能够在一秒钟内完成1000个数字。
停止 – 你为什么这样做?
如果这些数字已经在一个SQL数据库中,那么做一个连接,让数据库找出最有效的路线。
如果他们不在数据库中,那么我认为你已经走到了某个地方,真的应该重新考虑你是如何到达这里的。
$same_numbers = array_intersect($numbers1, $$numbers2); foreach($same_numbers as $n) { doBla(); }
对两个列表进行sorting,然后使用旧主控新主控顺序更新模式同时移动两个列表。 只要您可以对数据进行sorting,那么这是自从您真正只在列表中走一次的最快方式,到最大列表的最长长度。
你的代码只是需要更复杂的。
假设你正在寻找的是每个位置的数字匹配(而不仅仅是数组包含相同的数字),你可以将你的循环平铺为一个单一的。
<?php // Fill two arrays with random numbers as proof. $first_array = array(1000); $second_array = array(1000); for($i=0; $i<1000; $i++) $first_array[$i] = rand(0, 1000); for($i=0; $i<1000; $i++) $second_array[$i] = rand(0, 1000); // The loop you care about. for($i=0; $i<1000; $i++) if ($first_array[$i] != $second_array[$i]) echo "Error at $i: first_array was {$first_array[$i]}, second was {$second_array[$i]}<br>"; ?>
使用上面的代码,你将只循环1000次,而不是循环1000000次。
现在,如果您只需检查数字是否出现在数组中,请使用array_diff和array_intersect,如下所示:
<?php // Fill two arrays with random numbers as proof. $first_array = array(1000); $second_array = array(1000); for($i=0; $i<1000; $i++) $first_array[$i] = rand(0, 1000); for($i=0; $i<1000; $i++) $second_array[$i] = rand(0, 1000); $matches = array_intersect($first_array, $second_array); $differences = array_diff($first_array, $second_array); ?>
也许我没有看到这里的东西,但这看起来像是一个经典的交集。 这里有几行perl就可以做到。
foreach $ e(@a,@b){$ union {$ e} ++ && $ isect {$ e} ++}
@union =键%union; @isect =键%isect;
在@isect这些代码行的末尾将包含@a和@b中的所有数字。 我敢肯定,这是可以直接转换为PHP或多或less。 FWIW,这是我最喜欢的Perl Cookbook的一段代码。
如果使用桶sorting,则可以在O(n)时间内完成。 假设你知道数字可以达到的最大值(虽然有办法)。
我认为使用内build的array_intersect函数会容易得多。 用你的例子,你可以做到:
$results = array_intersect($numbers1, $numbers2); foreach($results as $rk => $rv) { doSomething($rv); }
更好的方法是做这样的事情:
// 1. Create a hash map from one of the lists. var hm = { }; for (var i in list1) { if (!hm[list1[i]]) { hm[list1[i]] = 1; } else { hm[list1[i]] += 1; } } // 2. Lookup each element in the other list. for (var i in list2) { if (hm[list2[i]] >= 1) { for (var j = 0; j < hm[list2[i]]; ++j) { doBla(); } } }
这是保证O(n)[假设在哈希映射中插入查找是O(1)摊销]。
更新:这种algorithm的最坏情况是O(n 2 ),没有办法减less – 除非你改变程序的语义。 这是因为在最坏的情况下,如果两个列表中的所有数字都相同,程序将调用doBla()n 2次。 但是,如果两个列表都有唯一的数字(即在列表中通常是唯一的),那么运行时间将趋向于O(n)。
我将在Visual Basic中创build一个GUI界面,看看我能否跟踪这些数字
合并两个列表,从两个列表开始,然后在每个列表中同时search相似的数字。
所以,在伪代码,它会像…
Mergesort (List A); Mergesort (list B) $Apos = 0; $Bpos = 0; while( $Apos != A.Length && $Bpos != B.length) // while you have not reached the end of either list { if (A[$Apos] == B[$Bpos])// found a match doSomething(); else if (A[$Apos] > B[$Bpos]) // B is lower than A, so have B try and catch up to A. $Bpos++; else if (A[$Apos] < B[$Bpos]) // the value at A is less than the value at B, so increment B $Apos++; }
如果我是对的,这个algorithm的速度是O(n logn)。
我不知道为什么Mrk Mnl是downvoted,但函数调用是在这里的开销 。
在匹配后,将匹配的数字推出到另一个数组,doBla()。 作为一个testing/ /出doBla(),看看你是否遇到同样的性能问题。
有可能把这些数字放到两个数据库表中,然后做一个INNER JOIN
? 这将是非常有效的,只提供了两个表中包含的数字。 这对于数据库来说是一个完美的任务。
-
创build两个重复的集合,最好是具有快速查找时间的集合,比如HashSet或者TreeSet。 避免列表,因为他们有很差的查找时间。
-
当你find元素,从两个集合中删除它们。 这可以通过在稍后的search中筛选更less的元素来减less查找时间。
如果你想获得一个没有任何重复的数字列表,你可以使用哈希:
$unique = array(); foreach ($list1 as $num) { $unique[$num] = $num; } foreach ($list2 as $num) { $unique[$num] = $num; } $unique = array_keys($unique);
这将比数组步行方法略微(稍微)慢一些,但是在我看来,它更干净。
这段代码将每次在$numbers1
中find$numbers2
的值时调用doBla()
:
// get [val => occurences, ...] for $numbers2 $counts = array_count_values($numbers2); foreach ($numbers1 as $n1) { // if $n1 occurs in $numbers2... if (isset($counts[$n1])) { // call doBla() once for each occurence for ($i=0; $i < $counts[$n1]; $i++) { doBla(); } } }
如果您只需要在find匹配项时调用doBla()
一次:
foreach ($numbers1 as $n1) { if (in_array($n1, $numbers2)) doBla(); }
如果$numbers1
和$numbers2
将仅包含唯一值,或者如果两个数组中出现任何特定值的次数都不重要,则array_intersect()
将执行该任务:
$dups = array_intersect($numbers1, $numbers2); foreach ($dups as $n) doBla();
我同意以前的几个post,对doBla()
的调用可能需要比遍历数组花费更多的时间。
这个问题可以分解成2个任务。 第一个任务是find所有组合(n ^ 2-n)/ 2。 对于n = 1000,解决scheme是x = 499500。 第二项任务是遍历所有x个数字,并将它们与函数doBla()进行比较。
function getWayStr(curr) { var nextAbove = -1; for (var i = curr + 1; i < waypoints.length; ++i) { if (nextAbove == -1) { nextAbove = i; } else { wayStr.push(waypoints[i]); wayStr.push(waypoints[curr]); } } if (nextAbove != -1) { wayStr.push(waypoints[nextAbove]); getWayStr(nextAbove); wayStr.push(waypoints[curr]); } }
合并,sorting,然后数
<?php $first = array('1001', '1002', '1003', '1004', '1005'); $second = array('1002', '1003', '1004', '1005', '1006'); $merged = array_merge($first, $first, $second); sort($merged); print_r(array_count_values($merged)); ?>
输出/数值为三是你想要的
Array ( [1001] => 2 [1002] => 3 [1003] => 3 [1004] => 3 [1005] => 3 [1006] => 1 )