web-dev-qa-db-ja.com

1000の数字の2つのセットを互いに比較するにはどうすればよいですか?

他の1000個の番号に対して約1000個の番号をチェックする必要があります。

両方をロードして、サーバー側で比較しました。

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

これには長い時間がかかったため、2つの隠されたdiv要素を使用して、同じ比較クライアント側を実行しようとしました。次に、JavaScriptを使用してそれらを比較しました。ページの読み込みにはまだ45秒かかります(非表示のdiv要素を使用)。

同じではない番号をロードする必要はありません。

より高速なアルゴリズムはありますか?私はそれらをデータベース側で比較し、エラー番号をロードしてから、残りの非エラー番号に対してAjax呼び出しを行うことを考えています。しかし、MySQLデータベースは十分高速ですか?

64
baklap

リストを最初にソートします。その後、最初から両方のリストを上に移動して、移動しながら比較できます。

ループは次のようになります。

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
Pointy
85
Phill Pafford

データベース用語では、これは1000行を別の1000行に結合できます。最新のデータベースシステムはこれを処理できます。

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

ここで、table1およびtable2は関係する行であり、同じテーブルである可能性があります。

27
Preet Sangha

そんなに長くはいけないこと-doBla()は何をするのでしょうか?私はそれが時間がかかっていると思う?同じアルゴリズムを使用して2つの1000000数のセットを比較するのに時間がかかりません。

これは陽気です-答えとしての最適化手法の数-問題はあなたのアルゴリズムではありません-それはdoBla()が何をするにしても、最適化があなたを助けるだろうよりも何倍も長い時間をかけていることです:)セットの長さが1000のみであるため、最初に並べ替える必要があります。

26
markmnl

たぶん、両方の配列に存在する数値を見つけるために配列値を交差させるだけですか?

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

最初にlist2をソートしてから、list1の各数値に対してバイナリ検索を実行すると、速度が大幅に向上します。

私はnot a PHP guy、しかしこれはあなたにアイデアを与えるはずです:

sort($numbers2);

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

明らかにPHP guyではありません。ライブラリはわかりませんが、ソートとバイナリ検索は簡単に見つけることができるはずです。

注:バイナリ検索に慣れていない場合。バイナリ検索はソートされたリストを操作する必要があるため、list2をソートしています。

9
Giovanni Galbo

停止-なぜこれを行うのですか?

番号がすでにSQLデータベースにある場合は、結合を実行して、DBに最も効率的なルートを見つけさせます。

それらがデータベースにない場合は、どこかで軌道に乗っていないので、実際にここに来た方法を再検討する必要があります。

5
dethSwatch

最初に並べ替えます。

5
JRL

私はPHPエキスパートではないので、これには多少のデバッグが必要かもしれませんが、O(n) time:

// 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)実装でさえ、1秒で1000個の数字を処理できるはずです。

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

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

両方のリストをソートし、 old-master new-master順次更新パターン を使用して両方のリストを同時にウォークします。データを並べ替えることができる限り、それはリストを一度だけ最大のリストの最長の長さまで歩くので、最速の方法です。

3
skamradt

コードは必要以上に複雑です。

探しているのは各位置の数字が一致している(配列に同じ数字が含まれているだけではない)と仮定すると、ループを単一の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>";

?>

上記のコードを使用すると、1000000回ループするのではなく、1000回だけループします。

ここで、配列に数値が表示されるかどうかを確認する必要がある場合は、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
Jack Shedd

バケットの並べ替えを使用する場合は、O(n)時間で行うことができます。数値が取ることができる最大値を知っていると仮定します(その方法はありますが)。

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

2
ashanan

ここには何も表示されていないかもしれませんが、これは集合交差の古典的なケースのように見えます。これを行うPerlの数行を次に示します。

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

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

これらのコード行の最後に、@ isectには@aと@bの両方にあるすべての数値が含まれます。私はこれが多かれ少なかれ直接PHPに翻訳できると確信しています。 FWIW、これはPerl Cookbookの私のお気に入りのコードです。

2
Shahbaz

Visual BasicでGUIインターフェイスを作成し、数値を追跡できるかどうかを確認します

1
edahs

より良い方法は、このようなことをすることです:

// 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()nを呼び出すためです。2 両方のリストのすべての数値が同じ場合の回数。ただし、両方のリストに一意の番号がある(つまり、一般的にリスト内で一意である)場合、ランタイムはO(n)に向かう傾向があります。

1
dhruvbird

組み込みのarray_intersect関数を使用する方がはるかに簡単だと思います。あなたの例を使用すると、次のことができます:

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

両方のリストをマージソートし、両方のリストの先頭から開始してから、各リストを検索して、同様の番号を同時に検索します。

したがって、擬似コードでは、次のようになります...

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
waffles

Mrk Mnlがダウン投票された理由はわかりませんが、関数呼び出しはオーバーヘッドですここです。

一致した数値を別の配列にプッシュし、それらに対してdoBla()after比較します。テストとして// doBla()を外し、同じパフォーマンスの問題が発生しているかどうかを確認します。

1
Martin Blank

マージ、ソート、そしてカウント

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

出力/カウント3の値が必要な値です

Array
(
    [1001] => 2
    [1002] => 3
    [1003] => 3
    [1004] => 3
    [1005] => 3
    [1006] => 1
)
0
jaymz

この問題は、2つのタスクに分けることができます。最初のタスクは、すべての組み合わせ(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
Gigamegs

このコードは、_$numbers1_の値が_$numbers2_で見つかるたびにdoBla()を1回呼び出します。

_// 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()を1回だけ呼び出す必要がある場合:

_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
gregjor

JavaScriptではなくWebAssemblyを使用します。

0
Hasan Savran
  1. HashSetまたはTreeSetのように、2つの重複するコレクション、できればルックアップ時間が速いコレクションを作成します。ルックアップ時間が非常に短いため、リストは避けてください。

  2. 要素を見つけたら、両方のセットから削除します。これにより、後の検索で選別する要素が少なくなり、検索時間が短縮されます。

0
Edwin Buck

重複のない数字のリストを取得しようとしている場合は、ハッシュを使用できます。

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

それは配列walkメソッドよりもわずかに(非常にわずかに)遅くなりますが、私の意見ではよりきれいです。

0
brianloveswords

これらの数値を2つのデータベーステーブルに入れてから、INNER JOIN?これは非常に効率的で、両方のテーブルに含まれる数値のみを提供します。これは、データベースに最適なタスクです。

0
m.edmondson