web-dev-qa-db-ja.com

特定の合計または平均を持つ範囲でN個のランダムな整数を生成する効率的な方法はありますか?

N個の整数のランダムな組み合わせを生成する効率的な方法はありますか?

  • 各整数は[minmax]の範囲内にあり、
  • 整数はsumの合計を持ち、
  • 整数は任意の順序(ランダムな順序など)で表示できます。
  • 組み合わせは、他の要件を満たすすべての組み合わせの中からランダムに均一に選択されていますか?

ランダムな組み合わせに対して同様のアルゴリズムがあり、整数は値によってソートされた順序で表示される必要がありますか?

(_sum = N * mean_の場合、meanの平均と適切な組み合わせを選択するのは特別なケースです。この問題は、sumの均一なランダムパーティションを生成し、それぞれが[minmax]の間隔でN個の部分に分割されて表示されるのと同じです。任意の順序で、または場合によっては値によってソートされた順序で)

この問題は、ランダムな順序で現れる組み合わせに対して次の方法で解決できることを認識しています(編集[4月27日]:アルゴリズムが変更されました)。

  1. _N * max < sum_または_N * min > sum_の場合、解決策はありません。

  2. _N * max == sum_の場合、ソリューションは1つだけで、すべてのNの数値はmaxと等しくなります。 _N * min == sum_の場合、ソリューションは1つだけで、すべてのNの数値はminと等しくなります。

  3. アルゴリズムを使用 SmithとTrombleで指定(「単位シンプレックスからのサンプリング」、2004)。合計_sum - N * min_でN個のランダムな非負整数を生成します。

  4. この方法で生成された各数値にminを追加します。

  5. maxより大きい数値がある場合は、手順3に進みます。

ただし、maxsumよりもはるかに小さい場合、このアルゴリズムは遅くなります。たとえば、私のテストによれば(上記のmeanを含む特別なケースの実装を使用して)、アルゴリズムは平均して拒否します—

  • _N = 7, min = 3, max = 10, sum = 42_の場合は約1.6サンプルですが、
  • _N = 20, min = 3, max = 10, sum = 120_の場合、約30.6サンプル。

上記の要件を満たしながら、このアルゴリズムを変更して大きなNに対して効率的にする方法はありますか?

編集:

コメントで提案されている代替手段として、有効なランダムな組み合わせを生成する効率的な方法は(最後の要件以外のすべてを満たす)です。

  1. Xsumminを指定して、max、可能な有効な組み合わせの数を計算します。
  2. _[0, X)_の一様なランダム整数であるYを選択します。
  3. ( "unrank")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
}

_
21
Peter O.

範囲[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も整数(に近い)である必要があります。

0
Lior Kogan