3つの整数を見つけるためのブルートフォース検索以外の効率的なアルゴリズムはありますか?
うん。これをO(n 2)時間で解決できます!まず、問題P
を「目標値」の必要性を排除するわずかに異なる方法で同等に表現できることを考慮してください。
元の問題P
:整数の配列とターゲット値A
が与えられたn
場合S
、A
その合計から3タプルは存在しS
ますか?
修正された問題P'
:整数の配列A
が与えられたn
場合、A
その合計からゼロまでの3タプルは存在しますか?
問題のこのバージョンから移動できることに注意してください P'
P
各要素からS / 3を引くからA
、現在は目標値は必要ありません。
明らかに、考えられるすべての3タプルを単純にテストすると、O(n 3) -これがブルートフォースのベースラインです。より良いことは可能ですか?もう少しスマートな方法でタプルを選択するとどうなるでしょうか。
最初に、配列を並べ替えるのに少し時間を費やしますが、O(n log n)の初期ペナルティがかかります。次に、このアルゴリズムを実行します。
for (i in 1..n-2) {
j = i+1 // Start right after i.
k = n // Start at the end of the array.
while (k >= j) {
// We got a match! All done.
if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])
// We didn't match. Let's try to get a little closer:
// If the sum was too big, decrement k.
// If the sum was too small, increment j.
(A[i] + A[j] + A[k] > 0) ? k-- : j++
}
// When the while-loop finishes, j and k have passed each other and there's
// no more useful combinations that we can try with this i.
}
このアルゴリズムは、3つのポインタを置くことによって動作i
、j
およびk
配列内の様々なポイントで。i
最初から始まり、ゆっくりと最後まで進んでいきます。k
最後の要素を指します。j
どこi
から始まったかを示します。それぞれのインデックスの要素を繰り返し合計し、次のいずれかが発生するたびに試みます。
- 金額は正確です!答えを見つけました。
- 合計が小さすぎます。移動し
j
、次の最大数を選択するために、近い終わりまで。
- 合計が大きすぎます。移動し
k
、次の最小数を選択するために、近い先頭に。
それぞれについてi
、のポインタj
とは、k
徐々にお互いに近づくだろう。結局、彼らはお互いを通過し、その時点で私たちはそのために何もする必要はありません。i
同じ要素を異なる順序で合計する。その後、次を試してi
繰り返します。
最終的には、有用な可能性を使い果たすか、解決策を見つけるでしょう。外側のループをO(n)回実行し、内側のループをO(n)回実行しているため、これはO(n 2)であることがわかります。各整数をビットベクトルとして表現し、高速フーリエ変換を実行することにより、本当に奇妙な場合は、これを準二次的に行うことは可能ですが、それはこの回答の範囲を超えています。
注:これはインタビューの質問なので、ここで少し浮気しました。このアルゴリズムでは、同じ要素を複数回選択できます。つまり、(-1、-1、2)は(0、0、0)と同様に有効なソリューションです。また、タイトルが言及しているように、最も近い答えではなく、正確な答えのみを検索します。読者への演習として、個別の要素のみで機能させる方法(ただし、これは非常に単純な変更です)と正確な回答(これも単純な変更です)を説明します。