web-dev-qa-db-ja.com

1〜5から1〜7の範囲でランダムに拡大

1から5の範囲の乱数整数を生成する関数を考えて、1から7の範囲の乱数整数を生成する関数を書いてください。

  1. 簡単な解決策は何ですか?
  2. メモリ使用量を減らしたり、遅いCPUで実行したりするための効果的な解決策は何ですか?
681
praveen

これはAdam Rosenfieldのソリューションと同等ですが、一部の読者にとってはもう少し明確になるかもしれません。 Rand5()が1から5までの範囲の統計的にランダムな整数を返す関数であると仮定します。

int Rand7()
{
    int vals[5][5] = {
        { 1, 2, 3, 4, 5 },
        { 6, 7, 1, 2, 3 },
        { 4, 5, 6, 7, 1 },
        { 2, 3, 4, 5, 6 },
        { 7, 0, 0, 0, 0 }
    };

    int result = 0;
    while (result == 0)
    {
        int i = Rand5();
        int j = Rand5();
        result = vals[i-1][j-1];
    }
    return result;
}

それはどのように機能しますか?このように考えてみましょう。この2次元配列を紙に印刷し、それをダーツボードに貼り付けて、ダーツをランダムに投げることを想像してみてください。ゼロ以外の値を入力した場合、1から7の間の統計的にランダムな値になります。選択できるゼロ以外の値の数は等しいからです。あなたがゼロをヒットした場合は、あなたがゼロではないヒットするまでダーツを投げ続ける。それがこのコードがしていることです:iとjインデックスはランダムにDartボード上の場所を選択します、そして、もし我々が良い結果が得られないならば、我々はダーツを投げ続けます。

アダムが言ったように、これは最悪のケースで永遠に続くことができますが、統計的に最悪のケースは決して起こりません。 :)

559
Rob McAfee

1/7は基数5の無限小数であるため、一定の時間内に実行される(正確に正しい)解決策はありません。1つの簡単な解決策は、棄却サンプリングを使用することです。


int i;
do
{
  i = 5 * (Rand5() - 1) + Rand5();  // i is now uniformly random between 1 and 25
} while(i > 21);
// i is now uniformly random between 1 and 21
return i % 7 + 1;  // result is now uniformly random between 1 and 7

これは、ループの25/21 = 1.19反復の予想実行時間を持ちますが、無限にループする可能性は無限に小さいです。

344
Adam Rosenfield

私の最初の答え に加えて、別の答えを追加したいのですが。この答えは、ランダム性を最大限に利用するために、Rand5()の呼び出しごとのRand7()の呼び出し数を最小限に抑えることを目的としています。つまり、ランダム性を貴重なリソースと見なしている場合は、ランダムなビットを捨てずにできるだけ多くのリソースを使用します。この答えは、 Ivanの答え に提示されている論理といくつかの類似点も持っています。

確率変数の エントロピー は、明確に定義された量です。等しい確率(一様分布)でN個の状態をとる確率変数の場合、エントロピーはlogです。2 したがって、Rand5()は約2.32193ビットのエントロピーを持ち、Rand7()は約2.80735ビットのエントロピーを持ちます。ランダム性を最大限に利用したい場合は、Rand5()の呼び出しごとに2.32193ビットのエントロピーをすべて使用し、Rand7()の呼び出しごとに必要な2.80735ビットのエントロピーを生成するためにそれらを適用する必要があります。したがって、根本的な限界は、Rand5()の呼び出しごとにRand7()を呼び出すlog(7)/ log(5)= 1.20906を超えることができないことです。

補足:この回答のすべての対数は、特に指定がない限り2の基数になります。 Rand5()は[0、4]の範囲の数値を返すものとし、Rand7()は[0、6]の範囲の数値を返すものとします。範囲を[1、5]と[1、7]にそれぞれ調整するのは簡単です。

じゃあどうやってやるの? 0から1までの無限精度の乱数実数を生成します(このような無限精度の数値を実際に計算して格納できるようにします - 後で修正します)。 。基数5で数字を生成することでこのような数を生成することができます。乱数0を選択します。a1a2a3...、各桁はi Rand5()の呼び出しによって選択されます。たとえば、私たちのRNGがi すべてのiに対して= 1を指定しますが、それがあまりランダムではないという事実を無視します。これは、実数1/5 + 1/5に対応します。2 + 1/53 + ... = 1/4(幾何学的級数の合計)。

わかりました、だから私たちは0と1の間のランダムな実数を選びました。私は今そのような乱数が一様に分布していると主張します。直感的に、これは理解しやすいです、なぜなら各ディジットは一様に選ばれた、そして数は無限に正確です。しかし、これについての正式な証明はやや複雑です。今は離散的な分布ではなく連続的な分布を扱っているので、私たちの数が区間[ab]にある確率は次のようになることを証明する必要があります。その区間の長さ、b - a。証明は読者のための演習として残されています=)。

[0、1]の範囲から一様にランダムな実数が選択されたので、Rand7()の出力を生成するためにそれを[0、6]の範囲の一様乱数の列に変換する必要があります。どうやってこれをするのですか?ちょうどそれを逆にしました - それを7進法で無限に正確な10進数に変換します、そして7進法の各桁はRand7()の一つの出力に対応します。

先ほどの例をとると、Rand5()が無限の1のストリームを生成すると、ランダムな実数は1/4になります。 1/4を基数7に変換すると、無限小数の0.15151515 ...が得られるので、出力1、5、1、5、1、5などとして生成します。

わかりました、それで私達に主な考えがあります、しかし私達に2つの問題が残っています:私達は無限に正確な実数を実際に計算するか、または貯えることができません。次に、どうやって実際にそれを7進法に変換するのでしょうか。

0から1までの数値を基数7に変換する方法の1つは、次のとおりです。

  1. 7を掛ける
  2. 結果の整数部は、次の基本7桁です
  3. 小数部分だけを残して整数部分を引きます
  4. 後藤ステップ1

無限精度の問題に対処するために、部分的な結果を計算し、その結果がどうなるかについての上限も格納します。つまり、Rand5()を2回呼び出して、両方とも1を返したとします。これまでに生成した数は0.11(基数5)です。 Rand5()への無限の一連の呼び出しが生成するものが何であれ、生成する乱数実数は決して0.12より大きくなることはありません。0.11≦0.11xyz ... <0.12であることが常に当てはまります。

そのため、現在の数とそれまでの最大値を追跡しながら、両方の数を基数7に変換します。それらが最初のk桁に一致する場合は、次のk桁を安全に出力することができます - 基数5桁の無限ストリームが何であるかに関係なく、それらは基数7表現の次のk桁に影響を与えることは決してありません!

それがアルゴリズムです - Rand7()の次の出力を生成するには、変換時の次の桁の値を確実に知るために必要な数のRand5()だけを生成します。テスト用ハーネスを使ったPythonの実装です。

import random

Rand5_calls = 0
def Rand5():
    global Rand5_calls
    Rand5_calls += 1
    return random.randint(0, 4)

def Rand7_gen():
    state = 0
    pow5 = 1
    pow7 = 7
    while True:
        if state / pow5 == (state + pow7) / pow5:
            result = state / pow5
            state = (state - result * pow5) * 7
            pow7 *= 7
            yield result
        else:
            state = 5 * state + pow7 * Rand5()
            pow5 *= 5

if __== '__main__':
    r7 = Rand7_gen()
    N = 10000
    x = list(next(r7) for i in range(N))
    distr = [x.count(i) for i in range(7)]
    expmean = N / 7.0
    expstddev = math.sqrt(N * (1.0/7.0) * (6.0/7.0))

    print '%d TRIALS' % N
    print 'Expected mean: %.1f' % expmean
    print 'Expected standard deviation: %.1f' % expstddev
    print
    print 'DISTRIBUTION:'
    for i in range(7):
        print '%d: %d   (%+.3f stddevs)' % (i, distr[i], (distr[i] - expmean) / expstddev)
    print
    print 'Calls to Rand5: %d (average of %f per call to Rand7)' % (Rand5_calls, float(Rand5_calls) / N)

テストハーネスはRand7_gen()を10000回呼び出して10000個の乱数を生成し、その分布を測定します。これは、next(r7)が生成器を返すことに注意してください。整数演算のみが使用されるので、結果は正確に正しいです。

また、ここでの数は非常に大きく、非常に速くなることにも注意してください。 5と7の力は急速に成長します。そのため、2進数演算により、乱数をたくさん生成した後でパフォーマンスが著しく低下し始めます。しかし、ここで覚えておいてください、私の目標はパフォーマンスを最大にすることではなく、ランダムビットの使用を最大にすることでした(それは副次的な目標です)。

この1回の実行で、Rand5()への10000回の呼び出しに対してRand7()への12091回の呼び出しを行い、log(7)/ log(5)呼び出しの平均最小4有効数字を達成しました。出力は均一でした。

このコードを任意の大きさの整数が組み込まれていない言語に移植するには、pow5pow7の値をネイティブ整数型の最大値に制限する必要があります。それからすべてをリセットして最初からやり直してください。これにより、Rand5()の呼び出しごとのRand7()の呼び出しの平均数がごくわずかに増加しますが、32ビットまたは64ビットの整数であっても、あまり増加しないようにしてください。

151
Adam Rosenfield

(私は Adam Rosenfeldの答え を盗んで、実行速度を約7%速くしました。)

Rand5()が等分布で{0,1,2,3,4}の1つを返し、目標が等分布で{0,1,2,3,4,5,6}を返すと仮定します。

int Rand7() {
  i = 5 * Rand5() + Rand5();
  max = 25;
  //i is uniform among {0 ... max-1}
  while(i < max%7) {
    //i is uniform among {0 ... (max%7 - 1)}
    i *= 5;
    i += Rand5(); //i is uniform {0 ... (((max%7)*5) - 1)}
    max %= 7;
    max *= 5; //once again, i is uniform among {0 ... max-1}
  }
  return(i%7);
}

ループが変数maxに作成できる最大値を追跡しています。これまでの結果がmax%7とmax-1の間にある場合、結果はその範囲内で一様に分散されます。そうでなければ、0から最大の%7-1までの間のランダムな余りと、新しい数値と新しい最大を作るためのRand()への別の呼び出しを使います。それから我々は再び始めます。

編集:この方程式でRand5()を呼び出す回数はxです。

x =  2     * 21/25
   + 3     *  4/25 * 14/20
   + 4     *  4/25 *  6/20 * 28/30
   + 5     *  4/25 *  6/20 *  2/30 * 7/10
   + 6     *  4/25 *  6/20 *  2/30 * 3/10 * 14/15
   + (6+x) *  4/25 *  6/20 *  2/30 * 3/10 *  1/15
x = about 2.21 calls to Rand5()
36
Eyal

アルゴリズム:

7は3ビットのシーケンスで表すことができます。

Rand(5)を使用して、各ビットを0または1でランダムに埋めます。
例えば:Rand(5)に電話して

結果が1または2の場合は、ビットを0で埋めます。
結果が4または5の場合、ビットを1で埋めます。
結果が3の場合は、無視してもう一度実行します(拒否)。

このようにして、3つのビットをランダムに0/1で埋めて、1から7までの数字を得ることができます。

EDIT:これは最も単純で最も効率的な答えのように思われるので、ここにいくつかのコードを示します。

public static int random_7() {
    int returnValue = 0;
    while (returnValue == 0) {
        for (int i = 1; i <= 3; i++) {
            returnValue = (returnValue << 1) + random_5_output_2();
        }
    }
    return returnValue;
}

private static int random_5_output_2() {
    while (true) {
        int flip = random_5();

        if (flip < 3) {
            return 0;
        }
        else if (flip > 3) {
            return 1;
        }
    }
}
27
Anand
int randbit( void )
{
    while( 1 )
    {
        int r = Rand5();
        if( r <= 4 ) return(r & 1);
    }
}

int randint( int nbits )
{
    int result = 0;
    while( nbits-- )
    {
        result = (result<<1) | randbit();
    }
    return( result );
}

int Rand7( void )
{
    while( 1 )
    {
        int r = randint( 3 ) + 1;
        if( r <= 7 ) return( r );
    }
}
19
Mike F
Rand7() = (Rand5()+Rand5()+Rand5()+Rand5()+Rand5()+Rand5()+Rand5())%7+1

編集:それはまったく動作しません。 1000で約2パート離れています(完璧なRand5を想定)。バケットの取得:

value   Count  Error%
1       11158  -0.0035
2       11144  -0.0214
3       11144  -0.0214
4       11158  -0.0035
5       11172  +0.0144
6       11177  +0.0208
7       11172  +0.0144

合計に切り替えることにより

n   Error%
10  +/- 1e-3,
12  +/- 1e-4,
14  +/- 1e-5,
16  +/- 1e-6,
...
28  +/- 3e-11

2つ追加するごとに1桁増えるようです

ところで:上記のエラーの表は、サンプリングではなく、次の繰り返し関係によって生成されました。

p[x,n]は、noutput=xに呼び出したときにRand5が発生する可能性のある方法です。

  p[1,1] ... p[5,1] = 1
  p[6,1] ... p[7,1] = 0

  p[1,n] = p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1]
  p[2,n] = p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1]
  p[3,n] = p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1]
  p[4,n] = p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1]
  p[5,n] = p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1]
  p[6,n] = p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1]
  p[7,n] = p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1]
16
BCS
int ans = 0;
while (ans == 0) 
{
     for (int i=0; i<3; i++) 
     {
          while ((r = Rand5()) == 3){};
          ans += (r < 3) >> i
     }
}
15
Nescio

以下は、{1、2、3、4、5}上に一様分布を生成する乱数ジェネレータを使用して、{1、2、3、4、5、6、7}上に一様分布を生成します。コードは面倒ですが、論理は明らかです。

public static int random_7(Random rg) {
    int returnValue = 0;
    while (returnValue == 0) {
        for (int i = 1; i <= 3; i++) {
            returnValue = (returnValue << 1) + SimulateFairCoin(rg);
        }
    }
    return returnValue;
}

private static int SimulateFairCoin(Random rg) {
    while (true) {
        int flipOne = random_5_mod_2(rg);
        int flipTwo = random_5_mod_2(rg);

        if (flipOne == 0 && flipTwo == 1) {
            return 0;
        }
        else if (flipOne == 1 && flipTwo == 0) {
            return 1;
        }
    }
}

private static int random_5_mod_2(Random rg) {
    return random_5(rg) % 2;
}

private static int random_5(Random rg) {
    return rg.Next(5) + 1;
}    
13
jason

最も効率的な答え、つまり1から5までの長さIの一様分布整数の入力ストリームmを与えようとするという追加の制約を考えると、ストリームO [mに相対的な最長の1から7までの一様に分布する整数の_、例えばL(m)

これを分析する最も簡単な方法は、ストリームIとOをそれぞれ5進数と7進数として扱うことです。これは、ストリームa1, a2, a3,... -> a1+5*a2+5^2*a3+..を取得するという主な回答の考え方、およびストリームOの場合と同様に実現されます。

それでは、長さm choose n s.t. 5^m-7^n=cの入力ストリームの一部を取る場合、ここでc>0はできるだけ小さいです。次に、長さmの入力ストリームから1から5^mの整数へのユニフォームマップと、長さnの出力ストリームへの1から7^nの整数へのユニフォームマップがあります。マップされた整数が7^nを超えています。

そのため、これはL(m)の値がおよそm (log5/log7)になり、およそ.82mになります。

上記の分析の難しさは、厳密に解くことが容易ではない方程式5^m-7^n=cと、1から5^mまでの一様な値が7^nを超えて効率を失う場合です。

問題は、m(log5/log7)の可能な限り最良の値にどれだけ近づくことができるかということです。たとえば、この数が整数に近づくと、この整数倍の出力値を達成する方法を見つけることができますか。

5^m-7^n=cの場合、入力ストリームから0から(5^m)-1までの一様乱数を効果的に生成し、7^nより高い値は使用しません。ただし、これらの値は救済して再び使用することができます。それらは1から5^m-7^nまでの一様な数列を効果的に生成します。そのため、これらを使用して7桁の数値に変換して、さらに出力値を作成できるようにします。

T7(X)をサイズXの一様な入力から派生したrandom(1-7)整数の出力シーケンスの平均長とし、その5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7とします。

確率7 ^ n0/5 ^ mの長さのないシーケンスと確率5^m-7^n0の長さ(5^m-7^n0)/5^m)の残差があるので、T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)となります。

代入し続けるだけで、次のようになります。

T7(5^m) = n0x7^n0/5^m + n1x7^n1/5^m + ... + nrx7^nr/5^m  = (n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/5^m

それゆえ

L(m)=T7(5^m)=(n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/(7^n0+7^n1+7^n2+...+7^nr+s)

これを置くもう一つの方法は、次のとおりです。

If 5^m has 7-ary representation `a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r
Then L(m) = (a1*7 + 2a2*7^2 + 3a3*7^3+...+rar*7^r)/(a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r)

可能な限り最高のケースは、5^m=7^n+ss<7の上にある私のオリジナルのケースです。

それからT7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)は以前と同じです。

最悪の場合は、kとsしか見つけられない場合です。t5 ^ m = kx7 + s。

Then T7(5^m) = 1x(k.7)/(k.7+s) = 1+o(1)

他のケースはどこかの中間にあります。非常に大きいmに対してどれだけうまくいくことができるか、すなわち誤差項をどれだけうまく得ることができるかを見ることは興味深いであろう。

T7(5^m) = m (Log5/Log7)+e(m)

e(m) = o(1)を一般に達成することは不可能のようですが、うまくいけばe(m)=o(m)を証明することができます。

全体的なことは、mのさまざまな値に対する7桁の5^mの配布にかかっています。

これをカバーする理論がたくさんあると確信しています。ある時点で見て報告することができます。

12
Ivan

宿題の問題はここで許可されていますか?

この関数は、0から6までの数を生成するために、大まかな「基数5」の計算を行います。

function rnd7() {
    do {
        r1 = rnd5() - 1;
        do {
            r2=rnd5() - 1;
        } while (r2 > 1);
        result = r2 * 5 + r1;
    } while (result > 6);
    return result + 1;
}
11
Will Hartung

なぜそれを簡単にしないのですか?

int random7() {
  return random5() + (random5() % 3);
}

このソリューションで1と7を取得する可能性はモジュロのために低くなります、しかしあなたがただ速くて読みやすいソリューションを望むならば、これは行くべき道です。

10
Ante

これは Adamの答え の実用的なPython実装です。

import random

def Rand5():
    return random.randint(1, 5)

def Rand7():
    while True:
        r = 5 * (Rand5() - 1) + Rand5()
        #r is now uniformly random between 1 and 25
        if (r <= 21):
            break
    #result is now uniformly random between 1 and 7
    return r % 7 + 1

私が見ているアルゴリズムをPythonに投げるのが好きです。そうすれば、一緒に遊ぶことができるようになります。

10
James McMahon
int Rand7() {
    int value = Rand5()
              + Rand5() * 2
              + Rand5() * 3
              + Rand5() * 4
              + Rand5() * 5
              + Rand5() * 6;
    return value%7;
}

選択した解とは異なり、アルゴリズムは一定時間で実行されます。ただし、選択したソリューションの平均実行時間よりもRand5への呼び出しが2回多くなります。

このジェネレータは完璧ではないことに注意してください(数字の0は他のどの数字よりも0.0064%多くのチャンスを持っています)が、ほとんどの実用的な目的のために一定時間の保証はおそらくこの不正確さより重要です。

説明

この解は、15,624という数が7で割り切れるという事実から派生しているので、0から15,624までの数をランダムかつ一様に生成してからmod 7を取ると、ほぼ一様なRand7生成器を得ることができます。 0から15,624までの数字は、Rand5を6回回転させ、それらを使用して次のように基数5の数字を形成することによって、一様に生成することができます。

Rand5 * 5^5 + Rand5 * 5^4 + Rand5 * 5^3 + Rand5 * 5^2 + Rand5 * 5 + Rand5

しかし、mod 7の特性により、方程式を少し単純化することができます。

5^5 = 3 mod 7
5^4 = 2 mod 7
5^3 = 6 mod 7
5^2 = 4 mod 7
5^1 = 5 mod 7

そう

Rand5 * 5^5 + Rand5 * 5^4 + Rand5 * 5^3 + Rand5 * 5^2 + Rand5 * 5 + Rand5

になる

Rand5 * 3 + Rand5 * 2 + Rand5 * 6 + Rand5 * 4 + Rand5 * 5 + Rand5

理論

15,624という数は無作為に選ばれたのではなく、pが素数であればフェルマーの小さな定理を使って見つけることができます。

a^(p-1) = 1 mod p

だからこれは私たちに与えます、

(5^6)-1 = 0 mod 7

(5 ^ 6)-1は等しい

4 * 5^5 + 4 * 5^4 + 4 * 5^3 + 4 * 5^2 + 4 * 5 + 4

これは基数5の形式の数であるため、この方法は任意の乱数発生器から他の任意の乱数発生器への変換に使用できることがわかります。ただし、指数p-1を使用すると、常に0に向かう小さなバイアスが導入されます。

9
Thirlan

Adam Rosenfieldの正しい答えの背後にある前提は、次のとおりです。

  • x= 5 ^ n(彼の場合:n= 2)
  • n Rand5呼び出しを操作して、範囲[1、x]内の数値yを取得します。
  • z=((int)(x/7))* 7
  • y> zの場合は、やり直してください。そうでなければy%7 + 1を返す

Nが2に等しいとき、あなたは4つの使い捨て可能性を持つ:y = {22、23、24、25}。 n = 6の場合、捨てるのは1回だけです:y = {15625}。

5 ^ 6 = 15625
7 * 2232 = 15624

あなたはもっとRand5を呼ぶ。ただし、破棄値(または無限ループ)が発生する可能性ははるかに低くなります。 yの可能な使い捨て値を取得する方法がない場合、私はまだそれを見つけていません。

8
Dinah

これが私の答えです:

static struct Rand_buffer {
  unsigned v, count;
} buf2, buf3;

void Push (struct Rand_buffer *buf, unsigned n, unsigned v)
{
  buf->v = buf->v * n + v;
  ++buf->count;
}

#define Push(n, v)  Push (&buf##n, n, v)

int Rand16 (void)
{
  int v = buf2.v & 0xf;
  buf2.v >>= 4;
  buf2.count -= 4;
  return v;
}

int Rand9 (void)
{
  int v = buf3.v % 9;
  buf3.v /= 9;
  buf3.count -= 2;
  return v;
}

int Rand7 (void)
{
  if (buf3.count >= 2) {
    int v = Rand9 ();

    if (v < 7)
      return v % 7 + 1;

    Push (2, v - 7);
  }

  for (;;) {
    if (buf2.count >= 4) {
      int v = Rand16 ();

      if (v < 14) {
        Push (2, v / 7);
        return v % 7 + 1;
      }

      Push (2, v - 14);
    }

    // Get a number between 0 & 25
    int v = 5 * (Rand5 () - 1) + Rand5 () - 1;

    if (v < 21) {
      Push (3, v / 7);
      return v % 7 + 1;
    }

    v -= 21;
    Push (2, v & 1);
    Push (2, v >> 1);
  }
}

それは他よりも少し複雑ですが、私はそれがRand5への呼び出しを最小にすると思います。他のソリューションと同様に、それが長時間ループする可能性はわずかにあります。

8
Chris Suter

ここで、Rand(n)は、 "(からnまでの一様分布の乱数-1 "、これはPythonのrandintを使ったコードサンプルです。 randint(7)の効果を生成するために、randint(5)と定数のみを使用します。ちょっとばかげて、実際に

from random import randint
sum = 7
while sum >= 7:
    first = randint(0,5)   
    toadd = 9999
    while toadd>1:
        toadd = randint(0,5)
    if toadd:
        sum = first+5
    else:
        sum = first

assert 7>sum>=0 
print sum
8
Joshua Fox

シンプルで効率的

int Rand7 ( void )
{
    return 4; // this number has been calculated using
              // Rand5() and is in the range 1..7
}

あなたの好きな「プログラマー」漫画は何ですか? )。

7
chiccodoro

7つの選択肢が残っていない限り、別の乱数を引きます。これにより、可能性の数に5が掛けられます。 Perlの場合:

$num = 0;
$possibilities = 1;

sub Rand7
{
  while( $possibilities < 7 )
  {
    $num = $num * 5 + int(Rand(5));
    $possibilities *= 5;
  }
  my $result = $num % 7;
  $num = int( $num / 7 );
  $possibilities /= 7;
  return $result;
}
6
user223264

1から始まる範囲は好きではないので、0から始めます:-)

unsigned Rand5()
{
    return Rand() % 5;
}

unsigned Rand7()
{
    int r;

    do
    {
        r =         Rand5();
        r = r * 5 + Rand5();
        r = r * 5 + Rand5();
        r = r * 5 + Rand5();
        r = r * 5 + Rand5();
        r = r * 5 + Rand5();
    } while (r > 15623);

    return r / 2232;
}
6
fredoverflow

あなたはそこに行きます、一様分布とゼロRand5呼び出し。

def Rand7:
    seed += 1
    if seed >= 7:
        seed = 0
    yield seed

事前に種を設定する必要があります。

5
Kugel

私はそれが答えられたことを知っています、しかしこれはうまくいくように思えます、しかしそれがバイアスを持っているかどうか私はあなたに言うことができません。私の「テスト」は、少なくともそれが妥当であることを示唆しています。

おそらくAdam Rosenfieldがコメントに十分親切でしょうか?

私の(素朴な?)考えはこれです:

Rand7を作るのに十分なランダムビットがあるまでRand5を累積します。これは最大で2 Rand5のものを取ります。 Rand7の数値を取得するには、累積値mod 7を使います。

アキュムレータがオーバーフローするのを避けるため、そしてアキュムレータはmod 7なので、アキュムレータのmod 7を使用します。

(5a + Rand5) % 7 = (k*7 + (5a%7) + Rand5) % 7 = ( (5a%7) + Rand5) % 7

Rand7()関数は次のとおりです。

(私はRand5の範囲を0-4にし、Rand7も同様に0-6にします。)

int Rand7(){
  static int    a=0;
  static int    e=0;
  int       r;
  a = a * 5 + Rand5();
  e = e + 5;        // added 5/7ths of a Rand7 number
  if ( e<7 ){
    a = a * 5 + Rand5();
    e = e + 5;  // another 5/7ths
  }
  r = a % 7;
  e = e - 7;        // removed a Rand7 number
  a = a % 7;
  return r;
}

編集:1億件の試験の結果を追加しました。

'Real' Randはmod 5または7を機能します。

Rand5:avg = 1.999802 0:20003944 1:19999889 2:20003690 3:19996938 4:19995539 ​​Rand7:avg = 3.000111 0:14282851 1:14282879 2:14284554 3:14288546 4:14882336 6:14280046

私のRand7

平均は大丈夫に見え、数分布も大丈夫に見えます。

randt:avg = 3.000080 0:14288793 1:14280135 2:14287848 3:14285277 4:14286341 5:14278663 6:14292943

5
philcolbourn

これは完全に整数内に収まり、最適値の約4%以内に収まる解です(つまり、{0..6}のすべての乱数に対して{0..4}の1.26の乱数を使用します)。コードはScalaですが、数学はどの言語でもかなり明確になるはずです。7 ^ 9 + 7 ^ 8は5 ^ 11に非常に近いという事実を利用します。したがって、5進数で11桁の数字を選び、範囲内にある場合は7桁の9桁の数字として解釈し(9桁の7桁の数字を与える)、9桁を超える場合は8桁の数字として解釈します。:

abstract class RNG {
  def apply(): Int
}

class Random5 extends RNG {
  val rng = new scala.util.Random
  var count = 0
  def apply() = { count += 1 ; rng.nextInt(5) }
}

class FiveSevener(five: RNG) {
  val sevens = new Array[Int](9)
  var nsevens = 0
  val to9 = 40353607;
  val to8 = 5764801;
  val to7 = 823543;
  def loadSevens(value: Int, count: Int) {
    nsevens = 0;
    var remaining = value;
    while (nsevens < count) {
      sevens(nsevens) = remaining % 7
      remaining /= 7
      nsevens += 1
    }
  }
  def loadSevens {
    var fivepow11 = 0;
    var i=0
    while (i<11) { i+=1 ; fivepow11 = five() + fivepow11*5 }
    if (fivepow11 < to9) { loadSevens(fivepow11 , 9) ; return }
    fivepow11 -= to9
    if (fivepow11 < to8) { loadSevens(fivepow11 , 8) ; return }
    fivepow11 -= to8
    if (fivepow11 < 3*to7) loadSevens(fivepow11 % to7 , 7)
    else loadSevens
  }
  def apply() = {
    if (nsevens==0) loadSevens
    nsevens -= 1
    sevens(nsevens)
  }
}

テストをインタプリタ(実際にはREPL)に貼り付けると、次のようになります。

scala> val five = new Random5
five: Random5 = [email protected]

scala> val seven = new FiveSevener(five)
seven: FiveSevener = [email protected]

scala> val counts = new Array[Int](7)
counts: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0)

scala> var i=0 ; while (i < 100000000) { counts( seven() ) += 1 ; i += 1 }
i: Int = 100000000

scala> counts
res0: Array[Int] = Array(14280662, 14293012, 14281286, 14284836, 14287188,
14289332, 14283684)

scala> five.count
res1: Int = 125902876

分布は滑らかで平坦です(おおよそガウス分布から予想されるように、各ビンの10 ^ 8の1/7の約10k以内)。

4
Rex Kerr

上で引用したエレガントなアルゴリズムがありますが、これはアプローチする1つの方法ですが、これは迂回路かもしれませんが。値は0から生成されると想定しています。

R2 = 2未満の値を与える乱数発生器(サンプル空間= {0、1})
R8 = 8未満の値を与える乱数発生器(サンプル空間= {0、1、2、3、4、5、6、7})

R2からR8を生成するには、R2を3回実行し、3回の実行すべてを組み合わせた結果を3桁の2進数として使用します。 R2が3回実行されるときの値の範囲は次のとおりです。

0 0 0 - > 0
.
.
1 1 1 - > 7

さて、R8からR7を生成するために、それが7を返したらR7をもう一度実行します。

int R7() {
  do {
    x = R8();
  } while (x > 6)
  return x;
}

ロータリーの解決策は、R5からR2を生成し(R8からR7を生成したように)、次にR2からR8を生成し、次にR8からR7を生成することです。

4
Ashwin

積算合計を使用すると、両方を実行できます。

  • 均等分布を維持する。そして
  • ランダムな順序で要素を犠牲にする必要はありません。

これらの問題はどちらも、単純化されたRand(5)+Rand(5)...型ソリューションの問題です。次のPythonコードはそれを実装する方法を示しています(これの大部分はディストリビューションの証明です)。

import random
x = []
for i in range (0,7):
    x.append (0)
t = 0
tt = 0
for i in range (0,700000):
    ########################################
    #####            qq.py             #####
    r = int (random.random () * 5)
    t = (t + r) % 7
    ########################################
    #####       qq_notsogood.py        #####
    #r = 20
    #while r > 6:
        #r =     int (random.random () * 5)
        #r = r + int (random.random () * 5)
    #t = r
    ########################################
    x[t] = x[t] + 1
    tt = tt + 1
high = x[0]
low = x[0]
for i in range (0,7):
    print "%d: %7d %.5f" % (i, x[i], 100.0 * x[i] / tt)
    if x[i] < low:
        low = x[i]
    if x[i] > high:
        high = x[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / tt)

そしてこの出力は結果を示したものです:

pax$ python qq.py
0:   99908 14.27257
1:  100029 14.28986
2:  100327 14.33243
3:  100395 14.34214
4:   99104 14.15771
5:   99829 14.26129
6:  100408 14.34400
Variation = 1304 (0.18629%)

pax$ python qq.py
0:   99547 14.22100
1:  100229 14.31843
2:  100078 14.29686
3:   99451 14.20729
4:  100284 14.32629
5:  100038 14.29114
6:  100373 14.33900
Variation = 922 (0.13171%)

pax$ python qq.py
0:  100481 14.35443
1:   99188 14.16971
2:  100284 14.32629
3:  100222 14.31743
4:   99960 14.28000
5:   99426 14.20371
6:  100439 14.34843
Variation = 1293 (0.18471%)

これが6を超える値を返すような場合を無視した、単純なRand(5)+Rand(5)では、通常の18%の変動があります。100倍

pax$ python qq_notsogood.py
0:   31756 4.53657
1:   63304 9.04343
2:   95507 13.64386
3:  127825 18.26071
4:  158851 22.69300
5:  127567 18.22386
6:   95190 13.59857
Variation = 127095 (18.15643%)

pax$ python qq_notsogood.py
0:   31792 4.54171
1:   63637 9.09100
2:   95641 13.66300
3:  127627 18.23243
4:  158751 22.67871
5:  126782 18.11171
6:   95770 13.68143
Variation = 126959 (18.13700%)

pax$ python qq_notsogood.py
0:   31955 4.56500
1:   63485 9.06929
2:   94849 13.54986
3:  127737 18.24814
4:  159687 22.81243
5:  127391 18.19871
6:   94896 13.55657
Variation = 127732 (18.24743%)

そして、Nixuzのアドバイスに従って、Rand7...を抽出して使用できるように、スクリプトを整理しました。

import random

# Rand5() returns 0 through 4 inclusive.

def Rand5():
    return int (random.random () * 5)

# Rand7() generator returns 0 through 6 inclusive (using Rand5()).

def Rand7():
    Rand7ret = 0
    while True:
        Rand7ret = (Rand7ret + Rand5()) % 7
        yield Rand7ret

# Number of test runs.

count = 700000

# Work out distribution.

distrib = [0,0,0,0,0,0,0]
rgen =Rand7()
for i in range (0,count):
    r = rgen.next()
    distrib[r] = distrib[r] + 1

# Print distributions and calculate variation.

high = distrib[0]
low = distrib[0]
for i in range (0,7):
    print "%d: %7d %.5f" % (i, distrib[i], 100.0 * distrib[i] / count)
    if distrib[i] < low:
        low = distrib[i]
    if distrib[i] > high:
        high = distrib[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / count)
3
paxdiablo

phpで

function Rand1to7() {
    do {
        $output_value = 0;
        for ($i = 0; $i < 28; $i++) {
            $output_value += Rand1to5();
        }
    while ($output_value != 140);
    $output_value -= 12;
    return floor($output_value / 16);
}

16と127の間の乱数を生成するためにループし、1と7.9375の間のfloatを作成するために16で割り、それから1と7の間のintを得るために切り捨てます。 7つの結果のうちの1つ。

3
dqhendricks

この答えは、Rand5関数から可能な限り最もエントロピーの高いものを得るための実験です。したがって、tは多少不明瞭で、ほぼ確実に他の実装よりはるかに遅くなります。

0〜4の一様分布と、0〜6の一様分布を仮定します。

public class SevenFromFive
{
  public SevenFromFive()
  {
    // this outputs a uniform ditribution but for some reason including it 
    // screws up the output distribution
    // open question Why?
    this.fifth = new ProbabilityCondensor(5, b => {});
    this.eigth = new ProbabilityCondensor(8, AddEntropy);
  } 

  private static Random r = new Random();
  private static uint Rand5()
  {
    return (uint)r.Next(0,5);
  }

  private class ProbabilityCondensor
  {
    private readonly int samples;
    private int counter;
    private int store;
    private readonly Action<bool> output;

    public ProbabilityCondensor(int chanceOfTrueReciprocal,
      Action<bool> output)
    {
      this.output = output;
      this.samples = chanceOfTrueReciprocal - 1;  
    }

    public void Add(bool bit)
    {
      this.counter++;
      if (bit)
        this.store++;   
      if (counter == samples)
      {
        bool? e;
        if (store == 0)
          e = false;
        else if (store == 1)
          e = true;
        else
          e = null;// discard for now       
        counter = 0;
        store = 0;
        if (e.HasValue)
          output(e.Value);
      }
    }
  }

  ulong buffer = 0;
  const ulong Mask = 7UL;
  int bitsAvail = 0;
  private readonly ProbabilityCondensor fifth;
  private readonly ProbabilityCondensor eigth;

  private void AddEntropy(bool bit)
  {
    buffer <<= 1;
    if (bit)
      buffer |= 1;      
    bitsAvail++;
  }

  private void AddTwoBitsEntropy(uint u)
  {
    buffer <<= 2;
    buffer |= (u & 3UL);    
    bitsAvail += 2;
  }

  public uint Rand7()
  {
    uint selection;   
    do
    {
      while (bitsAvail < 3)
      {
        var x = Rand5();
        if (x < 4)
        {
          // put the two low order bits straight in
          AddTwoBitsEntropy(x);
          fifth.Add(false);
        }
        else
        { 
          fifth.Add(true);
        }
      }
      // read 3 bits
      selection = (uint)((buffer & Mask));
      bitsAvail -= 3;     
      buffer >>= 3;
      if (selection == 7)
        eigth.Add(true);
      else
        eigth.Add(false);
    }
    while (selection == 7);   
    return selection;
  }
}

Rand5の呼び出しごとにバッファーに追加されるビット数は、現在4/5 * 2なので1.6です。 1/5の確率値が含まれている場合、それは0.05から1.65まで増加しますが、私はこれを無効にしなければならなかったコードのコメントを見てください。

Rand7への呼び出しによって消費されるビット= 3 + 1/8 *(3 + 1/8 *(3 + 1/8 *(...
これは3 + 3/8 + 3/64 + 3/512 ...ですので約3.42

7から情報を抽出することによって、私は1コールあたり1/8 * 1/7ビットを再生するので、約0.018

これにより、1コールあたり3.4ビットの純消費量が得られます。これは、Rand7ごとにRand5に対して2.125コールであることを意味します。最適値は2.1です。

私はこの方法がここで他の多くのものよりかなり遅いと想像するでしょう。エントロピーの外部ソース).

3
ShuggyCoUk
extern int r5();

int r7() {
    return ((r5() & 0x01) << 2 ) | ((r5() & 0x01) << 1 ) | (r5() & 0x01);
}
3
maxchengcn
Rand25() =5*(Rand5()-1) + Rand5()

Rand7() { 
   while(true) {
       int r = Rand25();
       if (r < 21) return r%3;         
   }
}

これがなぜ機能するのか:ループが永遠に実行される確率は0です。

2
ekr

あなたが必要とする関数はRand1_7()です、私はあなたがそれをテストしてプロットできるようにRand1_5()を書きました。

import numpy
def Rand1_5():
    return numpy.random.randint(5)+1

def Rand1_7():
    q = 0
    for i in xrange(7):  q+= Rand1_5()
    return q%7 + 1
2
Andrea Ambu

最初の関数から出力を拡大縮小するだけ

0) you have a number in range 1-5
1) subtract 1 to make it in range 0-4
2) multiply by (7-1)/(5-1) to make it in range 0-6
3) add 1 to increment the range: Now your result is in between 1-7
2
cartonn

値0〜7の場合、以下のものがあります。

0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111

左から右へビットごとに、Rand5()はp(1) = {2/5、2/5、3/5}を持ちます。したがって、それらの確率分布(〜Rand5())を補完すれば、それを使用して自分の数値を生成できるはずです。解決策を挙げて後で報告します。誰か何か考えがありますか?

R

2
Rob Leclerc
int getOneToSeven(){
    int added = 0;
    for(int i = 1; i<=7; i++){
        added += getOneToFive();
    }
    return (added)%7+1;
}
2
pas1311

ここではRand(n) -> [0, n - 1]という表記法を使っています

私が読んだ答えの多くから、それらは統一性または停止保証を提供しますが、両方を提供しません(adam rosenfeldの第2の答えはあるかもしれません)。

しかし、それは可能です。私たちは基本的にこの分布をしています:

Rand5_proba.png

これは私達に[0-6]上の分布に穴を残します:5と6は発生する可能性がありません。今、確率分布をシフトして合計することによって穴を埋めようとしていると想像してください。

確かに、初期分布をそれ自体で1つシフトし、得られた分布を2つシフトした後、3つずつシフトして7まで繰り返すことによって繰り返すことができます(範囲全体をカバーします)。これを次の図に示します。色の順序は、ステップに対応して、青 - >緑 - >シアン - >白 - >マゼンタ - >黄色 - >赤です。

fig_moving_average_proba.png

各スロットは7つのシフトされた分布のうちの5つでカバーされ(シフトは0から6まで変化します)、そして乱数はran5()の呼び出しから独立していると仮定するので、

p(x) = 5 / 35 = 1 / 7       for all x in [0, 6]

これは、ran5()からの7つの独立した乱数が与えられると、[0-6]の範囲で一様な確率で乱数を計算できることを意味します。実際、ran5()確率分布は、サンプルが独立している限り、均一である必要すらありません(したがって、分布は試行ごとに同じままです)。また、これは5と7以外の数値にも有効です。

これは私たちに次のpython関数を与えます:

def Rand_range_transform(rands):
    """
    returns a uniform random number in [0, len(rands) - 1]
    if all r in rands are independent random numbers from the same uniform distribution
    """
    return sum((x + i) for i, x in enumerate(rands)) % len(rands) # a single modulo outside the sum is enough in modulo arithmetic

これは次のように使用できます。

Rand5 = lambda : random.randrange(5)

def Rand7():
    return Rand_range_transform([Rand5() for _ in range(7)])

Rand7()を7万回呼び出すと、次のようになります。

max: 6 min: 0 mean: 2.99711428571 std: 2.00194697049
0:  10019
1:  10016
2:  10071
3:  10044
4:  9775
5:  10042
6:  10033

完璧ではありませんが、これは良いことです。実際には、この実装では想定の1つが誤っている可能性があります。PRNGを使用するため、次の呼び出しの結果は最後の結果に依存します。

そうは言っても、真にランダムな数のソースを使用すると、出力も真にランダムになるはずです。そして、このアルゴリズムはどの場合でも終了します。

しかし、これにはコストが伴います。単一のRand5()呼び出しにはRand7()への7回の呼び出しが必要です。

2
bernard paulus

私は4つの答えを持っていると思います。2つは厳密な解決策を与えています @ Adam Rosenfieldのように 無限ループの問題はありません。

最も正確な解決策はRand5への7回の呼び出しを必要とします、しかし理解するために先に進みましょう。

方法1 - 正確

Adamの答えの強みは、それが完全に一様な分布を与えることであり、Rand5()への2回の呼び出しだけが必要とされる可能性が非常に高い(21/25)ということです。ただし、最悪の場合は無限ループです。

以下の最初の解決策も完全に一様な分布を与えますが、Rand5への合計42の呼び出しを必要とします。無限ループはありません。

これがRの実装です。

Rand5 <- function() sample(1:5,1)

Rand7 <- function()  (sum(sapply(0:6, function(i) i + Rand5() + Rand5()*2 + Rand5()*3 + Rand5()*4 + Rand5()*5 + Rand5()*6)) %% 7) + 1

Rに慣れていない人のために、これは単純化したものです:

Rand7 = function(){
  r = 0 
  for(i in 0:6){
    r = r + i + Rand5() + Rand5()*2 + Rand5()*3 + Rand5()*4 + Rand5()*5 + Rand5()*6
  }
  return r %% 7 + 1
}

Rand5の配布は保持されます。計算すると、ループの7回の繰り返しのそれぞれに5 ^ 6の可能な組み合わせがあるため、可能な組み合わせの総数は(7 * 5^6) %% 7 = 0です。したがって、7の等しいグループに生成された乱数を分割できます。これについての詳細は、方法2を参照してください。

考えられるすべての組み合わせは次のとおりです。

table(apply(expand.grid(c(outer(1:5,0:6,"+")),(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)

    1     2     3     4     5     6     7 
15625 15625 15625 15625 15625 15625 15625 

Adamの方法がはるかに速く実行されることを示すのは簡単なことだと思います。 AdamのソリューションでRand5への42以上の呼び出しがある可能性は非常に小さいです((4/25)^21 ~ 10^(-17))。

方法2 - 正確ではない

2番目の方法は、ほぼ一様ですが、Rand5を6回呼び出す必要があります。

Rand7 <- function() (sum(sapply(1:6,function(i) i*Rand5())) %% 7) + 1

これは単純化されたバージョンです:

Rand7 = function(){
  r = 0 
  for(i in 1:6){
    r = r + i*Rand5()
  }
  return r %% 7 + 1
}

これは基本的に方法1の1回の繰り返しです。すべての可能な組み合わせを生成すると、結果のカウントは次のようになります。

table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)

   1    2    3    4    5    6    7 
2233 2232 2232 2232 2232 2232 2232

ひとつの数字が5^6 = 15625トライアルでもう一度現れるでしょう。

さて、方法1では、1に6を加えることによって、2233という数字を連続する各点に移動します。したがって、組み合わせの総数は一致します。これは5 ^ 6 %% 7 = 1なので、7つの適切な変形をします(7 * 5 ^ 6 %% 7 = 0)。

方法3 - 正確

方法1と2の引数が理解されると、方法3が続き、Rand5への7回の呼び出しのみが必要になります。現時点で、私はこれが正確な解決のために必要とされる呼び出しの最小数であると感じます。

これがRの実装です。

Rand5 <- function() sample(1:5,1)

Rand7 <- function()  (sum(sapply(1:7, function(i) i * Rand5())) %% 7) + 1

Rに慣れていない人のために、これは単純化したものです:

Rand7 = function(){
  r = 0 
  for(i in 1:7){
    r = r + i * Rand5()
  }
  return r %% 7 + 1
}

Rand5の配布は保持されます。計算すると、ループの7回の反復のそれぞれに5つの結果が考えられるため、可能な組み合わせの総数は(7 * 5) %% 7 = 0です。したがって、我々は7の等しいグループで生成された乱数を分割することができます。これに関するさらなる議論は方法1と2を見てください。

考えられるすべての組み合わせは次のとおりです。

table(apply(expand.grid(0:6,(1:5)),1,sum) %% 7 + 1)

1 2 3 4 5 6 7  
5 5 5 5 5 5 5 

Adamの方法がまだ速く実行されることを示すのは簡単だと思います。 AdamのソリューションでRand5への7回以上の呼び出しがある可能性はまだ小さいです((4/25)^3 ~ 0.004)。

方法4 - 正確ではない

これは2番目の方法のマイナーなバリエーションです。ほぼ一様ですが、Rand5を7回呼び出す必要があります。これは、方法2の追加1つです。

Rand7 <- function() (Rand5() + sum(sapply(1:6,function(i) i*Rand5())) %% 7) + 1

これは単純化されたバージョンです:

Rand7 = function(){
  r = 0 
  for(i in 1:6){
    r = r + i*Rand5()
  }
  return (r+Rand5()) %% 7 + 1
}

すべての可能な組み合わせを生成すると、結果の数は次のようになります。

table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6,1:5),1,sum) %% 7 + 1)

    1     2     3     4     5     6     7 
11160 11161 11161 11161 11161 11161 11160

5^7 = 78125トライアルでは2つの数字が1回少なくなります。ほとんどの目的のために、私はそれと共に生きることができます。

2
Shambho

この解はエントロピーを無駄にせず、範囲内で最初に利用可能な真の乱数を与えます。 各反復で、答えが得られない確率は明らかに減少します。N回の反復で答えが得られる確率は、0から0までの乱数である確率です。そして、max(5 ^ N)はその範囲の最大の7の倍数よりも小さくなります(max-max%7)。少なくとも2回繰り返す必要があります。しかし、それはすべてのソリューションに必ず当てはまります。

int random7() {
  range = 1;
  remainder = 0;

  while (1) {
    remainder = remainder * 5 + random5() - 1;
    range = range * 5;

    limit = range - (range % 7);
    if (remainder < limit) return (remainder % 7) + 1;

    remainder = remainder % 7;
    range = range % 7;
  }
}

数値的には次のものと同等です。

r5=5;
num=random5()-1;
while (1) {
   num=num*5+random5()-1;
   r5=r5*5;
   r7=r5-r5%7;
   if (num<r7) return num%7+1;
}

最初のコードはモジュロ形式でそれを計算します。 2番目のコードは単なる数学です。それともどこかでミスをしました。 :-)

2
Martin
package CareerCup;

public class RangeTransform {
 static int counter = (int)(Math.random() * 5 + 1);

 private int func() {
  return (int) (Math.random() * 5 + 1);
 }

 private int getMultiplier() {
  return counter % 5 + 1;
 }

 public int rangeTransform() {
  counter++;
  int count = getMultiplier();
  int mult = func() + 5 * count;
  System.out.println("Mult is : " + 5 * count);
  return (mult) % 7 + 1;
 }

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  RangeTransform rangeTransform = new RangeTransform();
  for (int i = 0; i < 35; i++)
   System.out.println("Val is : " + rangeTransform.rangeTransform());
 }
}
2
DeWan

Randがすべてのビットに等しい重みを与えると仮定すると、上限でマスクされます。

int i = Rand(5) ^ (Rand(5) & 2);

Rand(5)1b10b11b100b101bのみを返すことができます。あなたは時々2ビットをセットすることであなた自身を心配する必要があるだけです。

2
Ben
function Rand7
   put 200 into x
   repeat while x > 118
      put ((random(5)-1) * 25) + ((random(5)-1) * 5) + (random(5)-1) into x
   end repeat
   return (x mod 7) + 1
end Rand7

Rand5への3回の呼び出しは、平均して125回のうち6回だけ繰り返されます。

それを1〜7回繰り返し、6個の空白で埋められた5×5×5の3D配列と考えてください。ブランクを巻き直します。 Rand5呼び出しは、その配列に3桁の5進数のインデックスを作成します。

4D以上のN次元配列での繰り返しは少なくなりますが、これはRand5関数への呼び出しが標準化されることを意味します。高次元では効率の低下が見られます。 3つは私にとっては良い妥協案のように思えますが、念のために互いに対してそれらをテストしていません。そしてそれはRand5実装固有のものです。

2
user470894

なぜこれがうまくいかないのですか?他にRand5()への1つの追加呼び出し?

i = Rand5() + Rand5() + (Rand5() - 1) //Random number between 1 and 14

i = i % 7 + 1;
2
Sam

これは、他人の答えを検討した後に作成できる最も簡単な答えです。

def r5tor7():
    while True:
        cand = (5 * r5()) + r5()
        if cand < 27:
            return cand

candは[6、27]の範囲にあり、r5()からの可能な結果が均等に分布している場合、可能な結果は均等に分布しています。あなたはこのコードで私の答えをテストすることができます:

from collections import defaultdict

def r5_outcome(n):
    if not n:
        yield []
    else:
        for i in range(1, 6):
            for j in r5_outcome(n-1):
                yield [i] + j

def test_r7():
    d = defaultdict(int)
    for x in r5_outcome(2):
        s = sum([x[i] * 5**i for i in range(len(x))])
        if s < 27:
            d[s] += 1
    print len(d), d

r5_outcome(2)は、r5()の結果のすべての可能な組み合わせを生成します。私は自分のソリューションコードと同じフィルタをテストに使います。あなたは、すべての結果が同様におそらく同じ価値を持っているからであることがわかります。

2
hughdbrown

これが私が見つけたものです:

  1. Random5は1〜5の範囲でランダムに分布します。
  2. 3回実行してそれらを一緒に追加すると、ランダムに分散された3〜15の範囲になります。
  3. 3〜15の範囲で算術演算を実行します
    1. (3〜15) - 1 =(2〜14)
    2. (2〜14)/ 2 =(1〜7)

それから私達が探しているRandom7である1〜7の範囲を得ます。

2
ming_codes

これが私の一般的な実装です、範囲[0、B-1]の一様ジェネレータを与えて範囲[0、N-1]のユニフォームを生成します。

public class RandomUnif {

    public static final int BASE_NUMBER = 5;

    private static Random Rand = new Random();

    /** given generator, returns uniform integer in the range 0.. BASE_NUMBER-1
    public static int randomBASE() {
        return Rand.nextInt(BASE_NUMBER);
    }

    /** returns uniform integer in the range 0..n-1 using randomBASE() */
    public static int randomUnif(int n) {
        int Rand, factor;
        if( n <= 1 ) return 0;
        else if( n == BASE_NUMBER ) return randomBASE();
        if( n < BASE_NUMBER ) {
            factor = BASE_NUMBER / n;
            do
                Rand = randomBASE() / factor;
            while(Rand >= n);
            return Rand;
        } else {
            factor = (n - 1) / BASE_NUMBER + 1;
            do {
                Rand = factor * randomBASE() + randomUnif(factor);
            } while(Rand >= n);
            return Rand;
        }
    }
}

壮観ではないが一般的でコンパクト。基本ジェネレータへの平均呼び出し:

 n  calls
 2  1.250 
 3  1.644 
 4  1.252 
 5  1.000 
 6  3.763 
 7  3.185 
 8  2.821 
 9  2.495 
10  2.250 
11  3.646 
12  3.316 
13  3.060 
14  2.853 
15  2.650 
16  2.814 
17  2.644 
18  2.502 
19  2.361 
20  2.248 
21  2.382 
22  2.277 
23  2.175 
24  2.082 
25  2.000 
26  5.472 
27  5.280 
28  5.119 
29  4.899 
1
leonbloy
  1. 簡単な解決策は何ですか? (Rand5() + Rand5()) % 7 + 1
  2. メモリ使用量を減らしたり、遅いCPUで実行したりするための効果的な解決策は何ですか? Yes, this is effective as it calls Rand5() only twice and have O(1) space complexity

Rand5()が1から5までの乱数を与えると考えてください。
(1 + 1)%7 + 1 = 3
(1 + 2)%7 + 1 = 4
(1 + 3)%7 + 1 = 5
(1 + 4)%7 + 1 = 6
(1 + 5)%7 + 1 = 7

(2 + 1)%7 + 1 = 4
(2 + 2)%7 + 1 = 5
(2 + 3)%7 + 1 = 6
(2 + 4)%7 + 1 = 7
(2 + 5)%7 + 1 = 1
...

(5 + 1)%7 + 1 = 7
(5 + 2)%7 + 1 = 1
(5 + 3)%7 + 1 = 2
(5 + 4)%7 + 1 = 3
(5 + 5)%7 + 1 = 4
...

等々

1
Devendra Lattu

まず頭に浮かんだのはこれです。しかし、私はそれが一様に分布しているかどうかわかりません。 pythonで実装されています

ランダムにインポート

def Rand5():

random.randint(1,5)を返します

def Rand7():

return((((Rand5()-1)* Rand5())%7)+1

1
HOT

フロートレンジを広げることからのリンクからここに来ました。これはもっと楽しいです。私が結論を出した方法の代わりに、 "base"b(4の場合、私はその理由を教えてくれる)を持つ与えられたランダム整数生成関数fに対して私はそれを思いつきました)、それは以下のように拡張することができます:

(b^0 * f() + b^1 * f() + b^2 * f() .... b^p * f()) / (b^(p+1) - 1) * (b-1)

これはランダムジェネレータをFLOATジェネレータに変換します。ここでbpの2つのパラメータを定義します。ここでの "base"は4ですが、bは実際には何でもかまいませんが、無理数などにすることもできます。p、精度とは、floatジェネレータをどの程度細かくしたいかの程度です。これをRand5の呼び出しごとにRand7に対して行われた呼び出しの数と考えてください。

しかし、bをbase + 1(この場合は4 + 1 = 5)に設定すると、良い点になり、一様分布になります。最初にこの1-5ジェネレータを取り除きなさい、それは真実Rand4()+ 1にある:

function Rand4(){
    return Math.random() * 5 | 0;
}

そこに着くために、あなたはRand5()-1Rand4を代用することができます

次に、Rand4を整数ジェネレータからfloatジェネレータに変換します。

function toFloat(f,b,p){
    b = b || 2;
    p = p || 3;
    return (Array.apply(null,Array(p))
    .map(function(d,i){return f()})
    .map(function(d,i){return Math.pow(b,i)*d})
    .reduce(function(ac,d,i){return ac += d;}))
    /
    (
        (Math.pow(b,p) - 1)
        /(b-1)
    )
}

これは最初に書いた関数を与えられたRand関数に適用します。それを試してみてください:

toFloat(Rand4) //1.4285714285714286 base = 2, precision = 3
toFloat(Rand4,3,4) //0.75 base = 3, precision = 4
toFloat(Rand4,4,5) //3.7507331378299122 base = 4, precision = 5
toFloat(Rand4,5,6) //0.2012288786482335 base = 5, precision =6
...

これで、この浮動小数点範囲(0-4 INCLUSIVE)を他の浮動小数点範囲に変換してから、整数になるようにダウングレードすることができます。ここでは4を扱っているので、ここではRand4をベースにしています。したがって、値b=5は一様分布になります。 bが4を超えると、分布に周期的なギャップが生じ始めます。私は3000ポイントで2から8の範囲のb値をテストし、JavaScriptのネイティブMath.randomと比較して、ネイティブのものよりも私にはさらに良く見えます:

http://jsfiddle.net/ibowankenobi/r57v432t/

上記のリンクについては、分布の上部にある[bin]ボタンをクリックして、ビニングサイズを小さくしてください。最後のグラフはネイティブのMath.randomです。4番目のグラフはd = 5で均一です。

浮動小数点数の範囲が得られたら、7を掛けて小数部をスローするか、7を掛けて、0.5を引いて丸めます。

((toFloat(Rand4,5,6)/4 * 7) | 0) + 1   ---> occasionally you'll get 8 with 1/4^6 probability.
Math.round((toFloat(Rand4,5,6)/4 * 7) - 0.5) + 1 --> between 1 and 7
1

私はこの問題に対する興味深い解決策を考え、それを共有したいと思いました。

function Rand7() {

    var returnVal = 4;

    for (var n=0; n<3; n++) {
        var Rand = Rand5();

        if (Rand==1||Rand==2){
            returnVal+=1;
        }
        else if (Rand==3||Rand==4) {
            returnVal-=1;
        }
    }

    return returnVal;
}

Rand7()を1万回ループし、すべての戻り値を合計して1万で除算するテスト関数を作成しました。 Rand7()が正しく機能している場合、計算された平均値は4になります。たとえば、(1 + 2 + 3 + 4 + 5 + 6 + 7/7)= 4です。 :)

1
Eric Rowell

ここでは一様分布を生成しない多くの解決策とそれを指摘する多くのコメントがありますが、質問では要件としてそれを述べていません 。最も簡単な解決策は、

int Rand_7() { return Rand_5(); }

1 - 5の範囲の乱数整数は、明らかに1 - 7の範囲です。まあ、技術的に最も簡単な解決策は定数を返すことですが、それはあまりにも些細なことです。

しかし、私はRand_5関数の存在はニシンだと思います。 「1 - 7の範囲の整数出力を持つ一様分布の擬似乱数ジェネレータを生成する」と質問されたとします。これは単純な問題です(技術的には単純ではありませんが、既に解決されているので、調べることができます)。

一方、質問が1から5までの範囲の整数のための真の乱数ジェネレータ(擬似乱数ではない)を実際に持っていることを意味すると解釈されるならば、解決策は以下のとおりです。

1) examine the Rand_5 function
2) understand how it works
3) profit
1
William Pursell

これはどう

Rand5()%2 + Rand5()%2 + Rand5()%2 + Rand5()%2 + Rand5()%2 + Rand5()%2

これが一様分布かどうかわからない。助言がありますか?

1
rich

Martin's answer に似ていますが、エントロピーを捨てる頻度をはるかに低くしています:

int Rand7(void) {
  static int m = 1;
  static int r = 0;

  for (;;) {
    while (m <= INT_MAX / 5) {
      r = r + m * (Rand5() - 1);
      m = m * 5;
    }
    int q = m / 7;
    if (r < q * 7) {
      int i = r % 7;
      r = r / 7;
      m = q;
      return i + 1;
    }
    r = r - q * 7;
    m = m - q * 7;
  }
}

ここで、0m-1の間のランダムな値を作成し、オーバーフローせずに収まるだけの状態を追加してmを最大化しようとします(INT_MAXはCのintに収まるか、または言語とアーキテクチャで意味のある大きな値に置き換えることができます)。

それから; rが7で割り切れる可能な最大間隔内に収まる場合、実行可能な結果が含まれ、その間隔を7で除算し、残りを結果として取得し、残りの値をエントロピープールに返すことができます。それ以外の場合、rは、均等に分割されない他の間隔にあり、その不適合間隔からエントロピープールを破棄して再起動する必要があります。

ここでの一般的な回答と比較すると、Rand5()を呼び出す頻度は平均して約半分です。

分割は、パフォーマンスのために簡単なビットツイドルとLUTに分解できます。

1
sh1

ここではカバーされていないように思われるもう一つの答え:

int Rand7() {
  int r = 7 / 2;
  for (int i = 0; i < 28; i++)
    r = ((Rand5() - 1) * 7 + r) / 5;
  return r + 1;
}

繰り返しごとに、rは0から6までのランダムな値です。これは、0から4までの範囲のランダム値に追加され(基数7)、結​​果は5で除算され、0から6までの範囲の新しいランダム値が得られます。 rはかなりの偏りから始まります(r = 3は非常に偏っています!)が、各反復はその偏りを5で割ります。

このメソッドは完全に統一されているわけではありません。ただし、バイアスはほとんどなくなります。 1 /(2 ** 64)のオーダーの何か。このアプローチの重要な点は、実行時間が一定であることです(Rand5()も実行時間が一定であると仮定して)。不運な呼び出しが永遠に無効な値を選んで繰り返す可能性があるという理論上の懸念はありません。


また、良い測定のための皮肉な答え(故意かどうかは関係ありません):

1-5はすでに1-7の範囲内にあるため、以下は有効な実装です。

int Rand7() {
  return Rand5();
}

質問は一様分布を求めていませんでした。

1
sh1

このソリューションは、Rob McAfeeの影響を受けています。
しかし、それはループを必要とせず、結果は一様分布になります。

// Returns 1-5
var rnd5 = function(){
   return parseInt(Math.random() * 5, 10) + 1;
}
// Helper
var lastEdge = 0;
// Returns 1-7
var rnd7 = function () {
  var map = [
     [ 1, 2, 3, 4, 5 ],
     [ 6, 7, 1, 2, 3 ],
     [ 4, 5, 6, 7, 1 ],
     [ 2, 3, 4, 5, 6 ],
     [ 7, 0, 0, 0, 0 ]
  ];
  var result = map[rnd5() - 1][rnd5() - 1];
  if (result > 0) {
    return result;
  }
  lastEdge++;
  if (lastEdge > 7 ) {
    lastEdge = 1;
  }
  return lastEdge;
};

// Test the a uniform distribution
results = {}; for(i=0; i < 700000;i++) { var Rand = rnd7(); results[Rand] = results[Rand] ? results[Rand] + 1 : 1;} 
console.log(results)

結果:[1: 99560, 2: 99932, 3: 100355, 4: 100262, 5: 99603, 6: 100062, 7: 100226]

jsFiddle

1
jantimon
function Rand7() {
    while (true) { //lowest base 5 random number > 7 reduces memory
        int num = (Rand5()-1)*5 + Rand5()-1;
    if (num < 21)  // improves performance
        return 1 + num%7;
    }
}

Pythonコード:

from random import randint
def Rand7():
    while(True):
        num = (randint(1, 5)-1)*5 + randint(1, 5)-1
        if num < 21:
                return 1 + num%7

100000回のテスト配布

>>> rnums = []
>>> for _ in range(100000):
    rnums.append(Rand7())
>>> {n:rnums.count(n) for n in set(rnums)}
{1: 15648, 2: 15741, 3: 15681, 4: 15847, 5: 15642, 6: 15806, 7: 15635}
1
dansalmo

1から5の範囲のランダムな整数を生成する関数Rand5()を与え、1から7の範囲のランダムな整数を生成する関数を書くRand7()

提案されたソリューションでは、Rand5を1回だけ呼び出します

実際のソリューション

float Rand7()
{
    return (Rand5() * 7.0) / 5.0 ;
}

ここでの分布はスケーリングされているため、Rand5の分布に直接依存します

整数解

int Rand7()
{
    static int prev = 1;

    int cur = Rand5();

    int r = cur * prev; // 1-25

    float f = r / 4.0; // 0.25-6.25

    f = f - 0.25; // 0-6

    f = f + 1.0; // 1-7

    prev = cur;

    return (int)f;
}

ここでの分布はシリーズRand7(i) ~ Rand5(i) * Rand5(i-1)に依存します

Rand7(0) ~ Rand5(0) * 1

1
Khaled.K

これはC++ 11の機能を利用した答えです。

#include <functional>
#include <iostream>
#include <ostream>
#include <random>

int main()
{
    std::random_device rd;
    unsigned long seed = rd();
    std::cout << "seed = " << seed << std::endl;

    std::mt19937 engine(seed);

    std::uniform_int_distribution<> dist(1, 5);
    auto Rand5 = std::bind(dist, engine);

    const int n = 20;
    for (int i = 0; i != n; ++i)
    {
        std::cout << Rand5() << " ";
    }
    std::cout << std::endl;

    // Use a lambda expression to define Rand7
    auto Rand7 = [&Rand5]()->int
    {
        for (int result = 0; ; result = 0)
        {
            // Take advantage of the fact that
            // 5**6 = 15625 = 15624 + 1 = 7 * (2232) + 1.
            // So we only have to discard one out of every 15625 numbers generated.

            // Generate a 6-digit number in base 5
            for (int i = 0; i != 6; ++i)
            {
                result = 5 * result + (Rand5() - 1);
            }

            // result is in the range [0, 15625)
            if (result == 15625 - 1)
            {
                // Discard this number
                continue;
            }

            // We now know that result is in the range [0, 15624), a range that can
            // be divided evenly into 7 buckets guaranteeing uniformity
            result /= 2232;
            return 1 + result;
        }
    };

    for (int i = 0; i != n; ++i)
    {
        std::cout << Rand7() << " ";
    }
    std::cout << std::endl;

    return 0;
}
1
user515430
int Rand7()
{
    return ( Rand5() + (Rand5()%3) );
}
  1. Rand5() - 1から5の値を返す
  2. Rand5()%3 - 0〜2の値を返します
  3. したがって、合計すると合計値は1〜7になります。
1
prabhakcv

これは@RobMcAfeeと似ていますが、2次元配列ではなくマジックナンバーを使用する点が異なります。

int Rand7() {
    int m = 1203068;
    int r = (m >> (Rand5() - 1) * 5 + Rand5() - 1) & 7;

    return (r > 0) ? r : Rand7();
}
1
invisal

私はあなたがこれをひっくり返していると思います。この単純な解決策はうまくいきませんか?

int Rand7(void)
{
    static int startpos = 0;
    startpos = (startpos+5) % (5*7);
    return (((startpos + Rand5()-1)%7)+1);
}
1
Graham Toal

誰かが私にこれについてのフィードバックを与えることができればクールだろう、私はそれをEclipseで実行するのが簡単で速いので私はちょうど主なメソッドを定義したかもしれないのでassertパターンなしでJUNITを使いました。ちなみに、Rand5は0〜4の値を与え、1を加えると1〜5になります(Rand7と同じ)。したがって、議論は解決策であり、分布であり、0〜4から始まるとは限りません。または1-5 ...

package random;

import Java.util.Random;

import org.junit.Test;

public class RandomTest {


    @Test
    public void testName() throws Exception {
        long times = 100000000;
        int indexes[] = new int[7];
        for(int i = 0; i < times; i++) {
            int Rand7 = Rand7();
            indexes[Rand7]++;
        }

        for(int i = 0; i < 7; i++)
            System.out.println("Value " + i + ": " + indexes[i]);
    }


    public int Rand7() {
        return (Rand5() + Rand5() + Rand5() + Rand5() + Rand5() + Rand5() + Rand5()) % 7;
    }


    public int Rand5() {
        return new Random().nextInt(5);
    }


}

実行すると、この結果が得られます。

Value 0: 14308087
Value 1: 14298303
Value 2: 14279731
Value 3: 14262533
Value 4: 14269749
Value 5: 14277560
Value 6: 14304037

これはとても公平なディストリビューションのようですね。

Rand5()の追加回数を少なくしても複数回にしても(回数が7で割り切れない場合)、分布は明らかにオフセットを示しています。たとえば、Rand5()を3回追加します。

Value 0: 15199685
Value 1: 14402429
Value 2: 12795649
Value 3: 12796957
Value 4: 14402252
Value 5: 15202778
Value 6: 15200250

したがって、これは次のようになります。

public int Rand(int range) {
    int randomValue = 0;
    for(int i = 0; i < range; i++) {
        randomValue += Rand5();
    }
    return randomValue % range;

}

それから私はさらに進むことができます:

public static final int ORIGN_RANGE = 5;
public static final int DEST_RANGE  = 7;

@Test
public void testName() throws Exception {
    long times = 100000000;
    int indexes[] = new int[DEST_RANGE];
    for(int i = 0; i < times; i++) {
        int Rand7 = convertRand(DEST_RANGE, ORIGN_RANGE);
        indexes[Rand7]++;
    }

    for(int i = 0; i < DEST_RANGE; i++)
        System.out.println("Value " + i + ": " + indexes[i]);
}


public int convertRand(int destRange, int originRange) {
    int randomValue = 0;
    for(int i = 0; i < destRange; i++) {
        randomValue += Rand(originRange);
    }
    return randomValue % destRange;

}


public int Rand(int range) {
    return new Random().nextInt(range);
}

DestRangeとoriginRangeをさまざまな値(Originでは7、DESTでは13)に置き換えて試してみたところ、次のような分布になりました。

Value 0: 7713763
Value 1: 7706552
Value 2: 7694697
Value 3: 7695319
Value 4: 7688617
Value 5: 7681691
Value 6: 7674798
Value 7: 7680348
Value 8: 7685286
Value 9: 7683943
Value 10: 7690283
Value 11: 7699142
Value 12: 7705561

ここから私が結論を下すことができるのはあなたがOriginのランダムな "目的地"回を使用することによって他のものに任意のランダムを変更できるということです。これは一種のガウス分布になるでしょう(中間値がよりありそうで、エッジ値がより珍しいです)。しかし、目的地の係数は、このガウス分布全体に均等に分布しているようです。数学者からのフィードバックがあると便利です。

クールなのは、コストが100%予測可能で一定であるのに対し、他のソリューションでは無限ループが発生する可能性が小さいことです。

0
Martin

まず、ramdom5()を1点に6回移動して、7つの乱数を取得します。次に、7つの数字を足して共通の合計を求めます。最後に、1から7までの結果を取得するために1を追加します。少し高い確率。

public int random7(){
    Random random = new Random();
    //function (1 + random.nextInt(5)) is given
    int random1_5 = 1 + random.nextInt(5); // 1,2,3,4,5
    int random2_6 = 2 + random.nextInt(5); // 2,3,4,5,6
    int random3_7 = 3 + random.nextInt(5); // 3,4,5,6,7
    int random4_8 = 4 + random.nextInt(5); // 4,5,6,7,8
    int random5_9 = 5 + random.nextInt(5); // 5,6,7,8,9
    int random6_10 = 6 + random.nextInt(5); //6,7,8,9,10
    int random7_11 = 7 + random.nextInt(5); //7,8,9,10,11

    //sumOfRandoms is between 28 and 56
    int sumOfRandoms = random1_5 + random2_6 + random3_7 + 
                       random4_8 + random5_9 + random6_10 + random7_11;
    //result is number between 0 and 6, and
    //equals 0 if sumOfRandoms = 28 or 35 or 42 or 49 or 56 , 5 options
    //equals 1 if sumOfRandoms = 29 or 36 or 43 or 50, 4 options
    //equals 2 if sumOfRandoms = 30 or 37 or 44 or 51, 4 options
    //equals 3 if sumOfRandoms = 31 or 38 or 45 or 52, 4 options
    //equals 4 if sumOfRandoms = 32 or 39 or 46 or 53, 4 options
    //equals 5 if sumOfRandoms = 33 or 40 or 47 or 54, 4 options
    //equals 6 if sumOfRandoms = 34 or 41 or 48 or 55, 4 options
    //It means that the probabilities of getting numbers between 0 and 6 are almost equal.
    int result = sumOfRandoms % 7;
    //we should add 1 to move the interval [0,6] to the interval [1,7]
    return 1 + result;
}
0
Michael Katkov

私の考えでは、これは複数のMath.random()関数呼び出しからRand5()を再作成し、「重み付き分数」(?)で再構築することによって単位区間(Math.random()の出力範囲)を再構築しようとします。次に、このランダムな単位間隔を使用して、1から7の間のランダムな整数を生成します。

function Rand5(){
  return Math.floor(Math.random()*5)+1;
}
function Rand7(){
  var uiRandom=0;
  var div=1;
  for(var i=0; i<7; i++){
    div*=5;
    var term=(Rand5()-1)/div;
    uiRandom+=term;
  }
  //return uiRandom;
  return Math.floor(uiRandom*7)+1; 
}

言い換えると、0〜4の間のランダムな整数(ちょうどRand5()-1)を取り、それぞれの結果に1/5、1/25、1/125、...を掛けてから合計します。バイナリ加重分数がどのように機能するかに似ています。代わりに、これを2進(base-5)加重小数と呼びます。一連の(1/5)^ n項として、0から0.999999までの数を生成します。

任意の入出力ランダム整数範囲をとるように関数を修正することは簡単なはずです。上のコードはクロージャとして書き直すと最適化できます。


あるいは、これを行うこともできます。

function Rand5(){
  return Math.floor(Math.random()*5)+1;
}
function Rand7(){
  var buffer=[];
  var div=1;
  for (var i=0; i<7; i++){
    buffer.Push((Rand5()-1).toString(5));
    div*=5;
  }
  var n=parseInt(buffer.join(""),5);
  var uiRandom=n/div;
  //return uiRandom;
  return Math.floor(uiRandom*7)+1; 
}

2進数(5進法)の重み付き分数の作成をいじるのではなく、実際に2進数を作成してそれを分数(前と同様に0--0.9999 ...)にしてから、ランダムな1-7桁を計算するそこから。

上記の結果(コードスニペット#2:それぞれ10万呼び出しの3回の実行):

1:14263。 2:14414。 3:14249。 4:14109。 5:14217。 6:14361。 7:14387

1:14205。 2:14394。 3:14238。 4:14187。 5:14384。 6:14224。 7:14368

1:14425。 2:14236。 3:14334。 4:14232。 5:14160。 6:14320。 7:14293

0
jongo45

どうして5で割って7で乗じてから丸めないのですか? (もちろん、あなたは浮動小数点数を使わなければならないでしょう。)

それは他の解決策よりもはるかに簡単で信頼性が高い(本当に?)。例えば。 Pythonでは:

def ranndomNo7():
    import random
    Rand5 = random.randint(4)    # Produces range: [0, 4]
    Rand7 = int(Rand5 / 5 * 7)   # /5, *7, +0.5 and floor()
    return Rand7

そんなに簡単じゃなかった?

0

単純な解決策は十分にカバーされています。1つのrandom5結果に対して2つのrandom7サンプルを取得し、結果が一様分布を生成する範囲外の場合はそれを実行します。あなたの目標がrandom5への呼び出しの数を減らすことであるならば、これは非常に無駄です - 捨てられたサンプルの数のためにrandom5の各出力に対するrandom7への平均呼び出しの数は2.38です。

一度に複数のrandom5出力を生成するには、より多くのrandom7入力を使用することで、より効果的に行うことができます。 31ビット整数で計算された結果の場合、9個のrandom5出力を生成するために12個のrandom7の呼び出しを使用し、1出力あたり平均1.34個の呼び出しを使用する場合が最適です。 244140625の結果のうち2018983だけが廃棄される必要がある、または1%未満であるため効率的です。

Pythonでのデモ:

def random5():
    return random.randint(1, 5)

def random7gen(n):
    count = 0
    while n > 0:
        samples = 6 * 7**9
        while samples >= 6 * 7**9:
            samples = 0
            for i in range(12):
                samples = samples * 5 + random5() - 1
                count += 1
        samples //= 6
        for outputs in range(9):
            yield samples % 7 + 1, count
            samples //= 7
            count = 0
            n -= 1
            if n == 0: break

>>> from collections import Counter
>>> Counter(x for x,i in random7gen(10000000))
Counter({2: 1430293, 4: 1429298, 1: 1428832, 7: 1428571, 3: 1428204, 5: 1428134, 6: 1426668})
>>> sum(i for x,i in random7gen(10000000)) / 10000000.0
1.344606
0
Mark Ransom

この問題の主な概念は正規分布に関するものであり、ここでこの問題に対する単純で再帰的な解決法を提供しました。

私たちのスコープに既にRand5()があるとします。

def Rand7():
    # twoway = 0 or 1 in the same probability
    twoway = None
    while not twoway in (1, 2):
        twoway = Rand5()
    twoway -= 1

    ans = Rand5() + twoway * 5

    return ans if ans in range(1,8) else Rand7()

説明

このプログラムは2つの部分に分けられます。

  1. 1または2が見つかるまでRand5()をループします。つまり、変数twowayに1または2が含まれる確率は1/2になります。
  2. 複合ansRand5() + twoway * 5、これはまさにRand10()の結果です。これが私たちの必要性(1〜7)と一致しない場合は、Rand7を再度実行します。

P.S twowayの各確率が個別である必要があるため、weは2番目の部分でwhileループを直接実行することはできません

しかし、最初のセクションのwhileループとreturn文の再帰のためにトレードオフがあります。この関数は実行時間を保証するものではなく、実際には効果的ではありません。

結果

私は自分の答えに対する分布を観察するための簡単なテストをしました。

result = [ Rand7() for x in xrange(777777) ]

ans = {
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0,
}

for i in result:
    ans[i] += 1

print ans

与えた

{1: 111170, 2: 110693, 3: 110651, 4: 111260, 5: 111197, 6: 111502, 7: 111304}

したがって、この答えが正規分布にあることがわかります。

簡単な答え

この関数の実行時間を気にしないのであれば、上記の回答に基づいた簡単な回答です。

def Rand7():
    ans = Rand5() + (Rand5()-1) * 5
    return ans if ans < 8 else Rand7()

これは8より大きい価値の可能性を増大させますが、おそらくこの問題に対する最短の回答となるでしょう。

0
Colin Su

この式は1から7までの間のランダムな整数を得るのに十分です。

int j = ( Rand5()*2 + 4 ) % 7 + 1;
0
user2713461

このアルゴリズムはRand5の呼び出し数を理論上の最小値7/5に減らします。それを7回呼び出すと、次の5つのRand7番号が生成されます。

任意のランダムビットを拒否することはなく、結果を常に待機し続ける可能性もありません。

#!/usr/bin/env Ruby

# random integer from 1 to 5
def Rand5
    STDERR.putc '.'
    1 + Rand( 5 )
end

@bucket = 0
@bucket_size = 0

# random integer from 1 to 7
def Rand7
    if @bucket_size == 0
        @bucket = 7.times.collect{ |d| Rand5 * 5**d }.reduce( &:+ )
        @bucket_size = 5
    end

    next_Rand7 = @bucket%7 + 1

    @bucket      /= 7
    @bucket_size -= 1

    return next_Rand7
end

35.times.each{ putc Rand7.to_s }
0
robermorales

実装をシンプルかつ効率的に保ちながら、Rand5()の呼び出し回数を最小限にしようとするソリューションがあります。特に、Adam Rosenfieldの2番目の答えとは異なり、任意の大きな整数を必要としません。 23/19 = 1.21052 ...がlog(7)/ log(5)= 1.20906 ...の合理的な近似であるという事実を利用しているため、{1、...、7の19個のランダム要素を生成できます。 } {1、...、5}の23個のランダムな要素のうち、わずかな棄却確率の棄却サンプリングによるもの。平均して、以下のアルゴリズムは、Rand7()の呼び出しごとに、約1.266回のRand5()呼び出しを必要とします。 Rand5()の分布が均一であれば、Rand7()も均一です。

uint_fast64_t pool;

int capacity = 0;

void new_batch (void)
{
  uint_fast64_t r;
  int i;

  do {
    r = 0;
    for (i = 0; i < 23; i++)
      r = 5 * r + (Rand5() - 1);
  } while (r >= 11398895185373143ULL);  /* 7**19, a bit less than 5**23 */

  pool = r;
  capacity = 19;
}

int Rand7 (void)
{
  int r;

  if (capacity == 0)
    new_batch();

  r = pool % 7;
  pool /= 7;
  capacity--;

  return r + 1;
}
0
Emil Jeřábek