これは確かに興味深い質問です (賛成票を投じてください!)。さらに創造的な解決策が見られることを楽しみにしています。リストしたコードで私が抱えている問題の1つはRandom
、longではなく整数のみを返すクラスを使用していることですが、それをlongにキャストするだけです。したがって、関数が本当に long を必要とする場合は、別の乱数ジェネレーターを見つける必要があります。
「理想的な」解決策は、すべての可能な追加の組み合わせ (パーティションと呼ばれる) を生成し、ランダムに 1 つを選択することです。ただし、これを行うための最もよく知られているアルゴリズムは、実際には非常に高価です。この問題については、優れたソース資料がたくさんあります。パーティショニングアルゴリズムに関する特に良い読み物は次のとおりです。
http://www.site.uottawa.ca/~ivan/F49-int-part.pdf
問題の一部は、この場合の「ランダム」の意味を理解することです。たとえば、合計が 1000 になる 5 つの数値の配列を探している場合、{1000,0,0,0,0} は {200,200,200,200,200} と同じくらい有効です。これは {136,238,124,66,436} と同じくらい有効です。 . これらのセットのいずれかを生成する可能性が等しいアルゴリズムは、真にランダムです。
ただし、完全にランダムな分布ではなく、その中間の分布を探していると思います。
残念ながら、私の Java はかなり錆びており、最近はほとんどすべてを C# で行っているため、C# で説明します...これらのアルゴリズムを Java に変換するのにそれほど多くの翻訳は必要ありません。
ここに問題に対する私の見解があります:
public int[] GetRandoms( int size, int sum, Random r=null ) {
if( r==null ) r = new Random();
var ret = new int[size];
while( sum > 0 ) {
var n = r.Next( 1, (int)Math.Ceiling((double)sum/size)+1 );
ret[r.Next(size)] += n;
sum -= n;
}
return ret;
}
(実際には、乱数ジェネレーターをどのように渡しているかを確認してください。デフォルトではnull
、便宜上、新しい乱数ジェネレーターを作成するだけですが、シードされた乱数ジェネレーターを渡すことができ、次の場合に同じ結果を生成できます。これはテスト目的に最適です.乱数ジェネレーターを利用する方法がある場合はいつでも、このアプローチを採用することを強くお勧めします.)
このアルゴリズムは基本的に、合計に達するまで、配列内のランダムなエントリに乱数を追加し続けます。その加算的な性質により、大きな数値が優先されます (つまり、小さな数値が結果に表示される可能性は非常に低くなります)。ただし、数値はランダムに表示され、適切に合計されます。
ターゲット数が小さい場合 (たとえば 100 未満)、すべてのパーティションを生成し、ランダムに 1 つを選択するという真のランダム アプローチを実際に実現できます。たとえば、パーティショニング アルゴリズムは次のとおりです (礼儀http://homepages.ed.ac.uk/jkellehe/partitions.php、C# に翻訳):
public List<int[]> GetPartitions( int n ) {
var result = new List<int[]>();
var a = new int[n+1];
var k = 1;
a[0] = 0;
var y = n - 1;
while( k != 0 ) {
var x = a[k-1] + 1;
k--;
while( 2*x <= y ) {
a[k] = x;
y -= x;
k++;
}
var l = k + 1;
while( x <= y ) {
a[k] = x;
a[l] = y;
result.Add( a.Take( k + 2 ).ToArray() );
x++;
y--;
}
a[k] = x + y;
y = x + y - 1;
result.Add( a.Take( k + 1 ).ToArray() );
}
return result;
}
(Java の翻訳を支援するために、配列からa.Take(x)
最初の要素を取り出すことを意味します...最近の Java には、それを行うための同等の魔法があると確信しています。)x
a
これにより、パーティションのリストが返されます...ランダムに1つを選択するだけで、完全にランダムな分布が得られます.