2

これが私の基本的な問題ですcurrentTime。たとえば、750 秒です。また、1000 ~ 2000 個のオブジェクトを含む配列もあり、それぞれに、、、startTimeおよびendTime属性があり_idます。が与えられた場合、その範囲内にあるとcurrentTimeを持つオブジェクトを見つける必要があります。たとえば、。startTimeendTimestartTime : 740endTime : 755

Javascriptでこれを行う最も効率的な方法は何ですか?

手始めに、私は単にこのようなことをしてきました:

var arrayLength = array.length; 
var x = 0;
while (x < arrayLength) {
 if (currentTime >= array[x].startTime && currentTime <= array[x].endTime) {
  // then I've found my object
 }
x++;
};

しかし、ループはここでは最良の選択肢ではないと思います。助言がありますか?

編集: わかりやすくするために、はandcurrentTime内に収まる必要がありますstartTimeendTime

私の解決策:私のデータの構造には、物事を少し単純化できる特定の利点があります。配列は既に startTime でソートされているため、示唆されているように、基本的なバイナリ検索を実行しました。この速度を完全にテストしたわけではありませんが、特に大規模な配列では、かなり高速であると思われます。

var binarySearch = function(array, currentTime) {

  var low = 0;
  var high = array.length - 1;
  var i; 

  while (low <= high) {
    i = Math.floor((low + high) / 2);

    if (array[i].startTime <= currentTime) {

      if (array[i].endTime >= currentTime ){
        // this is the one
        return array[i]._id; 

      } else {
        low = i + 1;
      }
    }

    else {
      high = i - 1;
    }
  } 

  return null;
}
4

6 に答える 6

5

この問題に対処する最善の方法は、検索関数を呼び出す必要がある回数によって異なります。

関数を数回だけ呼び出す場合は、m線形検索に進みます。この関数の呼び出しの全体的な複雑さは になりますO(mn)

関数を何度も呼び出す場合、つまりlog(n)度も呼び出す場合は、次のことを行う必要があります。

  • 配列を でソートO(nlogn)startTimeendTime等しい値を持つアイテムが複数ある場合はでソートしますstartTime
  • で要素の範囲を見つけるために二分探索を行いますstartTime <= x。これは、2 つのバイナリ検索を実行することを意味します。1 つstartは範囲の で、もう 1 つendは範囲の です。これはで行われますO(logn)
  • 内で線形検索を行い[start, end]ます。startTimesの順序では について何もわからないため、線形検索を行う必要がありますendTimesO(1)これはとの間のどこでもかまいません。これはO(n)、セグメントの分布と の値によって異なりますx

平均的なケース: O(nlogn)初期化とO(logn)各検索。

最悪の場合:多くの等しいセグメント、または共通の間隔を持つセグメントを含む配列で、この間隔で検索します。その場合O(nlogn)、初期化とO(n + logn) = O(n)検索のために行います。

于 2012-10-25T07:38:44.390 に答える
2

二分探索の問題のように聞こえます。

于 2012-10-25T07:22:07.863 に答える
2

検索配列が長寿命で比較的一定であると仮定すると、最初の反復はすべての配列要素を開始時間で並べ替えることになります (または、並べ替えたくない場合は、配列要素を指す並べ替えられた開始時間のインデックスを作成します)。 .

次に、開始が遅すぎるものを効率的に (バイナリ チョップで) 割り引くことができます。他のものを順番に検索すると、より高速になります。

さらに高速化するには、開始時間と終了時間に個別のソート済みインデックスを維持します次に、前述と同じ操作を行い、開始が遅すぎるものを破棄します。

次に、残りのものについては、終了時間インデックスを使用して、終了が早すぎるものを破棄します。残ったものが候補リストです。

ただし、これが実際に必要であることを確認してください。2,000 要素というのは大した量ではないように見えるので、現在のアプローチのタイミングを計り、実際に問題がある場合にのみ最適化を試みる必要があります。

于 2012-10-25T07:26:50.367 に答える
1

間隔ツリーは、合計n間隔がある場合、O(lg n) 時間 (平均と最悪の場合の両方) でそのようなクエリに応答できるようにするデータ構造です。データ構造を構築するための前処理時間は O(n lg n) です。スペースは O(n) です。拡張間隔ツリーの挿入時間と削除時間は O(lg n) です。m 間隔がポイントをカバーする場合、全間隔クエリに応答する時間は O(m + lg n) です。 ウィキペディアでは、いくつかの種類の間隔ツリーについて説明しています。たとえば、中心間隔ツリーは、各ノードが格納する 3 次ツリーです。

• 中心点
• 中心点の完全に左側にあるすべての間隔を
含む別のノードへのポインタ • 中心点の完全に右側にあるすべての間隔を含む別のノードへのポインタ
• 始点でソートされた中心点と重なるすべての間隔
• 終点でソートされた中心点に重なるすべての区間

間隔ツリーは、ポイントをカバーする 1 つの間隔を見つける平均的なクエリと最悪の場合のクエリの両方で O(lg n) の複雑さを持つことに注意してください。前の回答には、同じものに対する最悪の場合のクエリ パフォーマンスが O(n) あります。以前のいくつかの回答では、平均時間が O(lg n) であると主張されていました。しかし、それらのどれも証拠を提供していません。代わりに、彼らは単に平均パフォーマンスが O(lg n) であると断言しています。これらの以前の回答の主な機能は、開始時間のバイナリ検索を使用することです。次に、終了時間に線形検索を使用する人もいれば、二分検索を使用する人もいますが、後者の検索が終了する間隔のセットを明確にしません。彼らは O(lg n) 平均のパフォーマンスを持っていると主張していますが、それは単なる希望的観測です。Naive Approachという見出しの下のウィキペディアの記事で指摘されているように、

単純なアプローチは、2 つの並列ツリーを構築することです。一方は各間隔の開始点で並べ替えられ、もう一方は終了点で並べ替えられます。これにより、O(log n) 時間で各ツリーの半分を破棄できますが、結果をマージする必要があり、O(n) 時間がかかります。これにより、O(n + log n) = O(n) のクエリが得られますが、これはブルート フォースに勝るものはありません。

于 2012-10-25T17:37:46.683 に答える
1

与えられた情報からは、何が最善の解決策であるかを判断することはできません。配列がソートされていない場合、ループは単一クエリの最適な方法です。配列に沿った 1 回のスキャンには O(N) (N は配列の長さ) しかかかりませんが、それを並べ替えてからバイナリ検索を行うには O(N log(N) + log(N)) かかるため、この場合、さらに時間がかかります。

同じ大きな配列に対して多数の異なるクエリがある場合、分析は大きく異なります。同じ配列に約 N 個のクエリがある場合、各クエリは O(log(N)) かかるため、並べ替えによって実際にパフォーマンスが向上する可能性があります。したがって、N 個のクエリの場合、O(N log(N)) が必要になります (残りの log(N) は削除されます) が、並べ替えられていない検索でも O(N^2) が必要になりますが、これは明らかに大きくなります。並べ替えが影響を及ぼし始めるタイミングは、配列のサイズにも依存します。

アレイをかなり頻繁に更新する場合、状況もまた異なります。ソートされていない配列の更新は O(1) 償却で実行できますが、ソートされた配列の更新には O(N) かかります。そのため、かなり頻繁に更新する場合、並べ替えが問題になる可能性があります。

範囲クエリ用の非常に効率的なデータ構造もいくつかありますが、意味があるかどうかは実際の使用法に依存します。

于 2012-10-25T07:38:28.777 に答える
1

配列がソートされていない場合は、あなたの方法が正しいです。

最初に配列をソートしてから検索を適用するという考えに陥らないでください。

試したコードでは、 n が要素の数である O(n) の複雑さがあります

最初に配列を並べ替えると、最初にO(n log(n))の複雑さに陥ります (並べ替えアルゴリズムと比較して) average case

次に、平均複雑度O(log_ 2(n) - 1)で実行される二分探索を適用する必要があります。

したがって、平均的なケースでは、次のように支出することになります。

O(n log(n) + (log_2(n) - 1))

単なるO(n)の代わりに。

于 2012-10-25T07:40:50.767 に答える