web-dev-qa-db-ja.com

依存関係のあるタスクをスケジュールするための最適化されたアルゴリズム?

ファイルから読み取り、処理を行い、ファイルに書き込むタスクがあります。これらのタスクは、依存関係に基づいてスケジュールされます。また、タスクは並列で実行できるため、依存するタスクを逐次および可能な限り並列で実行するようにアルゴリズムを最適化する必要があります。

例えば:

  1. A-> B
  2. A-> C
  3. B-> D
  4. E-> F

したがって、これを実行する1つの方法は、1、2、および4を並行して実行することです。続いて3。

別の方法では、1を実行してから、2、3、4を並行して実行できます。

もう1つは、1と3をシリアルで、2と4をパラレルで実行できます。

何か案は?

23
user2186138

各タスク(例:A,B,...)を 有向非循環グラフ のノードとし、1,2,...に基づいてノード間のアークを定義します。

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

次に、 トポロジカル順序 グラフを作成できます(または [〜#〜] bfs [〜#〜] などの検索ベースのメソッドを使用します)。あなたの例では、C<-A->B->DE->Fがそうなので、AEの深さは0であり、最初に実行する必要があります。次に、FBCを並行して実行し、続いてDを実行します。

また、 [〜#〜] pert [〜#〜] もご覧ください。

更新:

BFよりも優先度が高いかどうかをどのようにして知ることができますか?

これは、順序を見つけるために使用されるトポロジカルソートの直感です。

最初にルート(着信エッジなし)ノードが検出されます(DAGに存在する必要があるため)。あなたの場合、それはAEです。これにより、完了する必要のある最初のジョブが確定します。次に、ルートノードの子(BCおよびF)を終了する必要があります。これは、グラフをクエリすることで簡単に取得できます。次に、検出(終了)するノード(ジョブ)がなくなるまで、このプロセスを繰り返します。

13
Jacob

アイテムとそれらが依存するアイテムとの間のマッピングが与えられた場合、トポロジカルソートは、依存するアイテムの前にアイテムがないようにアイテムを並べ替えます。

このRosettaコードタスク には Pythonでのソリューション があり、これにより、並列処理できるアイテムを確認できます。

入力を与えると、コードは次のようになります。

try:
    from functools import reduce
except:
    pass

data = { # From: http://stackoverflow.com/questions/18314250/optimized-algorithm-to-schedule-tasks-with-dependency
    # This   <-   This  (Reverse of how shown in question)
    'B':         set(['A']),
    'C':         set(['A']),
    'D':         set(['B']),
    'F':         set(['E']),
    }

def toposort2(data):
    for k, v in data.items():
        v.discard(k) # Ignore self dependencies
    extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
    data.update({item:set() for item in extra_items_in_deps})
    while True:
        ordered = set(item for item,dep in data.items() if not dep)
        if not ordered:
            break
        yield ' '.join(sorted(ordered))
        data = {item: (dep - ordered) for item,dep in data.items()
                if item not in ordered}
    assert not data, "A cyclic dependency exists amongst %r" % data

print ('\n'.join( toposort2(data) ))

次に、この出力が生成されます。

A E
B C F
D

出力の1行の項目は、任意のサブオーダーで、または実際には並行して処理できます。依存関係を維持するために、上位の行のすべてのアイテムが後続の行のアイテムの前に処理される限り。

7
Paddy3118

タスクは、(うまくいけば)サイクルのない方向性のあるグラフです。

私はsourceswellsを含みます(ソースは依存しないタスク(インバウンドEdgeがない)、ウェルはタスクをロック解除しないタスク(アウトバウンドEdgeがない)です)。

簡単な解決策は、その有用性に基づいてタスクを優先することです(Uと呼びましょう)。

通常、ウェルから始めて、それらには有用性がありますU = 1、私たちが彼らに仕上げてほしいので。

現在評価されているノードのリストLにすべてのウェルの先行ノードを入れます。

次に、Lの各ノードを取得すると、そのU値は、ノードに依存するノードのU値の合計+ 1になります。現在のノードのすべての親を配置しますLリストにあります。

すべてのノードが処理されるまでループします。

次に、開始できるタスクで最大のU値を持つタスクを開始します。これは、最大数のタスクのロックを解除するタスクであるためです。

あなたの例では、

U(C) = U(D) = U(F) = 1
U(B) = U(E) = 2
U(A) = 4

つまり、可能であれば最初にAをEで始め、次にBとC(可能であれば)、次にDとFから始めます。

1
njzk2

まず、タスクのトポロジー的な順序を生成します。この段階でサイクルを確認します。その後、最大アンチチェーンを調べることで並列処理を利用できます。大まかに言えば、これらは要素間の依存関係のないタスクセットです。

理論的な観点から、 この論文 はこのトピックをカバーしています。

1
collapsar

問題のシリアル/パラレルの側面を考慮せずに、このコードは少なくともシリアルソリューション全体を決定できます。

def order_tasks(num_tasks, task_pair_list):
    task_deps= []
    #initialize the list
    for i in range(0, num_tasks):
        task_deps[i] = {}

    #store the dependencies
    for pair in task_pair_list:
        task = pair.task
        dep = pair.dependency

        task_deps[task].update({dep:1})

    #loop through list to determine order
    while(len(task_pair_list) > 0):
        delete_task = None

        #find a task with no dependencies
        for task in task_deps:
            if len(task_deps[task]) == 0:
                delete_task = task
                print task
                task_deps.pop(task)
                break

        if delete_task == None:
            return -1

        #check each task's hash of dependencies for delete_task
        for task in task_deps:
            if delete_key in task_deps[task]:
                del task_deps[task][delete_key]

    return 0

完全に満たされている依存関係をチェックするループを更新してリスト全体をループし、依存関係がなくなったタスクを同時に実行/削除すると、タスクの完了を活用できるようになります。平行。

0
flyerfye