【问题标题】:How can I compare two sets of 1000 numbers against each other?如何比较两组 1000 个数字?
【发布时间】:2011-04-25 22:43:38
【问题描述】:

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

我在服务器端加载并比较了它们:

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

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

我不需要加载不一样的数字。

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

【问题讨论】:

  • 请看我的回答我怀疑搜索算法的优化是正确的答案..

标签: php javascript sql algorithm


【解决方案1】:

使用 WebAssembly 而不是 JavaScript。

【讨论】:

    【解决方案2】:

    【讨论】:

    • 然而,两者最多都是 O(n.log n)
    • @dhruvbird 问题“我必须检查大约 1000 个号码和 1000 个其他号码。”上面的 PHP 函数按要求执行。返回的输出应该是用户可以轻松操作以利用他们的 doBla() 的期望结果
    • “但是,两者最多都为 O(n.log n)”理论上是正确的,但调用哈希表查找 O(log n) 并不能真正做到公正。在实践中,它的行为更像 O(1)
    • @dhruvbird,O(n log n) 是解决这个问题的最佳方法。
    • @phill:但是 OPs 代码可能会打印 n^2 个数字,这意味着复杂度不能小于 O(n^2)。
    【解决方案3】:

    合并、排序然后计数

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • +1,正如 Preet 所说,要小心它是现代 Db,比如说... 1974 年后:P
      • 我很想知道 codasyl dbs 是如何处理这个问题的。
      • 我不确定为什么假设这些数组的元素来自数据库。是因为他用 PHP 和 Javascript 演示了一个例子吗?来源可以是任何东西,真的。
      • 是的,解决方案可以是任何东西,真的,因为它也被标记为 SQL。
      • 这将完全缓解页面加载问题,因为数据库肯定会比 javascript 解释器快得多,即使它效率低下。跨度>
      【解决方案5】:

      首先对列表进行排序。然后,您可以从一开始就遍历这两个列表,然后进行比较。

      循环看起来像这样:

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

      【讨论】:

      • 如果不是,那么.. 如果 A 的大小为 n,B 的大小为 m,则需要 nlg(n)+mlg(m)+min(m,n) 时间,而, OP 的方法就是 mn ...
      • 如果 mn 都很大 - 正如问题中规定的那样 - 那么排序绝对会更快。算一算!对 1000 个元素的数组进行排序大约需要 3000 次操作,因此是 3000+3000+1000。但是 1000 * 1000 是 工作量的 100 倍
      • 是的,但是对于两个大小为 1000 的数组,即 1000*lg(1000) + 1000*lg(1000) + 1000,其中 ~ 2000*10 + 1000,其中 ~ 21000。OP 的方法,mn 时间是 1000^2,即 1000000。21000 lg(n)), nlg (n) = O(mn) ,这意味着 Pointy 的方法要快得多,因为 lg(n)
      • 此代码不会重现 OP 的代码所做的事情。如果x 分别在列表 1 和 2 中出现 x1 次和 x2 次,则 doBla() 应该运行 x1 * x2 次,这里不是这种情况。
      • @Kache4 这将是一个相当简单的更改 - 我将把它作为练习留给读者:-)
      【解决方案6】:

      这个问题可以分成两个任务。第一项任务是找到所有组合 (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]);
        }
       } 
      

      【讨论】:

        【解决方案7】:

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

        这很有趣 - 作为答案的优化技术的数量 - 问题不是你的算法 - 无论 doBla() 做什么,它所花费的时间比任何优化都会帮助你的时间大很多倍:) 特别是。鉴于这些集合只有 1000 长,您必须先对它们进行排序..

        【讨论】:

        • 我想知道你为什么被否决了?你是对的,当然——即使是他的蛮力比较也应该相当快。典型情况下他一定是调用了很多次doBla,不然每次执行都需要很长时间……
        【解决方案8】:

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

        不是一个 PHP 人,但这应该会给你一个想法:

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

        显然不是 PHP 人,我不知道这个库,但我确信排序和二进制搜索应该很容易找到。

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

        【讨论】:

        • 如果所需的行为与原始问题中的实现完全相同,在 $numbers1 有三个 1 并且 $numbers2 有两个 1 的情况下,doBla() 将运行 6 次。在这个实现中,它只会运行 3 次。
        • 不幸的是,第 1 行有一个错误。PHP 的 sort() 将在原地更改 $numbers2,并返回 true 或 false。 es.php.net/sort
        • @Adam 谢谢,已修复。如果任何了解 PHP 的人想要修复我的示例,请继续,它是开源的 ;)
        【解决方案9】:

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

        如果数字已经在 SQL 数据库中,则进行连接,让数据库找出最有效的路线。

        如果它们不在数据库中,那么我敢打赌你在某个地方偏离了轨道,真的应该重新考虑你是如何到达这里的。

        【讨论】:

        • 我们只知道他有 2000 个号码,关于他在哪里以及如何得到不涉及 SQL 数据库的号码,有无穷无尽的可能性。
        • 他确实提到了在数据库中做这项工作,然后想知道 MySQL 是否足够强大来处理负载 ;-)
        【解决方案10】:

        每次在$numbers2 中找到$numbers1 中的值时,此代码将调用一次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();
                }
            }
        }
        

        如果您只需要在找到匹配项时调用一次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() 可能比遍历数组花费更多时间。

        【讨论】:

          【解决方案11】:

          我不确定为什么 Mrk Mnl 被否决,但 函数调用是开销

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

          【讨论】:

            【解决方案12】:

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

            所以,在伪代码中,它会变成......

            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)。

            【讨论】:

              【解决方案13】:

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

              【讨论】:

              • 危急时刻需要绝招。
              【解决方案14】:

              如果你想得到一个没有任何重复的数字列表,你可以使用哈希:

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

              它会比数组遍历方法稍慢(非常轻微),但在我看来它更干净。

              【讨论】:

                【解决方案15】:
                1. 创建两个重复的集合,最好是具有快速查找时间的集合,例如 HashSet 或 TreeSet。避免使用列表,因为它们的查找时间很短。

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

                【讨论】:

                  【解决方案16】:

                  如果你使用桶排序,你可以在 O(n) 时间内完成。假设您知道数字可以取的最大值(尽管有一些方法)。

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

                  【讨论】:

                    【解决方案17】:

                    更好的方法是这样做:

                    // 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(n2) 并且没有办法减少——除非你改变程序的语义。这是因为在最坏的情况下,如果两个列表中的所有数字都相同,程序将调用 doBla() n2 次。但是,如果两个列表都有唯一编号(即通常在列表中唯一),则运行时将趋向于 O(n)。

                    【讨论】:

                    • 如果您的算法的最坏情况运行时间与原始算法的运行时间相同,那么您将失去可读性。无论如何,OP 中没有足够的细节来弄清楚实际需要什么以及数组的组成是什么。
                    • @srdjan:我认为很明显,只要数组 1 中的数字与数组 2 中的数字匹配,OP 就想调用 doBlah()。他的解决方案是 Theta(n^2) 而这个是 O(n^2) 和 Omega(n)。
                    【解决方案18】:

                    我不是 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 个数字。

                    【讨论】:

                      【解决方案19】:

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

                      【讨论】:

                      • @ebneter 我发现它比 RPG II 参考书更具可读性。 :)
                      【解决方案20】:

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

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

                      【讨论】:

                        【解决方案21】:

                        也许我在这里没有看到任何东西,但这看起来像是集合交集的经典案例。下面是 perl 中的几行代码。

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

                        @union = 键 %union; @isect = 键 %isect;

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

                        【讨论】:

                          【解决方案22】:
                          $same_numbers = array_intersect($numbers1, $$numbers2);
                          
                          foreach($same_numbers as $n)
                          {
                            doBla();
                          }
                          

                          【讨论】:

                            【解决方案23】:

                            您的代码比需要的更复杂。

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

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

                            【讨论】:

                              【解决方案24】:

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

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

                              【讨论】:

                                【解决方案25】:

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

                                【讨论】:

                                • 如果这些都在 RAM 中,那为什么还要费尽心思写到磁盘等等呢?
                                【解决方案26】:

                                先排序。

                                【讨论】:

                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2012-09-02
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多