web-dev-qa-db-ja.com

重複する時間を見つけるためのアルゴリズム

ERPシステムはバックグラウンドジョブを実行するためのサブシステムです。送信、開始、終了時間のタイムスタンプを含む、テーブル内のジョブに関するさまざまなメタデータを追跡します。

日ごとに詳細なジョブシステムのパフォーマンスを示すレポートを作成しています。 1つのKPIは、同時に実行されるジョブの最大数です。私が現在使用しているアルゴリズムは次のとおりです。

dim cnt as integer    'number of overlapping jobs
dim max as integer    'maximum number of jobs running at one time

for each Job where 
         Job.SubmitTs = today

  'bJob is a 2nd instance of Job
  for each bJob where
           bJob.SubmitTs =  today and
           bJob.StartTs  <= Job.EndTs and
           bJob.EndTs    >= Job.EndTs
    cnt = cCnt + 1
  next

  if cnt > max then max = cnt
next

このアルゴリズムの問​​題は、すべてのループのために非常に遅いということです。これを実装するより速い方法があるかどうか疑問に思っていましたか?

編集 SQLクエリを使用してデータにアクセスできません。

7
briddums
  1. ジョブのすべての開始点と終了点を(時間内に)配列に含めます(これにより、2 * N要素​​が作成されます(1は開始、1は終了)。
  2. イベントのタイムスタンプで配列の順序を並べ替えます。
  3. 次に、次のように(2 * N)要素を反復します。

    for each element X 
    do 
      if(X.type == start)
        counter++
      else
        counter--
      ans=max(ans, counter);
    end
    

複雑さ:O(n.log(n))ソート済み構造の初期ビルド用+ O(n) forその要素の反復。

編集:配列が使用されることがGiorgioによって提案されていますが、これはより良いオプションです。 (元々は赤/黒のツリーを使用するという提案でしたが、タスクを削除する機能は必要ないようなので、順序を維持するのは簡単です)。

15
K.Steff

SQLはデータの並べ替え方法を認識しており、エンジンが可能な限り最も効率的なアルゴリズム(O(n log(n)))を使用している一方で、結果セットがRAM(配列がRAMに収まらない場合に失敗するK.Steffの回答とは異なります)Pythonでは:

_import sqlite3
connection = sqlite3.connect("database.db")
cursor = connection.cursor()
cursor.execute("SELECT isStart from (" +
               "  SELECT startTime AS time, 1 AS isStart FROM myTable " +
               "  UNION ALL " +
               "  SELECT endTime as time, -1 AS isStart FROM myTable " +
               "  ORDER BY time ASC, isStart ASC" +
               ")")
maxOverlap = 0
currentOverlap = 0
for (isStart,) in cursor:
    currentOverlap += isStart
    maxOverlap = max(maxOverlap, currentOverlap)
print maxOverlap
_

_isStart ASC_の順序が必要であるため、間隔_[10,15]_と_[15,23]_が重複しているとは見なされないことに注意してください。

さらなる改善のために、おそらく列startTimeおよびendTimeにインデックスがあるため、SQLエンジンはすでにソートされた2つのテーブルを効率的にマージし、O(n)操作(O(log(n))次に、各行の挿入時に要素がインデックスに追加されたときに発生しますが、とにかくすでに持っています)。 SQLエンジンが効率的なマージを行うのに十分スマートでない場合は、オンザフライで実行します(まだpythonで):

_import sqlite3
connection = sqlite3.connect("database.db")
cursorStart = connection.cursor()
cursorStart.execute("SELECT startTime FROM myTable ORDER BY startTime ASC")
cursorEnd = connection.cursor()
cursorEnd.execute("SELECT endTime FROM myTable ORDER BY endTime ASC")
maxOverlap = 0
currentOverlap = 0
currentStart = cursorStart.fetchone()
currentEnd = cursorEnd.fetchone()
while currentStart is not None and currentEnd is not None:
    if currentStart < currentEnd:
        currentOverlap += 1
        currentStart = cursorStart.fetchone()
    else:
        currentOverlap -= 1
        currentEnd = cursorEnd.fetchone()
    maxOverlap = max(maxOverlap, currentOverlap)
print maxOverlap
_
1