18

与えられたパズルのすべての解を見つける、マルチスレッドの数独ソルバーを作成するという宿題があります。私は以前、非常に高速なシングル スレッドのバックトラッキング数独ソルバーを作成したことがあるので、数独を解く面での助けは必要ありません。

私の問題はおそらく同時実行性を実際に理解していないことに関連していますが、この問題がマルチスレッドからどのように恩恵を受けるかわかりません。パズルの複数のコピーを維持せずに、同じ問題に対して同時に異なる解決策を見つける方法がわかりません。この仮定を考えると (それが間違っていることを証明してください)、マルチスレッド ソリューションがシングル スレッドよりも効率的であるとは思えません。

アルゴリズムの最初の提案を誰かに教えていただければ幸いです(コードなしでお願いします...)


言い忘れていましたが、使用するスレッドの数はプログラムの引数として指定されているので、私が知る限り、パズルの状態とはまったく関係がありません...

また、一意の解決策がない場合もあります。有効な入力は完全に空のボードである可能性があります。それらの1つを報告min(1000, number of solutions)して表示する必要があります(存在する場合)

4

14 に答える 14

21

とてもシンプルです。基本的な概念は、バックトラッキング ソリューションでは、選択肢があったときに分岐するというものです。1 つのブランチを試し、バックトラックしてから、別の選択肢を試しました。

次に、選択肢ごとにスレッドを生成し、両方を同時に試します。システムにすでにあるスレッド数 (入力引数) より少ない場合にのみ、新しいスレッドを生成します。それ以外の場合は、単純な (つまり、既存の) シングルスレッド ソリューションを使用します。効率を高めるには、これらのワーカー スレッドをスレッド プールから取得します。

これは多くの点で分割統治法です。検索スペースを半分に分割し、各スレッドに半分を割り当てる機会として選択肢を使用しています。ほとんどの場合、一方が他方よりも難しいということは、スレッドの寿命が変化することを意味しますが、それが最適化を興味深いものにしています。

明らかな同期化の問題を処理する簡単な方法は、現在のボードの状態をコピーして関数の各インスタンスに渡すことです。これは関数の引数です。このコピーは、共有された並行性について心配する必要がないことを意味します。シングル スレッド ソリューションでグローバル変数またはメンバー変数を使用してボードの状態を保存する場合、スタック上 (簡単) またはスレッドごと (難しい) にこのコピーが必要になります。関数が返す必要があるのは、ボードの状態と、それに到達するまでの移動回数だけです。

複数のスレッドを呼び出して作業を行う各ルーチンは、n 個の作業がある場合に n-1 個のスレッドを呼び出し、n 番目の作業を実行してから、他のすべてのスレッドが終了するまで同期オブジェクトで待機する必要があります。次に、それらの結果を評価します - n ボードの状態があり、移動数が最も少ないものを返します。

于 2009-05-12T02:47:47.993 に答える
9

マルチスレッドは、1 つのスレッドがリソースを待機する必要があり、その間に別のスレッドを実行できる場合に役立ちます。これには、別のスレッドが CPU 作業を続行している間に、I/O 要求またはデータベース アクセスを待機しているスレッドが含まれます。

マルチスレッドは、個々のスレッドを異なる CPU (またはコア) にファームアウトして、完全に同時に実行できる場合にも役立ちますが、通常はデータを共有する必要があるため、まだ競合が発生します。

マルチスレッドの数独ソルバーがシングルスレッドの数独ソルバーよりも効率的である理由がわかりません。単にリソースを待機する必要がないからです。すべてがメモリ内で行われます。

しかし、大学でやった宿題のいくつかを覚えていますが、それも同様に役に立ちませんでした (30 度で 1 マイル、次に 15 度でさらに 1 マイル掘ったときのトンネルの深さを確認する Fortran コード - はい、私はきれいです年 :-)。重要なのは、それが有用であることではなく、それができることを示すことです。

アルゴリズムに進みます。

私は、基本的に各パスで一連のルールを実行して、別の正方形を作成しようとするシングル スレッド ソルバーを作成しました。サンプル ルールは次のとおりです。行 1 に空いているマスが 1 つしかない場合、その数字は行 1 の他のすべての数字から明らかです。

すべての行、すべての列、すべての 3x3 ミニグリッドに同様のルールがありました。行/列の交差をチェックするルールもありました (たとえば、特定の正方形が行のために 3 または 4 しか含まず、列のために 4 または 7 しか含まない場合、それは 4 でした)。ここでは詳しく説明しませんが、基本的には手動で解決するのと同じ方法です。

実装に同様のルールがあると思われます(ブルートフォース以外に解決する方法は考えられないため、ブルートフォースを使用した場合は希望がありません:-)。

私が提案するのは、各ルールをスレッドに割り当てて、グリッドを共有させることです。各スレッドは独自のルールを実行し、そのルールのみを実行します。

アップデート:

ジョン、あなたの編集に基づいて:

[編集] 言い忘れていましたが、使用するスレッドの数はプログラムの引数として指定されているため、私が知る限り、それはパズルの状態とはまったく関係がありません...

また、一意の解決策がない場合もあります。有効な入力は完全に空のボードである可能性があります。min(1000, 解の数) を報告し、そのうちの 1 つを表示する必要があります (存在する場合)。

先生は、ルールに基づいて分割するのではなく、分岐点 (複数のルールが適用される可能性がある場所) に基づいて分割することを望んでいるようです。

つまり、ソリューションの任意の時点で、2 つ以上の可能な前進がある場合、それぞれの可能性を個別のスレッドに割り当てる必要があります (効率のためにルールを使用しつつ、同時に各可能性をチェックします)。これにより、ボードの競合が発生しないため、同時実行性が向上します (スレッドを個別の CPU/コアで実行できると仮定します)。各スレッドは独自のコピーを取得します。

さらに、スレッドの数を制限しているため、これを実現するにはスレッド プールの魔法を使用する必要があります。

私がお勧めするのは、作業キューと N スレッドを用意することです。メイン スレッドがすべてのワーカー スレッドを開始するとき、ワーク キューは最初は空です。次に、メイン スレッドはパズルの開始状態をワーク キューに入れます。

ワーカー スレッドは、状態がワーク キューに置かれるのを待つだけで、そのうちの 1 つが処理のためにそれを取得します。作業スレッドは、1 つの小さな変更を加えたシングルスレッド ソルバーです。前進する可能性が X 個ある場合 (X > 1)、ワーカーはそれらのうち X-1 個を作業キューに戻し、他の可能性の処理を続行します。

ですから、解決策は 1 つしかないとしましょう (本当の数独 :-)。最初のワーカー スレッドは、フォークを見つけることなく解決策を徐々に減らしていきます。これは、現在の状況とまったく同じです。

しかし、移動 27 で 2 つの可能性 (たとえば、3 または 4 が左上のセルに入る可能性がある) がある場合、スレッドは最初の可能性 (そのセルに 3 を入れる) で別のボードを作成し、それを作業キューに配置します。次に、4 を独自のコピーに入れて続行します。

別のスレッドは、そのセルに 3 があるボードを取り上げて続行します。そうすれば、2 つの可能性を同時に処理する 2 つのスレッドを実行できます。

いずれかのスレッドがそのボードが解決できないと判断すると、そのボードを破棄し、作業キューに戻ってさらに作業を行います。

いずれかのスレッドがそのボードが解決されたと判断すると、それを保存できるメインスレッドに通知し、以前の解決策を上書きするか (最初に見つかった解決策が解決策)、すでに解決策がある場合は破棄します (最後に見つかった解決策が解決策です)。その後、ワーカー スレッドはさらに作業を行うために作業キューに戻ります。いずれの場合も、メイン スレッドは見つかったソリューションの数を増やす必要があります。

すべてのスレッドがアイドル状態でワーク キューが空の場合、main には解決策があるか、ないかのどちらかです。また、ソリューションの数も含まれます。

ワーカーとメイン スレッド間のすべての通信はミューテックスする必要があることに注意してください (質問の情報に基づいてこれを知っていると仮定しています)。

于 2009-05-12T02:32:02.153 に答える
5

マルチスレッドの背後にある考え方は、複数の CPU を持つことを利用して、複数の計算を同時に実行できるようにすることです。もちろん、各スレッドには独自のメモリが必要ですが、通常は問題になりません。

ほとんどの場合、可能な解の状態をいくつかのサブスペースに分割し、それらを可能な限り独立させ (スレッド作成のオーバーヘッドであまりにも多くのリソースを浪費することを避けるため)、アルゴリズムを「適合」させます (実際に利益を得るために)。複数のスレッドを持つことから)。

于 2009-05-12T02:28:42.377 に答える
4

これは貪欲な力ずくのシングル スレッド ソルバーです。

  1. 次の空のセルを選択します。空きマスがなくなったら勝利!
  2. 可能なセル値 = 1
  3. 無効な部分解 (行、列、または 3x3 ブロックでの重複) を確認します。
  4. 部分解が無効な場合は、セルの値を増やして手順 3 に戻ります。そうでない場合は、手順 1 に進みます。

上記の概要を見ると、ステップ 2 と 3 の組み合わせは明らかにマルチスレッド化の候補です。より野心的なソリューションには、スレッド プールに送信されるタスクを生成する再帰的な探索の作成が含まれます。

この点に対応するための編集:「パズルの複数のコピーを維持せずに、同じ問題に対して同時に異なる解決策を見つける方法がわかりません。」

できません。それが要点です。ただし、具体的な 9 スレッドの例では、利点がより明確になる場合があります。

  1. 例の問題から始めます。
  2. 最初の空のセルを見つけます。
  3. 9 つのスレッドを作成します。各スレッドには、空のセルの候補値として独自のインデックスを持つ問題の独自のコピーがあります。
  4. 各スレッド内で、この問題のスレッド ローカルに変更されたコピーに対して元のシングル スレッド アルゴリズムを実行します。
  5. スレッドの 1 つが答えを見つけたら、他のすべてのスレッドを停止します。

ご想像のとおり、各スレッドが実行する問題領域はわずかに小さくなり、各スレッドは独自の CPU コアで実行される可能性があります。シングルスレッドのアルゴリズムだけでは、マルチコア マシンの利点を享受することはできません。

于 2009-05-12T02:37:10.883 に答える
2

与えられたパズルのすべての解決策と言うとき、パズルの最終的な唯一の解決策を意味しますか? それとも、1 つのソリューションに到達するためのさまざまな方法ですか? 私は、定義上、数独パズルの解は 1 つしかないことを理解していました...

前者の場合、Pax のルール ベースのアプローチ、既存のバックトラッキング アルゴリズムをマルチスレッド化する Tom Leys のアプローチが適しているかもしれません。

後者の場合、パズルの各段階で可能な動きごとに (パズルの独自のコピーを使用して) 新しいスレッドを起動する、ある種の分岐アルゴリズムを実装できます。

于 2009-05-12T02:23:46.537 に答える
2

マルチスレッドの恩恵を受ける必要がありますか、それともマルチスレッドを利用して課題を学習できるようにする必要がありますか?

ブルート フォース アルゴリズムを使用する場合、複数のスレッドに分割するのはかなり簡単です。割り当てがスレッドのコーディングに集中している場合は、許容できる解決策になる可能性があります。

于 2009-05-12T02:25:19.857 に答える
1

いくつかの一般的なポイント: 1) 問題を分割するのが簡単でない限り、プロセスを並行して実行しません。2) そうすることで利益が得られることがわかっています。スレッド間で変更可能な値を共有することは完全に避けます-または最小限に抑えます。一部の人々は、ミューテックスを安全に使用できるほど賢いです。私は違います。

自然な分岐または大きな作業単位を作成するアルゴリズム内のポイントを見つける必要があります。作業するユニットを特定したら、それをキューにドロップして、スレッドが取得できるようにします。些細な例として。アップグレードする 10 個のデータベース。10 台のサーバーすべてでアップグレードの非同期を開始します。すべてが完了するまで待ちます。スレッド/プロセス間で状態を共有することを簡単に回避でき、結果を簡単に集計できます。

数独で頭に浮かぶのは、効率的な数独ソリューションは、特定の深さを決して超えない 2 ~ 3 (またはそれ以上) の戦略を組み合わせる必要があるということです。私が数独をやっていると、どの時点でも、さまざまなアルゴリズムが最小の作業で解決策を提供することが明らかです。ほんの一握りの戦略を実行し、限られた深さまで調査させ、報告を待つだけです。すすぎ、繰り返します。これにより、ソリューションの「ブルート フォース」が回避されます。各アルゴリズムには独自のデータ空間がありますが、答えを組み合わせます。

Sciam.com には 1、2 年ほど前にこの記事が掲載されていましたが、公開されていないようです。

于 2009-05-12T02:33:55.707 に答える
1

シングル スレッド ソルバーのコーディング方法によっては、ロジックを再利用できる場合があります。マルチスレッド ソルバーをコーディングして、パズルを解くための一連の異なる戦略を使用して各スレッドを開始できます。

これらのさまざまな戦略を使用すると、マルチスレッド ソルバーは、シングル スレッド ソルバーよりも短い時間でソリューションのセット全体を見つけることができます (ただし、真の Sudoku パズルにはソリューションが 1 つしかないことに注意してください...クラスであのひどいゲームに対処しなければならなかった)

于 2009-05-12T02:24:22.353 に答える
1

問題を解決するためにバック トラッキングを使用したとおっしゃいました。できることは、検索スペースを 2 つに分割し、各スペースをスレッドに処理することです。各スレッドは、最後のノードに到達するまで同じことを行います。www2.cs.uregina.ca/~hmer200a で見つけることができるこれに対する解決策を実行しましたが、シングル スレッドを使用していますが、検索スペースを分割するメカニズムはブランチ アンド バウンドを使用しています。

于 2009-05-12T02:45:26.767 に答える
0

ちょっとしたメモ。私は実際に最適化された数独ソルバーを実装し、マルチスレッドを調べましたが、2つのことが私を止めました。

まず、スレッドを開始する単純なオーバーヘッドは0.5ミリ秒かかりましたが、全体の解決には1〜3ミリ秒かかりました(Javaを使用しましたが、他の言語や環境では異なる結果が得られる場合があります)。

第二に、ほとんどの問題はバックトラックを必要としません。そして、そうする人は、すべてのゲームルールが使い果たされて、仮説を立てる必要がある場合にのみ、問題の解決の後半にそれを必要とします。

于 2010-08-09T22:33:54.933 に答える
0

ここでとても楽しいアイデアがあります.. アクター モデルでそれをやってみましょう! 私はアーランを使用すると思います..どのように?元のボードから始めて、..

  • 1) 最初に空のセルで番号の異なる 9 人の子供を作成し、その後自殺します。
  • 2) 各子供は無効かどうかを確認し、無効な場合は自殺し、そうでない場合は
    • 空のセルがある場合は 1 へ)
    • 完了した場合、このアクターはソリューションです

明らかに、生き残っているすべてのアクターが問題の解決策です =)

于 2009-10-28T22:21:48.987 に答える
0

数年前、私が数独を解くことを見たとき、最適な解決策は論理分析アルゴリズムの組み合わせを使用し、必要な場合にのみ力ずくで頼るように思えました。これにより、ソルバーはソリューションを非常に迅速に見つけることができ、ボードを使用して新しいパズルを生成したい場合は、ボードを難易度でランク付けすることもできました。このアプローチを採用した場合、スレッドを実際に連携させるのは難しいかもしれませんが、ある程度の並行性を確実に導入できます。

于 2009-05-12T02:25:11.370 に答える
0

これが私自身のペニーです。それが役に立てば幸い。

プロセッサ間/スレッド間通信はコストがかかることに注意してください。必要がない限り、マルチスレッドにしないでください。他のスレッドで実行する作業/計算があまりない場合は、シングル スレッドを使用することもできます。

スレッド間でデータを共有することをできる限り回避してください。必要な場合にのみ使用してください

可能な限りSIMD 拡張機能を利用してください。ベクトル エクステンションを使用すると、複数のデータに対して一度に計算を実行できます。それはあなたを大いに助けることができます。

于 2014-05-03T00:31:35.983 に答える