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

Я должен проверить приблизительно 1000 чисел против 1000 других чисел.

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

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

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

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

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

Ответ 1

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

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

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

Ответ 3

В терминах базы данных это может быть объединение 1000 строк в другие 1000 строк. Любая современная система баз данных может справиться с этим.

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

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

sort($numbers2);

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

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

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

Ответ 12

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

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

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

?>

Ответ 13

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

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

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

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

Ответ 14

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

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

Ответ 15

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

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

Ответ 16

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

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

Ответ 17

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

Ответ 18

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

Итак, в псевдокоде это будет похоже на...

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

Ответ 19

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

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

Ответ 20

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

Ответ 21

  • Создайте две повторяющиеся коллекции, предпочтительно с быстрым временем поиска, например, HashSet или, возможно, TreeSet. Избегайте списков, поскольку они имеют очень плохое время поиска.

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

Ответ 22

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

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

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

Ответ 23

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

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

Ответ 24

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

Ответ 25

Эта проблема может быть разбита на 2 задачи. 1-я задача - найти все комбинации (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]);
  }
 }