Как я могу сравнить два набора из 1000 чисел друг с другом?
Я должен проверить примерно 1000 номеров против 1000 других номеров.
Я загрузил оба и сравнил их на стороне сервера:
foreach( $numbers1 as $n1 ) {
foreach( $numbers2 as $n2 ) {
if( $n1 == $n2 ) {
doBla();
}
}
}
Это заняло много времени, поэтому я попытался сделать то же самое сравнение на стороне клиента, используя два скрытых
div
элементы. Затем сравнил их с помощью JavaScript. Это по-прежнему занимает 45 секунд, чтобы загрузить страницу (с помощью hidden div
элементов).
мне не нужно загружать номера, которые не совпадают.
есть более быстрый алгоритм? Я думаю о том, чтобы сравнить их со стороны базы данных и просто загрузить номера ошибок, а затем выполнить вызов Ajax для оставшихся номеров без ошибок. Но достаточно ли быстра база данных MySQL?
26 ответов:
сначала отсортируйте списки. Затем вы можете идти вверх по обоим спискам с самого начала, сравнивая, как вы идете.
цикл будет выглядеть примерно так:
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.)
Edit - просто чтобы быть справедливым ко всем поклонникам hashtable (которых я уважаю, конечно), это довольно легко сделать в 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 преобразование в строку перед хэшированием удивительно дешево), это будет довольно быстро.
в терминах базы данных это может объединить 1000 строк с другими 1000 строк. Любая современная система баз данных может справиться с этим.
select x from table1 inner join table2 on table1.x = table2.y
здесь
table1
иtable2
являются ли соответствующие строки и могут быть одной и той же таблицей.
что у вас не должно занять так много времени - что делает doBla ()? Я подозреваю, что это занимает время? Сравнение двух наборов 1000000 чисел с одним и тем же алгоритмом не занимает много времени..
Это весело-количество методов оптимизации в качестве ответов-проблема не в вашем алгоритме - это то, что делает doBla (), что занимает время в разы больше, чем любая оптимизация поможет вам :) esp. учитывая, что наборы только 1000 длиной, и вы должны сортировать их в первую очередь..
может быть, просто пересечь значения массива, чтобы найти числа, существующие в обоих массивах?
$result = array_intersect($numbers1, $numbers2); foreach ($result as $val) doBla();
Если вы сначала отсортируете list2, а затем выполните двоичный поиск для каждого числа в list1, вы увидите огромное увеличение скорости.
Я не PHP парень, но это должно дать вам идею:
sort($numbers2); foreach($numbers1 as $n1) { if (BinarySearch($numbers2, $n1) >= 0) { doBla(); } }
очевидно, что я не парень PHP, я не знаю библиотеку, но я уверен, что сортировка и двоичный поиск должны быть достаточно просты для поиска.
Примечание: в случае, если вы не знакомы с двоичным поиском; вы сортируете list2, потому что двоичный поиск должен работать по отсортированным спискам.
Я не эксперт 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 чисел за секунду.
остановка- зачем ты это делаешь?
Если числа уже находятся в базе данных SQL, то выполните соединение и позвольте БД определить наиболее эффективный маршрут.
Если их нет в базе данных, то я держу пари, что вы где-то сбились с пути и действительно должны пересмотреть, как вы сюда попали.
Сортировать оба списка, а затем ходить оба списка одновременно с помощью старый мастер новый мастер последовательное обновление pattern. Пока вы можете сортировать данные, это самый быстрый способ, так как ваш действительно только ходит по списку один раз, до самой длинной длины самого большого списка.
ваш код просто сложнее, чем должно быть.
предполагая, что вы ищете, что числа в каждой позиции совпадают (а не только то, что массив содержит одни и те же числа), вы можете сгладить свой цикл до одного 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); ?>
может быть, я не вижу что-то здесь, но это выглядит как классический случай пересечения множества. Вот несколько строк в perl, которые сделают это.
foreach $e (@a, @b) { $union{$e}++ && $isect{$e}++ }
@union = ключи %union; @isect = ключи %isect;
в конце этих строк кода @isect будет содержать все числа, которые находятся как в @a, так и в @b. я уверен, что это можно перевести на php более или менее напрямую. FWIW, это мой любимый фрагмент кода из Поваренной книги Perl.
вы можете сделать это за O(n) время, если вы используете сортировку ведра. Предполагая, что вы знаете максимальное значение, которое могут принимать числа (хотя есть способы обойти это).
Я думаю, было бы гораздо проще использовать встроенную функцию array_intersect. Используя Ваш пример, вы могли бы сделать:
$results = array_intersect($numbers1, $numbers2); foreach($results as $rk => $rv) { doSomething($rv); }
лучше всего было бы сделать что-то вроде этого:
// 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).
Mergesort оба списка, начать в начале обоих списков, а затем искать по каждому списку для одинаковых номеров в то же время.
Так, в псевдокоде, это будет что-то вроде...
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).
Я не уверен, почему Mrk Mnl был понижен, но вызов функции накладные расходы здесь.
выталкиваем совпадающие числа в другой массив и добла () на них после сравнения. В качестве теста / / out doBla () и посмотреть, если вы испытываете ту же проблему производительности.
можно ли поместить эти числа в две таблицы базы данных, а затем сделать
INNER JOIN
? Это будет очень эффективно и обеспечит только те числа, которые содержатся в обеих таблицах. Это идеальная задача для базы данных.
создайте две повторяющиеся коллекции, предпочтительно с быстрым временем поиска, например HashSet или, возможно, TreeSet. Избегайте списков, поскольку они имеют очень плохое время поиска.
Как вы найдете элементы, удалите их из обоих наборов. Это может сократить время поиска, имея меньше элементов для просеивания в последующих поисках.
Если вы пытаетесь получить список чисел без дубликатов, вы можете использовать хэш:
$unique = array(); foreach ($list1 as $num) { $unique[$num] = $num; } foreach ($list2 as $num) { $unique[$num] = $num; } $unique = array_keys($unique);
Это будет немного (очень немного) медленнее, чем метод обхода массива, но он чище, на мой взгляд.
этот код будет вызывать
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()
- вероятно, принимая больше времени, чем итерации по массивам.
эта проблема может быть разбита на 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]); } }
слияние, сортировка и подсчет
<?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 )