あなたは基本的に正しい軌道に乗っていると思います。精度または範囲の問題が発生する可能性がありますが、一般に、たとえば 3、2、1、または 0 をランダムに選択する場合、3 を選択する確率を 0 を選択する確率の 4 倍にしたい場合、次のもので満たされたグリッドのすぐ下にある紙の演習:
3 3 3 3
2 2 2
1 1
0
何かを投げて、着地した数字を読みます。
目的の線形スケールのオプションの数は次のとおりです。
- 1 if number of options, n, = 1
- 1 + 2 if n = 2
- 1 + 2 + 3 if n = 3
- ... etc ...
これは、等差数列の単純な合計です。n(n+1)/2 の可能な結果が得られます。たとえば、n = 1 の場合は 1 * 2 / 2 = 1 です。n = 2 の場合は 2 * 3 /2 = 3 です。n = 3 の場合は 3 * 4 / 2 = 6 です。
したがって、すぐに次のように記述します。
NSUInteger random_linear(NSUInteger range)
{
NSUInteger numberOfOptions = (range * (range + 1)) / 2;
NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
... something ...
}
その時点で、uniformRandom がどのビンに分類されるかを決定する必要があります。最も簡単な方法は、最も明白なループを使用することです。
NSUInteger random_linear(NSUInteger range)
{
NSUInteger numberOfOptions = (range * (range + 1)) / 2;
NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
NSUInteger index = 0;
NSUInteger optionsToDate = 0;
while(1)
{
if(optionsToDate >= uniformRandom) return index;
index++;
optionsToDate += index;
}
}
反復せずに optionsToDate を計算できることを考えると、すぐに明らかなより高速な解決策は二分探索です。
さらに賢い見方をすると、uniformRandom は (0, 0) から (n, n) までの線の下にあるボックスの合計です。グラフの下の領域で、グラフは単純な直角三角形です。したがって、面積式から逆算できます。
具体的には、位置 x の (0, 0) から (n, n) までのグラフの下の領域は (x*x)/2 です。つまり、x を探しています。
(x-1)*(x-1)/2 <= uniformRandom < x*x/2
=> (x-1)*(x-1) <= uniformRandom*2 < x*x
=> x-1 <= sqrt(uniformRandom*2) < x
その場合、結果が数値グリッドの次の離散列に進まなかったため、x-1 を取得する必要があります。したがって、平方根演算の単純な整数の切り捨てでそこに到達できます。
したがって、途中で正確な不等式を混乱させていないと仮定し、すべての精度が適合すると仮定します。
NSUInteger random_linear(NSUInteger range)
{
NSUInteger numberOfOptions = (range * (range + 1)) / 2;
NSUInteger uniformRandom = arc4random_uniform(numberOfOptions);
return (NSUInteger)sqrtf((float)uniformRandom * 2.0f);
}