web-dev-qa-db-ja.com

予約スケジューリングアルゴリズム(N人の空きビジースロットを持つN人、制約充足)

問題ステートメント

N人の面接を希望する雇用主が1人いるため、N面接枠を作成します。すべての人がこれらのスロットの空き時間スケジュールを持っています。可能であればN人をNスロットにスケジュールし、それが不可能な場合はフラグ/エラー/などを返すアルゴリズムを提供します。ランタイムの最速の複雑さはどれくらいですか?

これまでの私のアプローチ

ナイーブ:Nがあります! N人をスケジュールする方法。それらすべてを調べ、順列ごとに、それが可能かどうかを確認します。オン! )

バックトラック:

  1. 1人しか持てないインタビューの時間枠を探します。人をスケジュールし、候補者のリストから削除して、スロットを削除します。
  2. 1スロットにしか入れることができない候補を探します。人をスケジュールし、候補者のリストから削除して、スロットを削除します。
  3. そのような組み合わせがなくなるまで1と2を繰り返します。
  4. 人を選んで、利用可能なスロットの1つにランダムにスケジュールします。この操作を覚えておいてください。
  5. スケジュールがあるか、解決できない競合があるまで、1、2、3を繰り返します。スケジュールがある場合は返却してください。解決できない競合がある場合は、バックトラックします。

これはO(n!)の最悪のケースだと思います。

D.Pがあるかもしれません。解決策も-私はまだそれを見ていません。

その他の考え

この問題は、行が「スロット」、列が「人」であり、値が空きの場合は「1」、ビジーの場合は「0」であるNxNマトリックスで表すことができます。次に、この行列内で行と列が入れ替わった単位行列を探します。手順1と2は、「1」が1つだけの行または列を探しています。 (行列のランクが= Nの場合、つまり、解があることを意味します。ただし、その逆は成り立ちません)これを調べる別の方法は、重み付けされていない有向グラフエッジ行列として行列を扱うことです。次に、ノードはそれぞれ1つの候補と1つのスロットを表します。次に、グラフのすべてのノードに1つの出力エッジと1つの入力エッジがあるように、エッジのセットを探しています。または、2xノードの場合、2部グラフになります。

マトリックスの例:

1 1 1 1
0 1 1 0
1 0 0 1
1 0 0 1
23
DarthShader

あなたが指摘したように、問題は二部グラフで最大の一致を見つける問題と同等です(頂点のセットは人のセットであり、他のセットはスロットのセットです、人とスロットの間にエッジがあります)人がこのスロットで利用できる場合)。

この問題は Hopcroft-Karpアルゴリズム で解決できます。

最悪の場合の複雑度O(n ^(5/2))。グラフがスパースの場合に適しています。

12
Thomash

制約プログラミングのアプローチについては、たとえばマトリックスアプローチやセットベースのアプローチなど、さまざまな方法でモデル化できます。

セットベースのアプローチは、高水準CP言語 MiniZinc で以下に示されています。 sは、(セット表記を使用して)時間枠ごとに利用可能な人です。決定変数は配列xです(その人は毎回割り当てられるべきです)。

 include "globals.mzn"; 
 int:n = 4; 
 
%の時間スロットあたりの空き人数
 array [1..n] intのセット:s = 
 [{1,2,3,4}、
 {2,3}、
 {1,4}、
 {1,4}]; 
 
 
%決定変数
%スロットへの人物の割り当て(アポイントメント番号1..n)
 array [1..n] var 1 。 。] forall(i in 1..n)(
 x [i] in s [i] 
)
/\%各ユーザーが個別のタイムスロットを取得できるようにします
 alldifferent(x)
; 
 
 output [show(x)]; 

モデルはこれらの4つのソリューションを(0.5msで)出力します。時間1は人3に割り当てられます。

 x:[3、2、4、1] 
 ---------- 
 x:[2、3、4、1] 
 ---------- 
 x:[3、2、1、4] 
 ---------- 
 x: [2、3、1、4] 

MiniZincモデルは次のとおりです: appointment_scheduling_set.mzn

マトリックスアプローチモデルはここにあり(出力セクションなし)、標準の整数プログラミングアプローチを使用します: appointment_scheduling.mzn

 int:n = 4; 
 
%行はタイムスロットです
%列は人です
 array [1..n、1 .. intのn]:m = array2d(1..n、1..n、
 
 1、1、1、1、
 0、1、1、0 、
 1、0、0、1、
 1、0、0、1、
); 
 
%決定変数
%スロットへの人物の割り当て(アポイントメント番号1..n)
 var [1..n、1..n] of var 0..1:x; 
 
ソルブ満足; 
 
制約
 forall(i in 1..n)(
%空きスロットを確保
合計([m [i、j] * x [i、j] | j in 1..n])= 1 
 
/\%スロットごとと人ごとに1つの割り当てを保証します
 sum([x [i、j] | j in 1..n])= 1 
 /\
 sum([x [j、i] | j in 1..n ])= 1 
)
; 

このモデルからのソリューションは同じですが、別の(そしてより冗長な)方法で提示されます-そして、それが発生した場合-セットベースのアプローチと同じ順序で

スロット1:3 
スロット2:2 
スロット3:4 
スロット4:1 
 --------- -
スロット1:2 
スロット2:3 
スロット3:4 
スロット4:1 
 -------- -
スロット1:3 
スロット2:2 
スロット3:1 
スロット4:4 
 ------- --- 
スロット1:2 
スロット2:3 
スロット3:1 
スロット4:4 
12
hakank

これが 最大2部マッチング問題 です。

グラフの密度に応じて、最速の解決策はおそらく Hopcroft-Karpアルゴリズム であり、最大でO(N ^(5/2))時間で実行されますが、おそらくはるかに高速です。

3
Chris Okasaki

ネットワークフロー を使用できると思います。

  • 候補iには頂点_u_i_を、スロットjには頂点_v_j_を作成します。
  • ソースノードsを作成し、sから(指示された)エッジを容量1の各_u_i_に配置します。
  • シンクノードtを作成し、各_v_j_から容量1のtにエッジを配置します。
  • 候補者iがスロットjで面接できる場合は、_u_i_から_v_j_にエッジを配置します。
  • O(N)頂点とO(N^2)エッジがあります。最大可能なフローはNです。
  • Ford-Fulkersonアルゴリズム を使用して最大フローを見つけます。これはO(E*f)を使用します。ここで、Eはエッジの数、fはは最大フローの値なので、O(N^3)を取ります。
  • 最大フローがNの場合、スケジュールがあります。エッジ_(u_i,v_j)_にフロー1がある場合、候補者iをスロットjに面接します。それ以外の場合は、それは不可能だ。

積分フローの定理により、最大フローはエッジに整数フロー(0または1)を持つため、有効なスケジュールがあります。

2
sxu