Как сделать сравнение иголки в стоге сена более эффективным - PullRequest
0 голосов
/ 18 декабря 2018

Я изо всех сил пытался сделать следующий фрагмент кода более эффективным.

Короче говоря;

У меня есть база данных с заголовками и описаниями.База данных составит в среднем 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])
          }
       }
    }
  }
}

Что бы я действительно хотел знать;Могу ли я быть более эффективным?Могу ли я использовать меньше циклов или есть более простой способ поиска в массиве?Если я могу быть более эффективным, кто-нибудь может подтолкнуть меня в правильном направлении?

РЕДАКТИРОВАТЬ: Может быть полезно знать, какие данные я получаю и что я хочу записать в другую таблицу:

База данных текстов настроена на включение

| article id | title | text | sourcename

Я сравниваю слова в названии со словами заголовка и текста, объединенными для всех других статей.Если они совпадают достаточно, я хочу записать оба идентификатора статьи в другую таблицу:

| id | original article id | compared article id |

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

Я выполнил множество тестов и внес несколько изменений в свой сценарий, и теперь знаю, какой был главный виновник.

Исходный случай:

  • Размер выборки 10.000;
  • Время выполнения: более 600 секунд (превышено максимальное время выполнения).

Контрольный пример:

  • Полностью урезанная версия оригинала
  • Размер выборки 1000;
  • Время выполнения: 24 секунды.

Что имело наибольшее значение?

Самым большим отличием было изменение местоположения следующей строки:

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

Я переместил эту строку в первый цикл вместо второго.Таким образом, заголовок из первого цикла делится только один раз каждые 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-скрипт продолжит работать на сервере ... но мысль пришла мне в голову:несколько раз.

в любом случае, вот новый сценарий

function compareArticle() {
  //For timing my script
  $time_start = microtime(true);

  include '../include/write.php';
  $readNewsQuery = "select title,text,articleid,name,datetoday from texts";
  $readNews = $dbwrite->query($readNewsQuery);
  $dateToday = date("Y-m-d");

  if ($readNews) {
    //Fetch mysql data as an array
    $news = $readNews->fetch_all(MYSQLI_NUM);
  }

  foreach ($news as $item) {
    // Decrease the sample pool
    if ($item[4] != $dateToday) {
      continue;
    }
    $strippedWords = strtolower($item[0]);
    $strippedWords = mb_split(' +', $strippedWords);

    // Start another foreach to loop through the articles to compare with
      foreach ($news as $compare) {

        $compareString = "";
        $compareString .= $compare[0];
        $compareString .= $compare[1];

        $count = 0;

        // 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) {

            if (strpos(strtolower($compareString), $word)) {
              $count++;
            }
          }
        }
0 голосов
/ 18 декабря 2018

Как только вы перебираете новость, вам больше не нужно сравнивать с ней другие новости, например, если новость 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)

...