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