如何比较两组1000个数字?

时间:2010-10-15 13:15:35

标签: php javascript sql algorithm

我必须检查大约1000个数字和1000个其他数字。

我加载了两个并比较了服务器端:

foreach( $numbers1 as $n1 ) {
  foreach( $numbers2 as $n2 ) {
    if( $n1 == $n2 ) {
      doBla();
    }
  }
}

这需要很长时间,所以我尝试使用两个隐藏的同一个比较客户端 div元素。然后使用JavaScript比较它们。加载页面仍需要45秒(使用隐藏的div元素)。

我不需要加载不相同的数字。

是否有更快的算法?我正在考虑比较它们的数据库端,只是加载错误号,然后对剩余的非错误号进行Ajax调用。但MySQL数据库的速度是否足够快?

26 个答案:

答案 0 :(得分:129)

首先对列表进行排序。然后你可以从头开始走两个列表,然后进行比较。

循环看起来像这样:

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中,在散列之前转换为字符串也非常便宜),这将非常快。

答案 1 :(得分:85)

答案 2 :(得分:27)

在数据库术语中,这可以将1000行连接到另外1000行。任何现代数据库系统都可以处理这个问题。

select x from table1
inner join table2
on table1.x = table2.y

其中table1table2是相关的行,可能是同一个表。

答案 3 :(得分:26)

你不应该花那么长时间 - doBla()做什么?我怀疑是花时间吗?使用相同的算法比较两组1000000个数字根本不需要时间..

这很有趣 - 作为答案的优化技术的数量 - 问题不是你的算法 - 无论doBla()做什么,花费的时间比任何优化都可以帮助你的因素多几倍......尤其是。鉴于这些集只有1000长,你必须先排序..

答案 4 :(得分:23)

也许只是将数组值相交以找到两个数组中都存在的数字?

$result = array_intersect($numbers1, $numbers2);
foreach ($result as $val)
  doBla();

答案 5 :(得分:9)

如果你先对list2进行排序,然后对list1中的每个数字进行二进制搜索,你会看到速度的巨大提升。

一个PHP家伙,但这应该会给你一个想法:

sort($numbers2);

foreach($numbers1 as $n1)
{
   if (BinarySearch($numbers2, $n1) >= 0) {
     doBla();
 }
}

显然不是一个PHP人我不懂图书馆,但我确信排序和二元搜索应该很容易找到。

注意:如果您不熟悉二进制搜索;你正在对list2进行排序,因为二进制搜索需要对排序列表进行操作。

答案 6 :(得分:5)

停止 - 你为什么要这样做?

如果这些数字已经存在于SQL数据库中,那么请进行连接并让数据库找出最有效的路径。

如果他们不在数据库中,那么我打赌你已经偏离了某个地方而且真的应该重新考虑你是如何来到这里的。

答案 7 :(得分:5)

我不是PHP专家,所以这可能需要一些调试,但你可以在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个数字。

答案 8 :(得分:5)

先排序。

答案 9 :(得分:4)

$same_numbers = array_intersect($numbers1, $$numbers2);

foreach($same_numbers as $n)
{
  doBla();
}

答案 10 :(得分:3)

对两个列表进行排序,然后使用old-master new-master sequential update pattern同时遍历两个列表。只要您可以对数据进行排序,这是最快的方式,因为您实际上只需要将列表一次,到最大列表的最长长度。

答案 11 :(得分:2)

您的代码根本需要更复杂。

假设你要找的是每个位置的数字匹配(而不仅仅是数组包含相同的数字),你可以将你的循环展平为单个。

<?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);

?>

答案 12 :(得分:2)

也许我没有在这里看到一些东西,但这看起来像集合交集的经典案例。这是perl中的几行,它会做到这一点。

  

foreach $ e(@ a,@ b){$ union {$ e} ++&amp;&amp; $ isect {$ e} ++}

     

@union = keys%union;   @isect = keys%isect;

在这些代码行的末尾,@ isect将包含@a和@b中的所有数字。我确信这可以或多或少地直接转换为php。 FWIW,这是我最喜欢的Perl Cookbook代码。

答案 13 :(得分:2)

如果您使用铲斗排序,则可以在O(n)时间内完成。假设您知道数字可以采用的最大值(尽管有很多方法)。

http://en.wikipedia.org/wiki/Bucket_sort

答案 14 :(得分:1)

我认为使用内置的array_intersect函数要容易得多。使用您的示例,您可以:

$results = array_intersect($numbers1, $numbers2);
foreach($results as $rk => $rv) {
    doSomething($rv);
}

答案 15 :(得分:1)

更好的方法是做这样的事情:

// 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)摊销的。)

更新:此算法的最坏情况是O(n 2 )并且无法减少 - 除非您更改程序的语义。这是因为在最坏的情况下,如果两个列表中的所有数字相同,程序将调用doBla()n 2 次。但是,如果两个列表都有唯一的数字(即列表中通常是唯一的),那么运行时将倾向于O(n)。

答案 16 :(得分:1)

我将在Visual Basic中创建一个GUI界面,看看我是否可以跟踪数字

答案 17 :(得分:1)

Mergesort两个列表,从两个列表的开头开始,然后在每个列表中同时搜索相似的数字。

所以,在伪代码中,它会像......一样......

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++;

}

如果我是对的,这个算法的速度是O(n logn)。

答案 18 :(得分:1)

我不确定为什么Mrk Mnl被低估了,但函数调用是开销

将匹配后的数字推出到另一个数组中,并在>比较之后将doBla()推出。作为测试//输出doBla()并查看您是否遇到了相同的性能问题。

答案 19 :(得分:0)

使用WebAssembly而不是JavaScript。

答案 20 :(得分:0)

这个问题可以分为2个任务。第一项任务是找到所有组合(n ^ 2-n)/ 2。对于n = 1000,解是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]);
  }
 } 

答案 21 :(得分:0)

每次在doBla()中找到$numbers1中的值时,此代码都会调用$numbers2一次:

// 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();
        }
    }
}

如果找到匹配项,您只需拨打一次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();

我同意之前的几篇帖子,对doBla()的调用可能比迭代数组花费更多时间。

答案 22 :(得分:0)

合并,排序然后计算

<?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));
?>

输出/计数为3的值是您想要的值

Array
(
    [1001] => 2
    [1002] => 3
    [1003] => 3
    [1004] => 3
    [1005] => 3
    [1006] => 1
)

答案 23 :(得分:0)

如果您尝试获取没有任何重复项的数字列表,可以使用哈希:

$unique = array();
foreach ($list1 as $num) {
  $unique[$num] = $num;
}
foreach ($list2 as $num) {
  $unique[$num] = $num;
}
$unique = array_keys($unique);

它会比数组步行方法稍微(非常轻微)慢,但在我看来它更清晰。

答案 24 :(得分:0)

  1. 创建两个重复的集合,最好是具有快速查找时间的集合,如HashSet或TreeSet。避免列表,因为它们的查找时间非常短。

  2. 找到元素后,从两个集合中删除它们。这可以通过在以后的搜索中筛选更少的元素来减少查找时间。

答案 25 :(得分:0)

是否可以将这些数字放入两个数据库表中,然后执行INNER JOIN?这将非常有效,并且仅提供两个表中包含的数字。这是数据库的完美任务。