如何更有效地比较大海捞针

时间:2018-12-18 14:47:06

标签: php performance mysqli foreach strpos

我一直在努力提高以下代码的效率。

简而言之;

我有一个包含标题和描述的数据库。该数据库将平均包含10000个文本。我想通过用“ mb_split”分割文本来搜索比较这些文本,然后循环浏览所有其他文本以比较该词是否存在。根据所做的比较,我想将商品编号写入该数据库中的另一个表。

以下代码可以正常工作,并且可以解决问题,但完成该过程会花费很长时间,并且会占用大量资源。我似乎找不到一种更有效地比较这些文本的方法。

\

我真正想知道的事;我可以提高效率吗?我可以使用更少的循环,还是有一种更简单的方法来搜索数组?如果我可以提高工作效率,有人可以按正确的方向推动我吗?

编辑: 了解我检索到的数据以及我要写入另一个表的数据可能会很有用:

将文本数据库设置为包含

function compareArticle() {
  include '../include/write.php';
  $readNewsQuery = "select title,text,articleid,name from texts";
  $readNews = $dbwrite->query($readNewsQuery);

  if ($readNews) {
    //Fetch mysql data as an array
    $news = $readNews->fetch_all(MYSQLI_NUM);
      // Start foreach to read every article once
      foreach ($news as $item) {
        echo $item[2].'<br />';
        // Start another foreach to loop through the articles to compare with
        foreach ($news as $compare) {
          $strippedWords = mb_split(' +', $item[0]);
          $count = 0;
          $compareString = "";
          $compareString .= $compare[0];
          $compareString .= $compare[1];
          $compareString = strtolower($compareString);
          // Start yet another foreach to loop through the words
          foreach ($strippedWords as $word) {
            // I only want to count the words that are longer than 4 characters
            if (strlen($word) > 4) {
              $woord = strtolower($word);
              if (strpos($compareString, $word) && $compare[2] != $item[2]) {
                $count++;
              }
            }
          }
          if ($count > 5) {
            echo $count.'<br />';
            //Insert action to write comparison to database (item[2] and compare[2])
          }
       }
    }
  }
}

我将标题中的单词与所有其他文章的标题和文本单词进行比较。如果它们足够匹配,我想将两个文章ID都写到另一个表中:

| article id | title | text | sourcename

2 个答案:

答案 0 :(得分:0)

循环浏览新闻条目后,您不再需要将其与其他新闻条目进行比较,例如,如果新闻条目1与其他50个新闻条目不匹配,那么当您开始检查新闻条目2时,您已经知道它与新闻项1不匹配。

因此,您无需在新闻条目中循环两次,而可以在第一个新闻文章循环的当前索引+1(无需将当前新闻条目与其自身进行比较)上开始第二个循环。

编辑:这是一个示例循环:

优化循环:

$matches = array();
$a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ];
$count = 0;
for ($i = 0; $i < count($a); ++$i) {
    for ($j = $i+1; $j < count($a); ++$j) {
        if ($a[$i] == $a[$j]) {
            array_push($matches, "$i, $j");
        }
        $count++; 
    }
}
echo "Optimized n loops: $count\n";
echo 'Matches: ' . count($matches);

// Output
// Optimized n loops: 435
// Matches: 5

未优化的循环

$matches = array();
$count = 0;
for ($i = 0; $i < count($a); ++$i) {
    for ($j = 0; $j < count($a); ++$j) {
        if ($a[$i] == $a[$j]) {
            array_push($matches, "$i, $j");
        }
        $count++; 
    }
}
$matches = array_unique($matches); // Dedupe
echo "Un-optimized n loops: $count\n";
echo 'Matches: ' . count($matches);

// Output
// Un-optimized n loops: 900
// Matches: 40

未优化的循环包含很多重复的匹配项(索引1匹配索引5,索引5匹配索引1)

答案 1 :(得分:0)

我已经执行了很多测试,并对脚本进行了一些更改,现在知道了最大的罪魁祸首。

原案:

  • 样本量为10.000;
  • 执行时间:超过600秒(达到最大执行时间)。

测试用例:

  • 完全精简的原始版本
  • 样本数量为1000;
  • 执行时间:24秒。

最大的不同是什么?

最大的区别是更改了以下行的位置:

origin

我将该行移至第一个循环,而不是第二个。这样,第一个循环中的标题仅每1000个项目分配一次,而不是每1000个项目分配1000次。我测量了时间差异:

  • 第二个循环中的mb_split:

总执行时间(以秒为单位):162.17704296112

  • 第一个循环中的mb_split:

总执行时间(以秒为单位):24.564566135406

这是一个惊人的巨大差异。我猜mb_split不是PHP要做的最简单的事情。将mb_split放到我的代码的错误部分会使脚本慢了近7倍:|

strtolower()

得出该结果之后,我很好奇可以更改其他文本修饰符的位置所带来的差异。因此,我采用了strtolower()并将其尽可能放入第一个循环中。

  • strtolower()在第二个循环中:

总执行时间(以秒为单位):44.315208911896

  • strtolower()在第一个循环中:

总执行时间(以秒为单位):37.129139900208

尽管这种差异要小得多,但仍是一个明显的差异。

其他可能原因

我不确定-因为我目前没有时间测试-是否完全正确,但是在测试一些情况时,我发现我的浏览器运行正常。当我告诉PHP向我的浏览器输出很多信息时,脚本感觉它们会运行更长的时间,并且浏览器也将在一段时间后停止显示信息。

如果情况到了,我还有剩余时间,我将测试这一理论,并尝试查看我的浏览器是否真的可以解决我的PHP脚本的持续时间问题。我似乎找不到逻辑上的理由来解释为什么它会影响我的PHP脚本的持续时间,因为我希望浏览器崩溃并且我的PHP脚本继续在服务器端正常工作...但这种想法使我不禁思索几次。

无论如何,这是新脚本

$strippedWords = mb_split(' +', $item[0]);