Как я могу сравнить два набора из 1000 чисел друг с другом?


Я должен проверить примерно 1000 номеров по сравнению с 1000 другими номерами.

Я загрузил оба и сравнил их на стороне сервера:

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

Это заняло много времени, поэтому я попытался сделать то же самое сравнение на стороне клиента, используя два скрытых div элементы. Затем сравнил их с помощью JavaScript. Загрузка страницы по-прежнему занимает 45 секунд (с использованием скрытых элементов div).

Мне не нужно загружать номера, которые не совпадают.

Существует ли более быстрый алгоритм? Я думаю о сравнивая их со стороны базы данных и просто загружая номера ошибок, затем выполните вызов Ajax для оставшихся номеров без ошибок. Но достаточно ли быстра база данных MySQL?

Author: Saeed Amiri, 2010-10-15

26 answers

Сначала отсортируйте списки. Затем вы можете просмотреть оба списка с самого начала, сравнивая по ходу.

Цикл будет выглядеть примерно так:

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 преобразование в строку перед хешированием на удивление дешево), это было бы довольно быстро.

 129
Author: Pointy, 2011-05-22 00:30:30

В терминах базы данных это может быть соединение 1000 строк с еще 1000 строками. С этим может справиться любая современная система баз данных.

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

Где table1 и table2 являются соответствующими строками и могут быть одной и той же таблицей.

 27
Author: Preet Sangha, 2011-05-22 00:31:32

То, что у вас есть, не должно занять так много времени - что делает doBla()? Я подозреваю, что это отнимает время? Сравнение двух наборов из 1000000 чисел с одним и тем же алгоритмом не занимает никакого времени.

Это забавно - количество методов оптимизации в качестве ответов - проблема не в вашем алгоритме - это то, что делает doBla(), что отнимает время во много раз больше, чем вам помогла бы любая оптимизация:) особенно. учитывая, что наборы имеют длину всего 1000, и вам нужно сортировать они первые..

 26
Author: markmnl, 2010-11-10 07:31:23

Может быть, просто пересечь значения массива, чтобы найти числа, существующие в обоих массивах?

$result = array_intersect($numbers1, $numbers2);
foreach ($result as $val)
  doBla();
 23
Author: sod, 2010-10-15 13:21:08

Если вы сначала отсортируете список 2, а затем выполните двоичный поиск для каждого числа в списке 1, вы увидите огромное увеличение скорости.

Я не PHP-парень, но это должно дать вам представление:

sort($numbers2);

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

Очевидно, что я не специалист по PHP, я не знаю библиотеку, но я уверен, что сортировку и двоичный поиск должно быть достаточно легко найти.

Примечание: На случай, если вы не знакомы с двоичным поиском; вы сортируете список 2, потому что двоичный поиск должен работать с отсортированным списки.

 9
Author: Giovanni Galbo, 2010-10-18 18:28:30

Сначала отсортируйте их.

 5
Author: JRL, 2010-10-15 13:18:15

Я не эксперт по 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 чисел за секунду.

 5
Author: munificent, 2010-10-15 17:36:02

Остановись - зачем ты это делаешь?

Если числа уже находятся в базе данных SQL, то выполните объединение и позвольте БД определить наиболее эффективный маршрут.

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

 5
Author: dethSwatch, 2010-10-18 16:07:13
$same_numbers = array_intersect($numbers1, $$numbers2);

foreach($same_numbers as $n)
{
  doBla();
}
 4
Author: Cesar, 2010-10-15 15:04:36

Отсортируйте оба списка, затем просмотрите оба списка одновременно, используя шаблон последовательного обновления старого мастера нового мастера. Пока вы можете сортировать данные, это самый быстрый способ, так как вы действительно проходите по списку только один раз, до самой длинной части самого большого списка.

 3
Author: skamradt, 2010-10-15 16:56:58

Ваш код просто сложнее, чем должен быть.

Предполагая, что вы ищете, чтобы числа в каждой позиции совпадали (а не только то, что массив содержит одинаковые числа), вы можете свести свой цикл к одному для.

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

?>
 2
Author: Jack Shedd, 2010-10-15 14:35:45

Может быть, я чего-то здесь не вижу, но это похоже на классический случай пересечения множеств. Вот несколько строк на perl, которые сделают это.

Для каждого $e (@a, @b) {$объединение{$e}++&&$исект {$e}++}

@объединение = ключи %объединение; @isect = ключи %isect;

В конце этих строк кода @isect будет содержать все числа, которые находятся как в @a, так и в @b. Я уверен, что это можно перевести на php более или менее напрямую. ФУ, это мой любимый фрагмент кода из поваренная книга Perl.

 2
Author: Shahbaz, 2010-10-15 15:57:23

Вы можете сделать это за O(n) времени, если используете сортировку по корзинам. Предполагая, что вы знаете максимальное значение, которое могут принимать числа (хотя есть способы обойти это).

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

 2
Author: ashanan, 2010-10-15 19:46:09

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

$results = array_intersect($numbers1, $numbers2);
foreach($results as $rk => $rv) {
    doSomething($rv);
}
 1
Author: David, 2010-10-15 16:13:28

Лучшим способом было бы сделать что-то вроде этого:

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

 1
Author: dhruvbird, 2010-10-15 17:47:56

Я создам графический интерфейс на Visual Basic, посмотрим, смогу ли я отслеживать цифры

 1
Author: edahs, 2010-10-15 21:22:06

Объединяет оба списка, начинает с начала обоих списков, а затем одновременно выполняет поиск по каждому списку похожих номеров.

Итак, в псевдокоде это будет выглядеть примерно так...

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

 1
Author: waffles, 2010-10-15 22:49:45

Я не уверен, почему Mrk Mnl был отклонен, но вызов функции - это накладные расходы здесь.

Вытолкните совпадающие числа в другой массив и доБла() на них после сравнения. В качестве теста //out doBla() и посмотрите, испытываете ли вы ту же проблему с производительностью.

 1
Author: Martin Blank, 2010-10-15 23:02:37

Можно ли поместить эти числа в две таблицы базы данных, а затем выполнить INNER JOIN? Это будет очень эффективно и даст только те цифры, которые содержатся в обеих таблицах. Это идеальная задача для базы данных.

 0
Author: m.edmondson, 2010-10-15 13:20:08
  1. Создайте две повторяющиеся коллекции, предпочтительно с быстрым временем поиска, например, HashSet или, возможно, TreeSet. Избегайте списков, так как у них очень плохое время поиска.

  2. Как только вы найдете элементы, удалите их из обоих наборов. Это может сократить время поиска за счет меньшего количества элементов для просеивания в последующих поисках.

 0
Author: Edwin Buck, 2010-10-15 19:51:20

Если вы пытаетесь получить список номеров без каких-либо дубликатов, вы можете использовать хэш:

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

Это будет немного (очень немного) медленнее, чем метод обхода массива, но, на мой взгляд, он чище.

 0
Author: brianloveswords, 2010-10-15 19:53:23

Этот код будет вызывать 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(), вероятно, занимают больше времени, чем повторение массивы.

 0
Author: gregjor, 2010-10-16 09:49:20

Эту задачу можно разбить на 2 задачи. 1-я задача - найти все комбинации (n^2-n)/2. Для n=1000 решение равно x=499500. 2-я задача состоит в том, чтобы перебрать все числа 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]);
  }
 } 
 0
Author: Bytemain, 2011-03-14 08:51:36

Объединение, сортировка и затем подсчет

<?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
)
 0
Author: jaymz, 2011-05-22 00:33:51

Используйте WebAssembly вместо JavaScript.

 0
Author: Hasan Savran, 2018-02-28 12:48:42