Rastgele aralığı 1–5 ile 1–7 arasında genişletin


693

1 ila 5 aralığında rasgele bir tamsayı üreten bir fonksiyon verildiğinde, 1 ila 7 aralığında rasgele bir tamsayı üreten bir fonksiyon yazın.

  1. Basit bir çözüm nedir?
  2. Bellek kullanımını azaltmak veya daha yavaş bir CPU'da çalıştırmak için etkili bir çözüm nedir?

Bu beklenmedik ilginç bir sorun olduğunu kanıtladı, hala nasıl 1) sabit zamanda yapmak ve 2) (eğer varsa) tekdüze dağılımı bozmak değil düşünüyorum
eugensk

Bir zar ile 5 oyuncu seçerken benzer bir sorun vardı. Zarları sırayla attık, maksimum puanı alan biri seçildi. Tekdüzelik elde edildi, ancak zaman sabitliği değil :)
eugensk

Sorunun verilen işlevi kullanmanız ve sadece 1-7 rasgele dönen bir tane yazmanız gerektiğini belirtmediğini belirten bir yanıt gönderirsem aşağı indirilir miyim?
Doktor Blue

Ne olmuş 7 * rand5() / 5?
kiwixz

@kiwixz, "1 ile 7 arasında" üretecek, ancak 3 veya 6 elde edemeyeceksiniz: {1: 19.96, 2: 20.02, 4: 20.01, 5: 19.99, 7: 20.02} kaba yüzdeleri elle test etme. 7 * .2, 7 * .4, 7 * .6, 7 * .8, 7 * 1.
pythonlarry

Yanıtlar:


572

Bu Adam Rosenfield'ın çözümüne eşdeğerdir, ancak bazı okuyucular için biraz daha açık olabilir. Rand5 () işlevinin 1 ile 5 (dahil) aralığında istatistiksel olarak rasgele bir tamsayı döndüren bir işlev olduğunu varsayar.

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;
}

O nasıl çalışır? Şöyle düşünün: Bu çift boyutlu diziyi kağıda yazdırdığınızı, dart tahtasına yapıştırdığınızı ve rasgele dart attığınızı hayal edin. Sıfır olmayan bir değere çarparsanız, 1 ile 7 arasında istatistiksel olarak rastgele bir değer vardır, çünkü aralarından seçim yapabileceğiniz eşit sayıda sıfır olmayan değer vardır. Eğer sıfıra vurursanız, sıfır olmayana kadar dart atmaya devam edin. Bu kod budur: i ve j indeksleri dart tahtasında rastgele bir yer seçer ve iyi bir sonuç alamazsak, dart atmaya devam ederiz.

Adem'in dediği gibi, bu en kötü durumda sonsuza kadar devam edebilir, ancak istatistiksel olarak en kötü durum asla gerçekleşmez. :)


5
Bu çözümün arkasındaki mantığı anladım, ancak bunun tekdüze bir olasılıkla nasıl sonuçlandığını anlayamıyorum? Birisi matematiği açıklayabilir mi?
user1071840 15:12

6
@ user1071840 - rand5tekdüze ise, valsızgaradaki her hücrenin eşit bir toplama olasılığı vardır. Izgara, [1, 7] aralığında her tamsayıdan tam olarak üç kopya ve dört sıfır içerir. Sonuçların "ham" akışı, [1, 7] değerlerin eşit bir karışımına ve ayrıca izin verilen herhangi bir değerden biraz daha sık meydana gelen bazı sıfırlara eğilimlidir. Ama bu önemli değil, çünkü sıfırlar çıkarıldı, [1, 7] değerlerinin eşit bir karışımını bıraktı.
Daniel Earwicker

3
Sorunu bununla gerçekleştirmenin kısayol yolu: sadece bir kez rand5 () çağırıyorsanız, sadece 5 olası sonucunuz vardır. Açıkçası daha fazla rastgelelik katmadan 5'ten fazla olası sonuca dönüştürmenin bir yolu yoktur.
Daniel Earwicker

1
Daha uzun versiyon: rand5 () sadece (1, 2, 3, 4, 5) değerlerine sahip olabilir. Bu nedenle rand5 () * 5 sadece tam bir aralıkla (1 ... 25) aynı olmayan değerlere (5, 10, 15, 20, 25) sahip olabilir. Eğer öyleyse, 4'ü çıkarmak bunu yapar (-3 ... 21), ancak bu durumda (1, 6, 11, 16, 21) olur, bu nedenle uç noktalar doğrudur, ancak dört büyük delik vardır: ( 2..5), (7..10), (12..15), (17..21). Sonunda mod 7 yaparsınız ve (2, 7, 5, 3, 1) vererek 1 eklersiniz. Böylece ne 4 ne de 6 meydana gelmez. Ancak (yukarıdaki kısayola bakın), sonuçlanan aralıkta sadece 5 sayı olabileceğini biliyorduk, bu yüzden iki boşluk olması gerekiyordu.
Daniel Earwicker

1
Ah, çünkü sadece rand5 () var, rand2 () değil :-)
gzak

353

Sabit bir süre içinde çalışacak (tam olarak doğru) bir çözüm yoktur, çünkü 1/7 baz 5'te sonsuz bir ondalık sayıdır. Basit bir çözüm, reddetme örneklemesi kullanmak olacaktır, örneğin:


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

Bu, döngüde 25/21 = 1.19 tekrarlama beklenen bir çalışma süresine sahiptir, ancak sonsuza kadar sonsuz küçük döngü olasılığı vardır.


7
> 21,> 26 b / c değerine çevrilirse -1 gerekli değildir. Alt sınır haritalarının nerede olduğu önemli değildir,
BCS

26
Bunun neden doğru olduğunu açıklamaya çalışmam: Diyelim ki 1'den 25'e kadar tek tip rasgele sayılar akışı veren bir program yazmak istiyorum; Bunun için cevaptaki kodda olduğu gibi 5 * (rand5 () - 1) + rand5 () döndürürdüm. Şimdi, 1 ile 21 arasında tekdüze rasgele sayılar akışı oluşturmak istiyorsam, sadece ilk akışı kullanır, ancak [22, 25] 'deki sayılar reddedilecek şekilde filtrelersem, bu akışı da oluşturabilirim. Daha sonra, eğer bu akışı alıp her eleman için x% 7 + 1 çıktısı verecek şekilde filtre edersem, 1'den 7'ye tekdüze rastgele sayılardan oluşan bir akışım olur! Oldukça basit, değil mi? : D
Paggas

6
Ve sınırsız en kötü durum çalışma zamanıyla mükemmel bir dağıtım mı yoksa sınırlı bir çalışma zamanıyla kusurlu bir dağıtım mı istediğinize bağlısınız. Bu, tüm güçlerin 5 7 ile bölünemez olmasının veya eşit olarak muhtemelen 5 ^ n uzunluğunda n dizilerine sahip olmanızın bir sonucudur, her diziye her biri 1 ila 7 arasında bir sayı atamanın bir yolu yoktur. 1..7 muhtemelen eşittir.
Adam Rosenfield

5
@Jules Olléon: En kötü durumda Nçağrı yapmaktan daha fazlasını yapmayı garanti etmeyen, sabit zamanda çalışan bir çözüm olduğunu varsayalım rand5(). Daha sonra, rand5her biri 1-7 çıkışa sahip olan çağrı dizisinin 5 ^ N olası sonucu vardır. Dolayısıyla, çıkışı kher 1≤k≤7 için olan tüm olası çağrı dizilerini toplarsanız, o zaman çıktının km / 5 ^ N olma olasılığı vardır , burada m bu sekansların sayısıdır. Yani, m / 5 ^ N = 1/7, ancak bu ==> çelişkisine olası tamsayı çözümü (N, m) yoktur.
Adam Rosenfield

4
@paxdiablo: Yanılıyorsunuz. Gerçek bir RNG'nin 5'li sonsuz bir sekans üretme şansı tam olarak 0'dır, bir jetonun sonsuz sayıda ardışık kafa üretmemesinin garanti edildiği gerçeğine benzer bir mantık kullanır . Bu aynı zamanda bu kodun sonsuza kadar döngü şansı tam olarak 0 olduğu anlamına gelir (her ne kadar rasgele sayıda yineleme için döngü şansı olsa da).
BlueRaja - Danny Pflughoeft

153

İlk cevabımın yanı sıra başka bir cevap daha eklemek istiyorum . Bu cevap , rastgele rand5()arama rand7()kullanımını en üst düzeye çıkarmak için çağrı başına çağrı sayısını en aza indirmeye çalışır . Yani, rasgeleliğin değerli bir kaynak olduğunu düşünüyorsanız, rasgele bitleri atmadan mümkün olduğunca çok kullanmak istiyoruz. Bu cevabın Ivan'ın cevabında sunulan mantıkla da bazı benzerlikleri var .

Rastgele değişkenin entropi iyi tanımlanmış bir miktarı belirtir. Eşit olasılıklı (tekdüze dağılım) N durumlarını alan rastgele bir değişken için, entropi log 2 N'dir. Dolayısıyla, rand5()yaklaşık 2.32193 bit entropiye rand7()sahiptir ve yaklaşık 2.80735 bit entropiye sahiptir. Rasgelelik kullanımımızı en üst düzeye çıkarmayı umut edersek, her çağrıdan 2.32193 bitlik entropinin tamamını kullanmalı ve her çağrı için rand5()gerekli 2.80735 bitlik entropi üretmeye uygulamalıyız rand7(). Öyleyse temel sınır, çağrı rand5()başına log (7) / log (5) = 1.20906 çağrısından daha iyisini yapamayacağımızdır rand7().

Yan notlar: bu cevaptaki tüm logaritmalar aksi belirtilmedikçe baz 2 olacaktır. rand5()[0, 4] rand7()aralığındaki sayıları döndüreceği ve [0, 6] aralığındaki sayıları döndüreceği varsayılır. Aralıkları sırasıyla [1, 5] ve [1, 7] olarak ayarlamak önemsizdir.

Peki bunu nasıl yapıyoruz? 0 ile 1 arasında sonsuz hassas bir rastgele gerçek sayı üretiyoruz (bu kadar sonsuz kesin bir sayı hesaplayabildiğimiz ve saklayabildiğimiz an için - bunu daha sonra çözeceğiz). Baz 5'te rakamlarını oluşturarak böyle bir sayı üretebiliriz: 0 rakamını seçiyoruz. a1 a2 a3 ..., burada her a rakamı ibir çağrı ile seçilir rand5(). Örneğin, RNG'miz iherkes için a = 1 seçtiyse i, o zaman çok rastgele olmadığı gerçeğini göz ardı ederseniz , bu gerçek sayı 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (geometrik bir serinin toplamı).

Tamam, bu yüzden 0 ile 1 arasında rastgele bir gerçek sayı seçtik. Şimdi böyle rastgele bir sayının eşit olarak dağıtıldığını iddia ediyorum. Sezgisel olarak, bunu anlamak kolaydır, çünkü her bir rakam eşit olarak seçilmiştir ve sayı sonsuz derecede kesindir. Bununla birlikte, bunun resmi bir kanıtı biraz daha kapsayıcıdır, çünkü şimdi ayrı bir dağıtım yerine sürekli bir dağıtımla uğraştığımızdan, sayımızın bir aralıkta olma olasılığının [ a, b] uzunluğuna eşit olduğunu kanıtlamamız gerekir . o aralık b - a. Kanıt okuyucu için bir alıştırma olarak bırakılmıştır =).

Artık [0, 1] aralığından eşit olarak seçilen rastgele bir gerçek sayıya sahip olduğumuza göre, çıktısını oluşturmak için [0, 6] aralığındaki bir dizi düzgün rasgele sayıya dönüştürmemiz gerekiyor rand7(). Bunu nasıl yapabiliriz? Az önce yaptığımızın tam tersi - onu taban 7'de son derece hassas bir ondalık sayıya dönüştürüyoruz ve daha sonra her taban 7 basamağı bir çıkışa karşılık gelecek rand7().

Daha önce örnek alarak, eğer rand5()1'ler sonsuz bir akış üretiyorsa, rastgele gerçek sayımız 1/4 olacaktır. 1/4'ü taban 7'ye dönüştürerek, sonsuz ondalık 0.15151515 ... alırız, böylece çıktı 1, 5, 1, 5, 1, 5 vb.

Tamam, bu yüzden ana fikrimiz var, ama iki problemimiz kaldı: sonsuz kesin bir gerçek sayı hesaplayamıyor veya saklayamıyoruz, bu yüzden bunun sadece sınırlı bir kısmını nasıl ele alacağız? İkincisi, aslında onu temel 7'ye nasıl dönüştürebiliriz?

0 ile 1 arasında bir sayıyı 7 tabanına dönüştürmenin bir yolu şöyledir:

  1. 7 ile çarp
  2. Sonucun ayrılmaz parçası, bir sonraki taban 7 rakamıdır
  3. İntegral parçayı çıkartın, sadece kesirli kısmı bırakın
  4. 1. adıma geçin

Sonsuz hassasiyet sorunuyla başa çıkmak için kısmi bir sonuç hesaplıyoruz ve sonucun ne olabileceği konusunda bir üst sınır da saklıyoruz. Yani, rand5()iki kez aradığımızı varsayalım ve iki kez 1 döndü. Şimdiye kadar ürettiğimiz sayı 0.11'dir (temel 5). Sonsuz çağrı serisinin geri kalanı ne olursa olsun, rand5()ürettiğimiz rastgele gerçek sayı asla 0.12'den büyük olmayacaktır: 0.11 ≤ 0.11xyz ... <0.12.

Bu nedenle, şimdiye kadarki sayıyı ve alabileceği maksimum değeri takip ederek, her iki sayıyı da taban 7'ye dönüştürüyoruz . İlk kbasamakları kabul ederse , sonraki kbasamakları güvenli bir şekilde çıkarabiliriz - ne olursa olsun baz 5 basamak sonsuz akışı vardır, onlar asla kbaz 7 temsil sonraki basamak etkilemez !

Ve bu algoritma - bir sonraki çıktısını üretmek için rand7() üretmek için rand5(), rastgele gerçek sayının taban 7'ye dönüştürülmesinde bir sonraki basamağın değerini kesin olarak bildiğimizden emin olmak için ihtiyaç duyduğumuz kadar basamak üretiyoruz. bir test kayışına sahip bir Python uygulaması:

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 __name__ == '__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)

Not rand7_gen()tabana 7. Test koşum aramaları sayıda dönüşümünü kapsayan iç durumunu sahip olduğu için, bir jeneratör döner next(r7)10000 rasgele sayılar üretmek için 10000 kez ve daha sonra dağılımlarını ölçer. Yalnızca tamsayı matematik kullanılır, bu nedenle sonuçlar tam olarak doğrudur.

Ayrıca buradaki sayıların çok büyüdüğünü, çok hızlı . 5 ve 7 güçleri hızla büyür. Bu nedenle, performans, bignum aritmetiği nedeniyle çok sayıda rastgele sayı üretildikten sonra belirgin bir şekilde düşmeye başlayacaktır. Ancak burada hatırlıyorum, hedefim, performansı en üst düzeye çıkarmak için değil, rastgele bitlerin kullanımını en üst düzeye çıkarmaktı (bu ikincil bir hedef olmasına rağmen).

Bir koşuda 12091 tane çağrı yaptım rand5() , 10000 çağrı içinrand7() , minimum günlük (7) / log (5) çağrısını ortalama 4 önemli rakama ve elde edilen çıktı eşitti.

Bu kodu yerleşik olarak tam olarak büyük tamsayıları olmayan bir dile taşımak için, yerel integral türünüzün değerlerini pow5ve pow7maksimum değerini sınırlamanız gerekir - çok büyük olursa, sıfırlayın her şey ve baştan başlayın. Bu, arama rand5()başına ortalama çağrı sayısını rand7()çok az artıracaktır, ancak umarım 32 veya 64 bit tamsayılar için bile çok fazla artmamalıdır.


7
Gerçekten ilginç bir cevap için +1. Belirli bir değere sıfırlamak yerine, kullanılan bitleri basitçe kaydırıp diğer bitleri yukarı hareket ettirmek ve temel olarak sadece kullanılacak bitleri tutmak mümkün mü? Yoksa bir şey mi kaçırıyorum?
Chris Lutz

1
% 100 emin değilim, ama eğer bunu yaparsanız, dağıtımı o kadar hafif eğirirsiniz (bu çarpıklığın trilyonlarca deneme olmadan ölçülebileceğinden şüpheliyim).
Adam Rosenfield

FTW! Ben bignumları küçültmeye çalıştım ama yapılamıyor çünkü 5'in hiçbir gücünün 7 gücüyle ortak faktörleri yok! Ayrıca, getiri anahtar kelimesinin iyi kullanımı. Çok iyi yapılmış.
Eyal

2
Çok hoş! Büyüme durumu olmadan ekstra entropiyi koruyabilir miyiz? Hile, hem üst hem de alt sınırların her zaman rasyonel sayılar olduğunu fark etmektir. Hassasiyeti kaybetmeden bunları ekleyebilir, çıkarabilir ve çoğaltabiliriz. Eğer hepsini taban 35'te yaparsak, neredeyse oradayız. Kalan kısım (yedi ile çarpma ve kesirli kısmı tutma) bir egzersiz olarak bırakılmıştır.
Ian

@adam "pow5 ve pow7 değerlerini yerel integral türünüzün maksimum değeriyle sınırlandırın". İkincisi, bunun en azından saf bir şekilde yapılması durumunda, bu dağılımı dağıtacağına inanıyorum.
katalizör

36

( Adam Rosenfeld'in cevabını çaldım ve yaklaşık% 7 daha hızlı çalışmasını sağladım.)

Rand5 () öğesinin eşit dağılımlı {0,1,2,3,4} değerinden birini döndürdüğünü ve hedefin eşit dağılımlı {0,1,2,3,4,5,6} dönüşü olduğunu varsayın.

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);
}

Döngünün değişkente yapabileceği en büyük değeri takip ediyoruz max . Sonuç şimdiye kadar maks.% 7 ile maks-1 arasındaysa, sonuç o aralıkta eşit olarak dağıtılacaktır. Değilse, 0 ile maks.% 7-1 arasında rastgele olan geri kalanını ve yeni bir sayı ve yeni bir maks. Sonra tekrar başlıyoruz.

Düzenleme: Bu denklemde rand5 () x sayısını kaç kez beklemeniz beklenir:

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()

2
Sonuçlar 1.000.000 denemede kataloglanmıştır: 1 = 47216; 2 = 127.444; 3 = 141.407; 4 = 221.453; 5 = 127.479; 6 = 167.536; 7 = 167.465. Gördüğünüz gibi, 1 elde etme ihtimaline göre dağıtım eksik.
Robert K

2
@ Kötü Pire: Sanırım yanılıyorsun. Testiniz için kullandığınız rand5 () girişinin bu çözümde belirtildiği gibi 1-5 yerine 0-4 ürettiğinden emin misiniz?
Adam Rosenfield

5
düzgün dağıtılmış sayılar eklemek eşit dağıtılmış bir sayı ile sonuçlanmaz. Aslında, normal bir dağılıma makul bir yaklaşım elde etmek için sadece bu şekilde eşit dağılmış 6 değişken toplamalısınız.
Mitch Wheat

2
@MitchWheat - Düzgün dağılmış iki tamsayı eklenmesi, aslında, her olası toplamın tam olarak bir şekilde oluşturulabilmesi koşuluyla, tekdüze dağıtılmış rasgele bir tamsayı ile sonuçlanır. İfadede durum böyle 5 * rand5() + rand5().
Ted Hopp

28

Algoritma:

7, 3 bitlik bir sırayla temsil edilebilir

Her biti 0 veya 1 ile rastgele doldurmak için rand (5) kullanın.
Örneğin: rand (5) 'i arayın ve

sonuç 1 veya 2
ise, sonuç 4 veya 5 ise biti 0 ile doldurun
, sonuç 3 ise biti 1 ile doldurun , sonra yok sayın ve tekrar yapın (ret)

Bu şekilde 3 biti 0/1 ile rastgele doldurabilir ve böylece 1-7 arasında bir sayı elde edebiliriz.

EDIT: Bu en basit ve en etkili cevap gibi görünüyor, bu yüzden bunun için bazı kod:

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;
        }
    }
}

1
Her zaman durma probleminin zayıf hayaleti vardır, çünkü zayıf bir rastgele sayı üreteci bir noktada çok fazla üçlü üretebilir .
Alex North-Keys

"sonuç 1 veya 2 ise, sonuç 4 veya 5 ise biti 0 ile doldurun, biti 1 ile doldurun" 1,2,4,5 kabul edilen ve 3 reddedilen mantık nedir? Bunu açıklayabilir misin?
gkns

@gkns Mantık yok, 0 bit ile 1 ve 2 ortalama dolum ve 1 ile 3 ve 4 ortalama dolum olabilir. en azından orijinal rand (5) işlevi kadar rastgele. Harika bir çözüm!
Mo Beigi

Bu ne basit ne de verimli. Random_7 başına random_5 için cals sayısı genellikle en iyi 3'tür. Bu sayfadaki diğer çözümler, 2.2 civarında olan en iyisine daha yakın.
Eyal

1
Boşver, "returnValue == 0" bölümünü kaçırdım
NicholasFolk

19
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 );
    }
}

2
Doğru bir çözüm, rand7 () çağrısı başına ortalama 30/7 = 4.29 çağrısı rand5 () çağrısı yapıyor.
Adam Rosenfield

17
rand7() = (rand5()+rand5()+rand5()+rand5()+rand5()+rand5()+rand5())%7+1

Düzenleme: Bu pek işe yaramıyor. 1000'de yaklaşık 2 parça kapalıdır (mükemmel bir rand5 varsayarak). Kovalar şunları alır:

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

Toplamına geçerek

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

eklenen her 2 için bir büyüklük sırası kazanıyor gibi görünüyor

BTW: yukarıdaki hata tablosu örnekleme yoluyla değil, aşağıdaki yineleme ilişkisi ile oluşturulmuştur:

p[x,n]çağrıların output=xverilebileceği sayı yollarıdır .nrand5

  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]

8
Bu tekdüze bir dağılım değildir. Bu var çok yakın üniforma, ama mükemmel üniforma değil.
Adam Rosenfield

Ah! Zar ve 7'ler. Yanlış olduğumu söyleyecekseniz, kanıtı okuyucu için bir egzersiz olarak bırakmamalısınız.
BCS

45
Tek tip olmadığının kanıtı basittir: Rasgeleliğin gidebileceği 5 ^ 7 olası yol vardır ve 5 ^ 7 7'nin katları olmadığı için, 7 toplamın hepsinin eşit olması olası değildir. (Temel olarak, 7'ye 5'e göreceli olarak asal ya da eşdeğeri 1/7, taban 5'te bir sonlu ondalık olmamakla kaynar.) Aslında, bu kısıtlama altında mümkün olan "en üniform" bile değildir: doğrudan hesaplama, 5 ^ 7 = 78125 toplamı, 1 ile 7 arasındaki değerleri elde etme sayısı {1: 11145, 2: 11120, 3: 11120, 4: 11145, 5: 11190, 6: 11215, 7: 11190} 'dır.
ShreevatsaR

@ShreevatsaR Peki ya rand5 () toplamını yedi kez almak yerine, 5 * 7 alırız - bu işe yaramaz mı? 35 ^ 7% 7 = 35 ^ 5% 7 = 0.
kba

4
@KristianAntonsen: Kaç kez rand5 () yaparsanız, eşit bir dağılım elde edemezsiniz. N kez yaparsanız, 7 ile bölünemeyen 5 ^ N olası çıkış vardır. (35 kez yaparsanız, 35 ^ 7 değil 5 ^ 35 vardır.) kullandığınız çok sayıda çağrıyı eşitleyin (ve herhangi bir sayı olabilir, 7'ye bölünmek zorunda değildir), ancak rand () için çok fazla çağrı kullanmak yerine IMHO tam cevaplar eşit olan ve rand () için beklenen çağrı sayısı az olan üst cevaplardaki algoritma.
ShreevatsaR

15
int ans = 0;
while (ans == 0) 
{
     for (int i=0; i<3; i++) 
     {
          while ((r = rand5()) == 3){};
          ans += (r < 3) >> i
     }
}

2
Doğru bir çözüm, rand7 () çağrısı başına ortalama 30/7 = 4.29 çağrısı rand5 () çağrısı yapıyor.
Adam Rosenfield

3
İhtiyaç olması sol shift : iş algoritmanınans += (r < 3) << i
woolfie

13

Aşağıdakiler, {1, 2, 3, 4, 5, 6, 7} üzerinde {1, 2, 3, 4, 5} üzerinde tekdüze bir dağılım üreten rastgele bir sayı üreteci kullanarak düzgün bir dağılım üretir. Kod dağınık, ancak mantık açık.

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;
}    

2
Çok etkili olmasa da doğru bir çözüm (sizi eğrinin önüne koyar). Bu, rastgele para çevirme başına ortalama 25/6 = 4.17 çağrı yapar, random_7 () çağrısı başına ortalama 100/7 = 14.3 çağrı random_5 () olur.
Adam Rosenfield

Bu çözümün diğerlerine göre avantajı, eşit dağılımlı başka bir aralık üretmek için kolaylıkla genişletilebilmesidir. Geçersiz değerleri yeniden yuvarlayarak bitlerin her birini rastgele seçin (mevcut çözümümüzde 8 sayı üreten 0 değeri gibi).
DenTheMan

1
olası sonsuz döngüler, vs.
robermorales 21:11

1
@robermorales: Son derece düşük.
jason

13
int rand7() {
    int value = rand5()
              + rand5() * 2
              + rand5() * 3
              + rand5() * 4
              + rand5() * 5
              + rand5() * 6;
    return value%7;
}

Seçilen çözümün aksine, algoritma sabit zamanda çalışacaktır. Bununla birlikte, seçilen çözümün ortalama çalışma süresinden rand5'e 2 çağrı daha yapar.

Bu jeneratörün mükemmel olmadığını unutmayın (0 sayısının diğer herhangi bir sayıdan% 0.0064 daha fazla şansı vardır), ancak çoğu pratik amaç için sabit zaman garantisi muhtemelen bu yanlışlığa ağır basar.

açıklama

Bu çözüm, 15,624 sayısının 7 ile bölünebilir olması gerçeğinden türetilir ve bu nedenle 0 ila 15,624 arasında rastgele ve tekdüze sayılar üretebilir ve daha sonra mod 7'yi alırsak, neredeyse üniform bir rand7 jeneratörü alabiliriz. 0'dan 15.624'e kadar olan sayılar, rand5'i 6 kez yuvarlayarak ve bunları kullanarak bir baz 5 numarasının basamaklarını oluşturmak için aşağıdaki şekilde homojen olarak üretilebilir:

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

Ancak mod 7'nin özellikleri denklemi biraz basitleştirmemize izin verir:

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

Yani

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

olur

rand5 * 3 + rand5 * 2 + rand5 * 6 + rand5 * 4 + rand5 * 5 + rand5

teori

15.624 sayısı rastgele seçilmedi, ancak fermat'ın küçük teoremi kullanılarak keşfedilebilir, bu da p'nin asal bir sayı olması durumunda

a^(p-1) = 1 mod p

Yani bu bize

(5^6)-1 = 0 mod 7

(5 ^ 6) -1 eşittir

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

Bu, baz 5 formundaki bir sayıdır ve bu nedenle, bu yöntemin herhangi bir rastgele sayı üretecinden herhangi bir rastgele sayı üretecine gitmek için kullanılabileceğini görebiliriz. P-1 üssünü kullanırken daima 0'a doğru küçük bir sapma uygulanır.

Bu yaklaşımı genelleştirmek ve daha doğru olmak için şöyle bir işleve sahip olabiliriz:

def getRandomconverted(frm, to):
    s = 0
    for i in range(to):
        s += getRandomUniform(frm)*frm**i
    mx = 0
    for i in range(to):
        mx = (to-1)*frm**i 
    mx = int(mx/to)*to # maximum value till which we can take mod
    if s < mx:
        return s%to
    else:
        return getRandomconverted(frm, to)

1
Bu jeneratör doğrudur, ancak tam olarak üniform değildir . Bunu görmek için, [0,15624] 'deki tek tip bir jeneratörün 7 ile bölünemeyen 15625 olası sonucu olduğu gerçeğini göz önünde bulundurun. Bu, 0 sayısına (2233/15625 şansı ve diğerleri sadece 2232/15625). Sonuçta, Fermat'ın küçük teoremini kullanırken ilk bakışta doğru görünebilir, (5 ^ 6)% 7 = 1 değil (5 ^ 6)% 7 = 0 diyor. İkincisi, herhangi bir üs için açıkça imkansızdır çünkü 5 ve 7'nin ikisi de asal sayılardır. Sanırım hala kabul edilebilir bir çözüm ve yayınınızı bunu yansıtacak şekilde düzenledim.
havacı

12

Burada ödev problemlerine izin veriliyor mu?

Bu işlev, 0 ile 6 arasında bir sayı üretmek için ham "temel 5" matematiğini yapar.

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

3
Çok etkili olmasa da doğru bir çözüm (sizi eğrinin önüne koyar). Bu, rnd7 () 'ye yapılan her çağrı için ortalama rnd5 () çağrısı yapar.
Adam Rosenfield

biraz daha açıklamaya ihtiyacım var pls
Barry

1
@Barry - İlk olarak, sadece iki rastgele sayı ekleyemezsiniz, doğrusal bir çözüm elde edemezsiniz (bir çift zar düşünün). Şimdi "Base 5" 'i düşünün: 00, 01, 02, 03, 04, 10, 11. Base 5'teki 0-6. Bu yüzden, baz 5 numarasının 2 hanesini üretmemiz ve aralık dahilinde bir tane alın. R2 * 5 + r1 bunu yapar. R2> 1 döngüsü oradadır çünkü asla> 1'den büyük bir rakam istemezdik.
Hartung

Bu çözelti tekdüze bir dağılım oluşturmaz. 1 ve 7 sayıları yalnızca bir şekilde oluşturulabilir, ancak 2 ila 6'nın her biri iki şekilde oluşturulabilir: r1, eksi 1 ve r2 sayısına eşittir 0 veya r1, eksi 2 ve r2 sayısına eşittir 1. Böylece 2'den 6'ya kadar ortalama 1 veya 7'den iki kat daha fazla geri
dönülecektir

12

Biz tek yani en verimli bir cevap vermek çalışmakla ek sınırlamayı düşünürsek bir giriş akışı sağladı ki I, uzunluğun birörnek dağıtılmış tamsayılar m1-5 çıkışlarına bir dere gelen Oen uzun boy göreli 1-7 arasında eşit olarak dağıtılmış tamsayılar, için m, demek L(m).

Bunu analiz etmenin en basit yolu, Osırasıyla I ve 5-ary ve 7-ary sayıları olarak işlem yapmaktır. Bu, ana cevaplayıcının akışı alma fikriyle a1, a2, a3,... -> a1+5*a2+5^2*a3+..ve benzer şekilde akış için elde edilir O.

Sonra uzunluktaki giriş akımının bir bölümünü alırsak m choose n s.t. 5^m-7^n=cnerede c>0ve mümkün olduğu kadar küçük olduğunu. Sonra gelen tamsayılar uzunluk m girdi akışından tek tip haritası var 1etmek 5^mve 1'den tamsayılar başka üniforma haritası 7^nuzunluğunun çıktı akımına n biz tamsayı eşlenen zaman giriş akımından birkaç vakayı kaybetmek gerekebilir nerede aşar 7^n.

Yani bu bir değer verir L(m), etrafımızda ait m (log5/log7)yaklaşık .82m.

Yukarıdaki analizi ile zorluk denklem 5^m-7^n=cüniforma değeri tam olarak nerede çözmek kolay değildir ve vaka 1için 5^maşar 7^nve biz verimliliği kaybederler.

Soru, m'nin (log5 / log7) mümkün olan en iyi değerine ne kadar yakın olabileceğidir. Örneğin, bu sayı bir tam sayıya yaklaştığında, bu tam integral sayı çıktı değerlerini elde etmenin bir yolunu bulabilir miyiz?

Eğer 5^m-7^n=cdaha sonra girdi akışından etkili bir tekdüze rasgele bir sayı üretmek 0için (5^m)-1ve daha yüksek bir değer kullanmayın 7^n. Ancak bu değerler kurtarılıp tekrar kullanılabilir. 1'den 1'e kadar düzgün bir sayı dizisi oluştururlar.5^m-7^n . Daha sonra bunları kullanmaya çalışabilir ve daha fazla çıkış değeri oluşturabilmemiz için bunları 7-ary sayılarına dönüştürebiliriz.

Düzgün bir boyut girişinden türetilen tamsayıların T7(X)çıkış dizisinin ortalama uzunluğu olmasına izin verirsek ve bunu varsayarsak .random(1-7)X5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7

Daha sonra T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)bir uzunluğa sahip olduğumuz için olasılıkla 7 ^ n0 / 5 ^ m 5^m-7^n0olasılıklı bir dizimiz yok.(5^m-7^n0)/5^m) .

Sadece değiştirmeye devam edersek elde ederiz:

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

bundan dolayı

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

Bunu koymanın başka bir yolu:

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)

Mümkün olan en iyi durum, nerede 5^m=7^n+s, nerede yukarıda orijinal durumum s<7.

Sonra T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)eskisi gibi.

En kötü durum sadece k ve st 5 ^ m = kx7 + s'yi bulabildiğimiz durumdur.

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

Diğer durumlar arasında bir yerde bulunmaktadır. Çok büyük m için ne kadar iyi yapabileceğimizi görmek ilginç olurdu, yani hata terimini ne kadar iyi alabiliriz:

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

e(m) = o(1)Genel olarak başarmak imkansız gibi görünüyor ama umarım kanıtlayabiliriz e(m)=o(m).

Her şey daha sonra 5^mçeşitli değerleri için 7-ary basamaklarının dağılımına dayanır m.

Eminim orada bunu kapsayan bir sürü teori var, bir noktada bir göz atabilir ve rapor verebilirim.


+2 (eğer yapabilirsem) - bu tek iyi cevaptı (sadece yeterli olanın aksine). 32 bit tamsayılara uyan en iyi ikinci cevaba sahipsiniz.
Rex Kerr 10'10

10

İşte Adam'ın cevabının çalışan bir Python uygulaması .

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'a baktığım algoritmaları atmaktan hoşlanıyorum, böylece onlarla oynayabilirim, orada birisinin faydalı olması umuduyla burada yayınlayacağımı düşündüm, birlikte atmanın çok uzun sürmedi.


Hayır, bu benim cevabımdan oldukça farklı. 21 kez döngü yapıyorsunuz ve ilk 20 yinelemenin sonuçlarını atıyorsunuz. Ayrıca giriş olarak bir rand4 () ve bir rand5 () kullanıyorsunuz, bu da sadece rand5 () kullanma kurallarını açıkça ihlal ediyor. Son olarak, homojen olmayan bir dağılım üretirsiniz.
Adam Rosenfield

Bunun için üzgünüm. Bu soruya baktığımda oldukça yoruldum, algoritmanızı tamamen yanlış okuyabileceğim kadar yoruldum. Aslında Python'a attım çünkü neden 21 kere döngü yaptığınızı anlayamadım. Şimdi çok daha mantıklı. Rastgele. Rasgele (1, 4) şeyi stenografi olarak yaptım ama sanırım haklısın, sorunun ruhuna aykırı. Kodu düzelttim.
James McMahon

@robermorales - Adam Rosenfeld'in cevabında açıkladığı gibi , [1, 7] üzerinde gerçek bir homojen dağılım sağlayan her çözüm, potansiyel olarak sonsuz olan bir tür kabul-reddetme döngüsünü içerecektir. (Ancak, eğer rand5()iyi bir PRNG ise, döngü sonsuz 5*(rand5() - 1) + rand5()olmayacaktır, çünkü sonunda kesinlikle <= 21 olacaktır.)
Ted Hopp

10

Neden basit değil?

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

Bu çözümde 1 ve 7 elde etme şansı modulo nedeniyle daha düşüktür, ancak sadece hızlı ve okunabilir bir çözüm istiyorsanız, bu yoludur.


13
Bu tekdüze bir dağılım oluşturmaz. Bu, olası tüm 25 sonuçların sayılmasıyla doğrulanabileceği 2/25, 4/25, 5/25, 5/25, 5/25, 3/25, 1/25 olasılıkları olan 0-6 sayılarını üretir.
Adam Rosenfield

8

Burada rand (n) " 0 ila n-1 arasında eşit dağılımlı rastgele tamsayı" anlamına geldiğini varsayarsak , burada Python'un randintini kullanan ve bu etkiye sahip bir kod örneği. Randint (7) etkisini üretmek için sadece randint (5) ve sabitleri kullanır . Aslında biraz aptalca

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

1
@robermorales Çünkü Python'da yok do ... while. Bu olabilirdi 1337, ya 12345, ya da herhangi bir sayı> 1.
tckmn

8

Adam Rosenfield'ın doğru cevabının ardındaki öncül:

  • x = 5 ^ n (durumunda: n = 2)
  • [1, x] aralığında y sayısı elde etmek için n rand5 çağrılarını değiştirin
  • z = ((int) (x / 7)) * 7
  • y> z ise tekrar deneyin. başka geri dönüş% 7 + 1

N eşittir 2, 4 atma olasılığınız vardır: y = {22, 23, 24, 25}. N eşittir 6 kullanırsanız, sadece 1 topu oyuna sokarsınız: y = {15625}.

5 ^ 6 = 15625
7 * 2232 = 15624

Rand5'e daha çok diyorsun. Ancak, bir fırlatma değeri (veya sonsuz döngü) elde etme şansınız çok daha düşüktür. Eğer y için olası bir geri dönüş değeri almanın bir yolu yoksa, henüz bulamadım.


1
Atma değeri olmayan hiçbir vaka yoktur - eğer atılma olmasaydı, 5 ^ n ve 7 ^ m'nin ortak bir faktörü olurdu. Ama onlar asal (güçler), yani değiller.
Rex Kerr 10'10

8

İşte cevabım:

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);
  }
}

Diğerlerinden biraz daha karmaşık, ama rand5 çağrılarını en aza indirdiğine inanıyorum. Diğer çözümlerde olduğu gibi, uzun süre döngü yapabileceği küçük bir olasılık var.


Bu, diğer çözümlerden çok farklı olmayan bir dağıtım üretir, ancak gereksiz derecede karmaşık olmanın dezavantajına sahiptir. Ayrıca, sayılar gerçekten rastgele ise, muhtemelen yanlış, deterministik olmayan döngü-sonsuza dek olasılıktan muzdariptir. Hala biraz daha az düzgün bir dağılım üretenlerin (yine de yeterli olandan çok daha fazla olsa da) ama deterministik davranışın daha iyi olduğunu düşünüyorum.
paxdiablo

@Pax: Lütfen bunun nasıl düzgün olmayan bir dağıtım oluşturduğuna dair beni aydınlatın. Kod analizimin yanı sıra kendi testlerim, bunun tekdüze bir dağılım oluşturduğunu gösterir. Daha önce tartıştığımız gibi, hem mükemmel bir şekilde eşit dağılım sağlamak hem de çalışma süresinin üst sınırında garantili sabit bir zamana sahip olmak imkansızdır.
Adam Rosenfield


6

Aralarından seçim yapabileceğiniz yedi olasılık kalmadığı sürece, olasılık sayısını beş ile çarpan başka bir rastgele sayı çizin. Perl dilinde:

$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;
}

dağıtımınız en azından ilk çağrıda tekdüze değildir. Gerçekten de, $possibilitiesdöngüden çıkıp geri dönmek için her zaman 25'e büyümesi gerekir. Yani, ilk sonucunuz, [0-124] % 7eşit olarak dağıtılmadığı için 125 % 7 != 0(bu aslında 6).
bernard paulus

6

1'den başlayan aralıkları sevmiyorum, bu yüzden 0'dan başlayacağım :-)

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;
}

Bu bir kazanan. Bu eşit olasılıkla 7 sonucun tümünü üretir. from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
hughdbrown

5

İşte, eşit dağılım ve sıfır rand5 çağrıları.

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

Önceden tohum ayarlamanız gerekir.


5

Yanıtlandığını biliyorum, ama bu işe yarıyor gibi görünüyor, ama bir önyargı olup olmadığını söyleyemem. 'Testlerim' en azından makul olduğunu gösteriyor.

Belki Adam Rosenfield yorum yapmak için kibar olurdu?

Benim (naif?) Fikrim şudur:

Bir rand7 yapmak için yeterli rastgele bit olana kadar rand5'i biriktirin. Bu en fazla 2 rand5 alır. Rand7 numarasını almak için birikmiş değer mod 7'yi kullanıyorum.

Akümülatörün taşmasını önlemek için ve akümülatör mod 7 olduğundan akümülatörün mod 7'sini alıyorum:

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

Rand7 () işlevi şu şekildedir:

(Rand5 aralığının 0-4 olmasına izin verdim ve rand7 de 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;
}

Düzenleme: 100 milyon deneme için sonuç eklendi.

'Gerçek' rand fonksiyonları mod 5 veya 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: 14292388 5: 14288736 6: 14280046

Rand7

Ortalama iyi görünüyor ve sayı dağılımları da iyi görünüyor.

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


Muhtemelen ardışık korelasyona bakmalısınız. Eğer ardışık çiftler (selefi ile eşleştirilmiş her "rastgele" sayı alırsanız, o zaman şaşırtıcı şeyler bulabilirsiniz. Niçin, dağıtımı niçin olursa olsun, her durumda eşit tutması gerektiğini açıklamadınız. Bir çalışma programı normalde neden çalıştığını açıklayarak başlamalıdır.
Ian

Sıralı korelasyon bu çözümlerin birçoğuna uygulanır mı?
philcolbourn

Sıralı korelasyon bu çözümlerin birçoğuna uygulanır mı? Bunu denediğimden beri bir süre geçti ve açıkladığımı düşündüm. Şimdi baktığımda, rand5'ten bir havuzda rastgele bitler biriktiriyorum, bir rand7 numarası yapmak için yeterince geri çekilmeden ve biriktiricimin taşmamasını sağlamak için yeterince birikmiş olduğundan eminim.
philcolbourn

4

Yukarıda değinilen zarif algoritmalar vardır, ancak bu, dolambaçlı olsa da, ona yaklaşmanın bir yolu. 0'dan üretilen değerleri varsayıyorum.

R2 = 2'den küçük değerler veren rastgele sayı üreteci (örnek alanı = {0, 1})
R8 = 8'den küçük değerler veren rastgele sayı üreteci (örnek alanı = {0, 1, 2, 3, 4, 5, 6, 7 })

R2'den R8 üretmek için üç kez R2 çalıştırırsınız ve 3 çalışmanın toplam sonucunu 3 basamaklı bir ikili sayı olarak kullanırsınız. R2 üç kez çalıştırıldığında değer aralığı şunlardır:

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

Şimdi R8'den R7 üretmek için 7 döndürürse R7'yi tekrar çalıştırırız:

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

Dolambaçlı çözüm, R5'ten R2 (aynen R8'den R7 ürettiğimiz gibi), daha sonra R2'den R8 ve sonra R8'den R7 üretmektir.


diğerleri gibi, bu yaklaşım R7 çağrısı başına keyfi olarak uzun sürebilir, çünkü R8'den uzun bir yedili dizi alabilirsiniz.
Alex North-Keys

4

İşte tamamen tamsayılara uyan ve optimalin yaklaşık% 4'ü dahilinde bir çözüm (yani {0..6} 'da her biri için {0..4}' de 1.26 rasgele sayılar kullanır). Kod Scala'da, ancak matematik herhangi bir dilde makul derecede açık olmalıdır: 7 ^ 9 + 7 ^ 8'in 5 ^ 11'e çok yakın olmasından yararlanabilirsiniz. Böylece, temel 5'te 11 basamaklı bir sayı seçersiniz ve sonra aralıkta ise (9 temel 7 sayı vererek) taban 7'de 9 basamaklı bir sayı olarak veya 9 basamaklı sayının üzerinde ise 8 basamaklı bir sayı olarak yorumlayabilirsiniz. .:

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)
  }
}

Yorumlayıcıya bir test yapıştırırsanız (aslında REPL), şunları elde edersiniz:

scala> val five = new Random5
five: Random5 = Random5@e9c592

scala> val seven = new FiveSevener(five)
seven: FiveSevener = FiveSevener@143c423

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

Dağıtım güzel ve düzdür (yaklaşık bir Gauss dağılımından beklendiği gibi, her bir kutuda 10 ^ 8'in yaklaşık 1/7'si içinde).


3

Yuvarlanan toplamı kullanarak her ikisini birden

  • eşit dağılımı sürdürmek; ve
  • rastgele dizideki herhangi bir öğeyi feda etmek zorunda değilsiniz.

Her iki sorun da basit rand(5)+rand(5)...tip çözümlerle ilgili bir sorundur . Aşağıdaki Python kodu nasıl uygulanacağını gösterir (bunların çoğu dağıtımı kanıtlamaktadır).

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)

Ve bu çıktı sonuçları gösterir:

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%)

Bunun rand(5)+rand(5)6'dan fazla geri döndüğü vakaları göz ardı eden basit , yukarıda gösterilen yöntemin 100 katı , % 18 tipik bir varyasyona sahiptir :

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%)

Ve Nixuz'un tavsiyesi üzerine, senaryoyu temizledim, böylece rand7...eşyaları çıkartabilir ve kullanabilirsiniz :

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)

2
Err, hadi bunu yeniden ifade edeyim. Belirli bir x'in sekansın bir noktasında üretildiği göz önüne alındığında, sekanstaki bir sonraki sayı için 7 sayının sadece 5'i üretilebilir. Gerçek bir RNG, tüm örneklerin birbirinden bağımsız olmasını sağlar, ancak bu durumda açıkça değildirler.
Adam Rosenfield

3
Orijinal sorunun giriş ve çıkış işlevlerinin bağımsız ve aynı şekilde dağıtılmış (iid) örnekler üretip üretmediğini belirtmemesi doğrudur, ancak giriş rand5 () iid ise çıkış rand7 () iid de olmalı. Bunun makul olduğunu düşünmüyorsanız, iid olmayan RNG'nizi kullanarak eğlenin.
Adam Rosenfield

1
Peki, üniversitedeki matematikçilerin kelimesi ne?
Adam Rosenfield

1
Bu çözüm açıkça kırılmıştır. Rand7 çağrısı başına rand5'i (ortalama olarak) bir kereden fazla çağırmanız gerektiği açıktır ve bu çözüm değildir. Bu nedenle sonuçlar rastgele herhangi bir aklıca tanımla rastgele olamaz.
Chris Suter

1
@Pax İşlevinizin her yinelemesinde, yalnızca beş farklı değerden birini döndürebilir (0-6 aralığında olsa da). İlk yineleme yalnızca 0-4 aralığındaki bir sayıyı döndürebilir. Bu nedenle, fonksiyonunuz düzgün bir dağılıma sahip olsa da, örneklerin bağımsız olmadığı, yani rastgele bir sayı üretecinde istediğiniz bir şey olmadığı korelasyonlu oldukları açık olmalıdır.
Chris Suter

3

Bu cevap daha çok Rand5 fonksiyonundan mümkün olan en fazla entropinin elde edilmesinde yapılan bir deneydir. Bu nedenle, diğer uygulamalardan biraz belirsiz ve neredeyse kesinlikle çok daha yavaştır.

0-4 arasında eşit dağılım ve 0-6 arasında eşit dağılım varsayılırsa:

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'e yapılan çağrı başına arabelleğe eklenen bit sayısı şu anda 4/5 * 2 yani 1.6'dır. 1/5 olasılık değeri 0,05 kadar 1,65 artar, ancak bunu devre dışı bırakmak zorunda kaldım koddaki açıklamaya bakın.

Rand7'ye çağrı ile tüketilen bitler = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
Bu 3 + 3/8 + 3/64 + 3/512 ... yani yaklaşık 3.42

Yediden bilgi çıkararak, arama başına 1/8 * 1/7 bit'i geri alırım, böylece yaklaşık 0.018

Bu, arama başına 3,4 bit net tüketim sağlar; bu, oranın her Rand7 için 2,125 çağrı olduğu Rand5 anlamına gelir. Optimum 2.1 olmalıdır.

Bu yaklaşım hayal ediyorum belirgin Rand5 için çağrı maliyeti (entropi bazı dış kaynağa seslendiğini demek) son derece pahalı olmadığı sürece yavaş burada diğerlerinden çok daha.


Bazı basit hataların yanı sıra çözümünüz doğru görünüyor: "if (count> 1)", "if (count <= 1)" olmalı ve kısa bir süre sonra oluşan "i ++", kendinden önceki kıvırcık ayraçların içinde olmalıdır. BitsSet () 'in doğru olup olmadığından emin değilim, ama bu biraz alakasız.
Adam Rosenfield

Genel olarak, fonksiyonunuzu anlamak çok zordur. Bu bir hale geliyor biraz daha komplikasyon pahasına, aksi durumda daha entropi daha iyi şekilde. Ayrıca, ilk çağrıda 3 rastgele yeterli olduğunda tamponu başlangıçta 35 rastgele bit ile doldurmak için bir neden yoktur.
Adam Rosenfield

Ben düzeltildi <= teşekkürler, i ++ gerçekten olsa orada olmalı. Sıfır ve 1 durumda gerçekleşmelidir (ara belleğe sırasıyla 1 veya sıfır ekleyerek). Bu kesinlikle tavsiye edeceğim şey değil, çok karmaşık. Sadece ilgileniyorum, problemin doğasında var olan teorik entropi sınırlarına ne kadar yaklaşabildim ... Geri bildiriminiz için teşekkürler. İronik olarak, ilk çağrıda tamponun doldurulması yazmayı kolaylaştırmaktı :)
ShuggyCoUk

Bunu (hız pahasına) anlaşılması daha kolay olacak şekilde yeniden çalıştım ama aynı zamanda doğru yaptım. Henüz optimum değildir, bazı nedenlerden dolayı 1/5 bitler sayıca eşit olsalar bile sorunlara neden olur.
ShuggyCoUk

3

php'de

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 ile 127 arasında rastgele bir sayı üretmek için döngüler, 1 ile 7.9375 arasında bir şamandıra oluşturmak için on altıya bölünür, sonra 1 ile 7 arasında bir int elde etmek için yuvarlanır. eğer yanılmıyorsam, 16/112 şans 7 sonuçtan herhangi biri.


her ne kadar koşullu bir döngü ve zemin yerine modulo kullanarak buna benzer daha kolay bir cevap olsa da. şu anda sadece sayıları kıramam.
dqhendricks

3
extern int r5();

int r7() {
    return ((r5() & 0x01) << 2 ) | ((r5() & 0x01) << 1 ) | (r5() & 0x01);
}

Sorun: Bu 0-6 aralığında değil, 0-7 aralığında eşit olmayan bir şekilde geri döner. Gerçekten,7 = 111bp(7) = 8 / 125
bernard paulus

3

Sanırım dört cevabım var, ikisi @Adam Rosenfield gibi kesin çözümler veriyor, ancak sonsuz döngü problemi olmadan ve diğer ikisi neredeyse mükemmel bir çözüm ama ilkinden daha hızlı uygulama.

En iyi çözüm 7 çağrı gerektirir rand5, ancak anlayabilmek için devam edelim.

Yöntem 1 - Kesin

Adem'in cevabının gücü, mükemmel bir düzgün dağılım sağlaması ve sadece iki rand5 () çağrısına ihtiyaç duyulması olasılığı çok yüksek (21/25). Ancak, en kötü durum sonsuz döngüdür.

Aşağıdaki ilk çözüm de mükemmel bir düzgün dağılım sağlar, ancak toplam 42 çağrı gerektirir rand5. Sonsuz döngü yok.

İşte bir R uygulaması:

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'ye aşina olmayan insanlar için, basitleştirilmiş bir sürüm:

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
}

Dağıtımı rand5korunacaktır. Matematiği yaparsak, döngünün 7 yinelemesinin her birinin 5 ^ 6 olası kombinasyonu vardır, bu nedenle toplam olası kombinasyon sayısı vardır (7 * 5^6) %% 7 = 0. Böylece, rastgele sayıları 7 eşit grupta bölebiliriz. Bununla ilgili daha fazla tartışma için ikinci yönteme bakınız.

İşte tüm olası kombinasyonlar:

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 

Bence Adam'ın yönteminin çok daha hızlı çalışacağını göstermek doğru. rand5Adam'ın çözümünde 42 veya daha fazla çağrı yapılması olasılığı çok azdır ( (4/25)^21 ~ 10^(-17)).

Yöntem 2 - Tam Değil

Şimdi neredeyse tek tip olan ancak 6 çağrı gerektiren ikinci yöntem rand5:

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

İşte basitleştirilmiş bir sürüm:

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

Bu, esasen yöntem 1'in bir tekrarıdır. Tüm olası kombinasyonları üretersek, sonuç olarak şunlar sayılır:

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 = 15625Denemelerde bir sayı bir kez daha görünecektir .

Şimdi, Yöntem 1'de, 1 ila 6 ekleyerek, birbirini izleyen noktaların her birine 2233 sayısını taşırız. Böylece toplam kombinasyon sayısı eşleşecektir. Bu, 5 ^ 6 %% 7 = 1 olduğu için çalışır ve sonra 7 uygun varyasyon yaparız, bu nedenle (7 * 5 ^ 6 %% 7 = 0).

Yöntem 3 - Kesin

Yöntem 1 ve 2'nin argümanı anlaşılırsa, yöntem 3 izler ve yalnızca 7 çağrı gerektirir rand5. Bu noktada, bunun kesin bir çözüm için gereken minimum çağrı sayısı olduğunu hissediyorum.

İşte bir R uygulaması:

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

rand7 <- function()  (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1

R'ye aşina olmayan insanlar için, basitleştirilmiş bir sürüm:

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

Dağıtımı rand5korunacaktır. Matematiği yaparsak, döngünün 7 yinelemesinin her birinin 5 olası sonucu vardır, bu nedenle toplam olası kombinasyon sayısıdır (7 * 5) %% 7 = 0. Böylece üretilen rastgele sayıları 7 eşit grupta bölebiliriz. Bununla ilgili daha fazla tartışma için birinci ve ikinci yöntemlere bakınız.

İşte tüm olası kombinasyonlar:

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 

Bence Adam'ın yönteminin hala daha hızlı çalışacağını göstermek doğru. rand5Adam'ın çözümünde 7 veya daha fazla çağrı yapılması olasılığı hala küçük ( (4/25)^3 ~ 0.004).

Yöntem 4 - Tam Değil

Bu, ikinci yöntemin küçük bir varyasyonudur. Neredeyse aynıdır, ancak 7 çağrıyı gerektirir rand5, bu yöntem 2'ye bir ektir:

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

İşte basitleştirilmiş bir sürüm:

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

Mümkün olan tüm kombinasyonları üretersek, sonuç olarak şunlar sayılır:

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 = 78125Denemelerde iki sayı daha az görünecektir . Çoğu amaç için, bununla yaşayabilirim.


1
R'ye aşina değilim, ama bunların nasıl çalıştığını yanlış anlamadım, o zaman yöntem 1 tam değil. (5 ^ 6) ^ 7 = 5 ^ 42 olası sonucu vardır, (5 ^ 6) * 7 değil; 5 ^ 42 7 ile bölünemez. Aynı şekilde yöntem 3 de kesin değildir. 5 * 7 değil, 5 ^ 7 olası sonucu vardır. (Yöntem 3 son döngü tekrarı i=7ilave Aynı zamanda, herhangi bir etkiye sahip 7*rand5()için rdeğerini değiştirmez rmod 7.)
Adam Rosenfield

2

İhtiyacınız olan fonksiyon rand1_7 () , test edip çizmek için rand1_5 () yazdım.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.