まず、問題を少し単純化できることに注意してください。「各バケツには 15 ~ 60 個のジャガイモを入れる必要があり、バケツの合計は 100 にする必要があります」の代わりに、「これらの数字の合計が 100 になるように、0 ~ 45 (60 - 15) の間の n 個の数字が必要です。 n×15インチ。つまり、すべてのバケツに 15 個のジャガイモを入れて、残りのジャガイモを使用してバケツの残りのスペースを埋める方法を決定しようとしています。
n 個のバケツがあり、p 個のジャガイモが残っているとします。0 から 45 までの乱数 r を生成すると、p - r ポテトが残ります。残りの (n-1) 個のバケットを埋めることができますか?
- (p - r) < 0 の場合、残りのバケットを埋めることができないのは明らかです。
- (p - r) > 45 * (n - 1) の場合、これは実現不可能です。ジャガイモが多すぎて、バケツの容量を超えなければなりません。
- それ以外の場合は、現在のバケツに r のジャガイモを入れても、次のバケツの解を見つけることができることがわかっています。
これを再帰的な解決策として書くことができます: 連続するバケットごとに、次のステップで解決策を見つけることが妨げられなくなるまで、ランダムな数のジャガイモを生成します。
以下は非常に粗雑な F# の実装であり、明らかに改良/最適化できますが、アルゴリズムの要点を示しています。allocation は、バケットにすでに割り当てられている数量を含む int のリストです。qty は割り当てられるジャガイモの残りの数量です。buckets は残りのバケットの数で、min、max はバケットの容量です。たとえば、ジャガイモ 100 個とバケツ 3 個の場合、ソルブ 100 3 15 60 を呼び出します。
結果として得られる割り当ては「可能な限りランダム」にする必要があります。つまり、可能な割り当てを同じ確率で生成する必要がありますが、1 つの注意点があります。バケットの順序は問題ではないと仮定しました。アルゴリズムがこれまでに割り当てられたものに基づいて残りのジャガイモを割り当てるとすれば、パスに応じて、リストの「先頭」が制限され、割り当てによって最初のバケットにより多くが割り当てられる可能性があります。「真の」ランダム割り当てが必要な場合は、割り当てが完了したらバケットをシャッフルする必要があります。
お役に立てれば!
let rng = new System.Random()
let rec allocate allocation qty buckets min max =
match buckets with
| 0 -> allocation
| 1 -> qty :: allocation
| _ ->
let candidate = rng.Next(min, max + 1) // try a number in [min,max]
let rest = qty - candidate
if rest < (buckets - 1) * min // not enough left
then allocate allocation qty buckets min max // try again
else if rest > (buckets - 1) * max // too much left
then allocate allocation qty buckets min max // try again
else allocate (candidate :: allocation) rest (buckets - 1) min max
let solve qty buckets min max =
if min * buckets > qty then failwith "unfeasible"
if max * buckets < qty then failwith "unfeasible"
allocate [] qty buckets min max