N個の整数のランダムな組み合わせを生成する効率的な方法はありますか?
min
、max
]の範囲内にあり、sum
の合計を持ち、ランダムな組み合わせに対して同様のアルゴリズムがあり、整数は値によってソートされた順序で表示される必要がありますか?
(_sum = N * mean
_の場合、mean
の平均と適切な組み合わせを選択するのは特別なケースです。この問題は、sum
の均一なランダムパーティションを生成し、それぞれが[min
、max
]の間隔でN個の部分に分割されて表示されるのと同じです。任意の順序で、または場合によっては値によってソートされた順序で)
この問題は、ランダムな順序で現れる組み合わせに対して次の方法で解決できることを認識しています(編集[4月27日]:アルゴリズムが変更されました)。
_N * max < sum
_または_N * min > sum
_の場合、解決策はありません。
_N * max == sum
_の場合、ソリューションは1つだけで、すべてのN
の数値はmax
と等しくなります。 _N * min == sum
_の場合、ソリューションは1つだけで、すべてのN
の数値はmin
と等しくなります。
アルゴリズムを使用 SmithとTrombleで指定(「単位シンプレックスからのサンプリング」、2004)。合計_sum - N * min
_でN個のランダムな非負整数を生成します。
この方法で生成された各数値にmin
を追加します。
max
より大きい数値がある場合は、手順3に進みます。
ただし、max
がsum
よりもはるかに小さい場合、このアルゴリズムは遅くなります。たとえば、私のテストによれば(上記のmean
を含む特別なケースの実装を使用して)、アルゴリズムは平均して拒否します—
N = 7, min = 3, max = 10, sum = 42
_の場合は約1.6サンプルですが、N = 20, min = 3, max = 10, sum = 120
_の場合、約30.6サンプル。上記の要件を満たしながら、このアルゴリズムを変更して大きなNに対して効率的にする方法はありますか?
編集:
コメントで提案されている代替手段として、有効なランダムな組み合わせを生成する効率的な方法は(最後の要件以外のすべてを満たす)です。
X
、sum
、min
を指定して、max
、可能な有効な組み合わせの数を計算します。[0, X)
_の一様なランダム整数であるY
を選択します。Y
を有効な組み合わせに変換します。しかし、有効な組み合わせ(または順列)の数を計算する式はありますか?整数を有効な組み合わせに変換する方法はありますか? [編集(4月28日):組み合わせではなく順列でも同じ]。
編集(4月27日):
Devroyeの Non-Uniform Random Variate Generation (1986)を読んだ後、これがランダム生成の問題であることを確認できますパーティション。また、661ページの演習2(特にパートE)はこの質問に関連しています。
編集(4月28日):
判明したように、私が与えたアルゴリズムは均一で、関連する整数はランダムな順序で与えられ、ではありません。値でソートされた順序。どちらの問題も一般的な関心事なので、この問題を修正して、両方の問題の標準的な答えを探しました。
次のRubyコードは、均一性の潜在的なソリューションを検証するために使用できます(ここでalgorithm(...)
は候補アルゴリズムです):
_combos={}
permus={}
mn=0
mx=6
sum=12
for x in mn..mx
for y in mn..mx
for z in mn..mx
if x+y+z==sum
permus[[x,y,z]]=0
end
if x+y+z==sum and x<=y and y<=z
combos[[x,y,z]]=0
end
end
end
end
3000.times {|x|
f=algorithm(3,sum,mn,mx)
combos[f.sort]+=1
permus[f]+=1
}
p combos
p permus
_
編集(4月29日):現在の実装のコードを再追加Rubyコード。
次のコード例はRubyで提供されていますが、私の質問はプログラミング言語とは無関係です。
_def posintwithsum(n, total)
raise if n <= 0 or total <=0
ls = [0]
ret = []
while ls.length < n
c = 1+Rand(total-1)
found = false
for j in 1...ls.length
if ls[j] == c
found = true
break
end
end
if found == false;ls.Push(c);end
end
ls.sort!
ls.Push(total)
for i in 1...ls.length
ret.Push(ls[i] - ls[i - 1])
end
return ret
end
def integersWithSum(n, total)
raise if n <= 0 or total <=0
ret = posintwithsum(n, total + n)
for i in 0...ret.length
ret[i] = ret[i] - 1
end
return ret
end
# Generate 100 valid samples
mn=3
mx=10
sum=42
n=7
100.times {
while true
pp=integersWithSum(n,sum-n*mn).map{|x| x+mn }
if !pp.find{|x| x>mx }
p pp; break # Output the sample and break
end
end
}
_
範囲[l、x-1]のランダム値の0≤a≤1を一様に生成し、範囲[x、h]のランダム値の1-aを一様に生成する場合、期待される平均は次のようになります。
m = ((l+x-1)/2)*a + ((x+h)/2)*(1-a)
したがって、特定のmが必要な場合は、aおよびxで遊ぶことができます。
たとえば、x = mを設定した場合:a =(h-m)/(h-l + 1)。
異なる組み合わせでより均一な確率を保証するには、上記の方程式の有効な解のセットからランダムにaまたはxを選択します。 (xは[l、h]の範囲にある必要があり、整数(に近い)である必要があります。N* aも整数(に近い)である必要があります。