Bir jeton kullanarak eşit dağılımlı rasgele sayılar oluşturma


25

Bir tane paran var. İstediğiniz kadar çevirebilirsiniz.

Bir rasgele sayı üretmek istiyoruz böyle bir r < b nerede r , bir , b Z + .rar<br,a,bZ+

Sayıların dağılımı tekdüze olmalıdır.

Eğer ise kolaydır :ba=2n

r = a + binary2dec(flip n times write 0 for heads and 1 for tails) 

Ya ?ba2n


Han-Hoshi algoritmasını kullanın - temelde aralığı ikiye bölün, rastgele bitinizi (jeton çevirme) iki aralıktan birini rastgele seçmek için kullanın, sonra bu işlemi bitlerin bitinceye kadar seçtiğiniz tarafta tekrarlayın. Bu size gerçek hattın bir bölümünden eşit olarak dağıtılmış bir aralık verecektir. Ne kadar çok çevirirseniz, aralık o kadar kesin olur.
zenna

Yanıtlar:


13

Aradığın şey, Reddetme örneklemesine veya reddetme yöntemine dayanıyor (Wiki sayfasının biraz teknik olduğunu unutmayın).

Bu yöntem şu tür durumlarda kullanışlıdır: bir kümeden rastgele bir nesne seçmek istiyorsunuz (kümenizdeki kümesindeki rastgele bir tamsayı ), ancak bunu nasıl yapacağınızı bilmiyorsunuz olabilir durumda da (ilk kümesini içeren daha büyük bir set bir miktar rasgele nesne çekme, [ a , 2 k + bir ] bazı k şekilde 2 k + bir b ; bu tekabül k[a,b][a,2k+a]k2k+abk para çevirir).

Böyle bir senaryoda, daha küçük sette rastgele bir öğe seçinceye kadar sadece büyük setteki öğeleri seçmeye devam edersiniz. Küçük setiniz, büyük setinize kıyasla yeterince büyükse (sizin durumunuzda, , [ a , b ] 'den en fazla iki tam sayı içeriyorsa , bu yeterlidir).[a,2k+a][a,b]

Alternatif bir örnek: 1. yarıçapı olan bir daire içinde rastgele bir nokta seçmek istediğinizi varsayalım. Şimdi bunun için doğrudan bir yöntem bulmak kolay değil. Kabul et-reddet yöntemine yöneliyoruz: daireyi kapsayan 1x1 karede noktaları örnekliyoruz ve çizdiğimiz sayının dairenin içinde olup olmadığını test ediyoruz.


3
B'de bir dağılım elde etmek için örnekleri reddedersek , beklenen yineleme sayısının | A |AB(geometrik dağılım ile bir deney yaparken). |A||B|
Raphael

Menzil 2'nin gücü olmadıkça, bunun tam olarak yapılamayacağını bir yerde gördüğümü hatırlıyorum (akla gelince, örn. 1/3, ikili genişlemenin sonlandırması yoktur).
von

7

(teknikler: cevap sayısının seçimine uyar )ax<b

Madeni parayı dilediğin kadar çevirmene izin verildiğinden, bir kesir r [ 0 , 1 ] seçerek istediğin kadar yakın olmalısın.r[0,1] sen çevirmek: (ikilik üs değeri kullanarak her noktadan sonra basamak) ve çoklu için para ile ba 0 ve [ba-1] () kadar bir tam sayıya aşağı yuvarlama arasında bir sayı alır. Bu sayıyı ekle bir ve bitirdiniz.rbaa

Örnek : deyin . İkili 1/3, 0.0101010101'dir .... O zaman, çeviriniz 0 ile 0.010101 arasındaysa, seçiminiz b'dir . o beween 0.010101 .. ve 0.10101010 ise ... senin seçim olacaktır Bir + 1 ve aksi takdirde olacak bir + 2 .ba=3ba+1a+2

Madeni paralarınızı kez çevirirseniz, a ve b arasındaki her sayı olasılıkla seçilecektir. 1tbirb.1b-bir±2-(t+1)


1
Bu düzgün bir dağılım vermez. Bazı uygulamalar için (örneğin, kripto, bazen), bu çok kötü olabilir.
Gilles 'SO- kötülük' dur '21

3
@Gilles: Sonuç değişmesi artık mümkün olmayana kadar çevirerek mükemmel bir üniform dağılım sağlamak için sabitlenebilir. Bu en etkili cevap.
Neil G

@NeilG Sabitlenebileceğini biliyorum, ama düzeltmek cevabın önemli bir parçası olacaktır.
Gilles 'SO- kötülük' dur '21

2
@Gilles: Haklısın. O süre çevirmek eğer mükemmel üniform dağılımını üretebilir söylemek cevabını değiştirebilir . En iyi ortalama durum ve en kötü durum süresi için benden +1. (b-bir)(f+2-t-1)(b-bir)(f-2-t-1)
Neil G

@NeilG, "sabit" olamaz, çünkü sonlandırıcı bir ikili kesir içermeyen oldukça büyük bir tam sayı kümesi vardır.
von marb

7

2 aralık sonraki daha büyük güç numarasını ve daha büyük ıskarta cevapları seç b-bir .

n = b-a;
N = round_to_next_larger_power_of_2(n)
while (1) {
  x = random(0 included to N excluded);
  if (x < n) break;
}
r = a + x;

4
Ve bu neden çalışıyor?
Raphael

@Raphael şüpheci misin, yoksa posterin daha ayrıntılı olarak açıklanmasını mı istiyorsun?
Suresh

1
@Suresh: İkincisi. Sahte kod biraz parlatılabilir, ancak diğer cevaplayıcıların açıkladıklarını uygular. Gerekçe olmadan, bu cevap kendi başına çok değerli değil.
Raphael

6

Kimse bundan bahsetmedi, bu yüzden resmen , boyutu iki olan bir etki alanı değilse, son olarak birçok adil madeni para fırınının, eşit derecede rastgele bir D üyesi oluşturmak için yeterli olmadığını kanıtlamama izin verin . D üyesi oluşturmak için k jeton kullandığını varsayalım . Her d D için , algoritmanızın d üretme olasılığı, bazı A tamsayıları için A / 2 k şeklindedir . Aritmetiğin temel teoremi, A / 2 k1 / | D | .DDkDdDdbir/2kbirbir/2k1/|D|

Eğer oluşturmak istiyorsanız bağımsız üniforma örnekleri D , ardından madalyonun beklenen sayısı kabaca edilir (optimum algoritma altında) gerekmez fırlatır n log 2 | D | . Eğer entropi bir dağıtımdan oluşturmak istiyorsanız Daha genel H , sen kabaca ihtiyaç n H beklenti içinde rastgele bit. Bunu sağlayan bir algoritma, rastgele bitlerin (görünüşte) sonsuz bir dizisine uygulanan aritmetik kod çözmedir.nDngünlük2|D|'Hn'H


3

Eğer ba 2 değerinde değilse, sonuç almak için birçok bozuk para çevirmeniz gerekebilir. Asla bir sonuç bile elde edemeyebilirsiniz, ancak bu aşırı derecede muhtemel değildir.

Yöntemler

En basit yöntem [a, a + 2 ^ n) 'de bir sayı üretmektir, burada 2 ^ n> = ba, biri [a, b)' ye gelene kadar. Bu yöntemle çok fazla entropi atarsınız.

Daha pahalı bir yöntem, tüm entropiyi korumanıza izin verir, ancak jeton sayıları / zar ruloları arttıkça hesaplama açısından çok pahalı hale gelir. Sezgisel olarak, madalyonun ondalık basamağın sağındaki bir ikili sayının hanesi olarak çevrilmesi, bu sayının ardından taban 2'den taban ab'ye dönüştürülmesi ve bu sayının hanelerinin 'sıkışıp kaldıkça' döndürülmesi gibidir.

Örnek

Aşağıdaki kod, adil bir n taraflı kalıbın rulolarını adil bir m taraflı kalıbın rulolarına dönüştürür (durumunda n = 2, m = ab), rulo sayısı arttıkça marjinal maliyeti arttırır. Rasgele bir sayıyla Rational sayı türüne olan ihtiyacı not alın. Güzel bir özellik, n taraflıdan m taraflı ve n taraflı olana geri dönüşümün orijinal akıma dönmesidir, ancak rakamlar sıkışması nedeniyle birkaç rulo tarafından geciktirilmiş olsa da.

public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
    //note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
    Rational b = 0; //offset of the chosen range
    Rational d = 1; //size of the chosen range
    foreach (var r in inputStream) {
        //narrow the chosen range towards the real value represented by the input
        d /= modIn;
        b += d * r;
        //check for output digits that have become fixed
        while (true) {
            var i1 = (b * modOut).Floor();
            var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
            if (i1 != i2) break; //digit became fixed?
            //fix the next output digit (rescale the range to make next digit range from 0 to 1)
            d *= modOut;
            b *= modOut;
            b -= i1;
            yield return i1;
        }
    }
}

"fakat bu aşırı derecede muhtemel değil" - bu olayın olasılığı 0; biz "neredeyse kesin" olmadığını söylüyoruz.
Raphael

2

Bir ikili ondalık oluşturun. Açıkça depolamak yerine, minimum ve maksimum olası değerleri takip edin. Bu değerler aynı tam sayı içinde bulunduğunda, tam sayıyı döndür. Kodun taslağı aşağıdadır.

(Düzenleme) Fuller açıklaması: Her biri 1/3 olasılık içeren, 1'den 3'e kadar rasgele bir tamsayı oluşturmak istediğinizi söyleyin. Bunu, (0, 1) aralığında rastgele bir ikili ondalık real, x oluşturarak yaparız. Eğer x <1/3, 1 ise, yoksa x <2/3 2 ise, 3 döndürür. Açıkça x için rakam üretmek yerine, sadece x'in minimum ve maksimum değerlerini takip ederiz. Başlangıçta, x'in min değeri 0 ve maks. 1'dir. Önce kafaları çevirirseniz, ondalık basamağın arkasında (ikili olarak) ilk x basamağı 1 olur. X'in olası minimum değeri (ikili olarak) 0.100000 olur. = 1/2 ve maks = 0.111111111 = 1. Şimdi bir sonraki çeviriniz kuyruklarsa x, 0.10 ile başlar. Mümkün olan minimum değer 0.1000000 = 1/2 ve maksimum değer 0.1011111 = 3/4'tür. En düşük olası x değeri 1 / 2'dir. x i 1 / 3'ü gerektirdiğinden 1'i geri döndürme şansı yoktur. Eğer x 1/2 <x <2/3 ise 3, eğer 2/3 <x <3/4 ise yine 2 alabilirsiniz. Şimdi üçüncü çevirinin kuyruk olduğunu varsayalım. O zaman x, 0.100 ile başlamalıdır. Min = 0.10000000 = 1/2 ve maks. = 0.100111111 = 5/8. Şimdi 1/3 <1/2 <5/8 <2/3'ten beri, x'in aralık içinde (1/3, 2/3) düşmesi gerektiğini biliyoruz, bu nedenle x'in rakamlarını üretmeyi durdurabilir ve sadece 2 döndürür.

Kod, temelde bunu, 0 ile 1 arasında x üretmek yerine, a ile b arasında x üretir, ancak prensibi aynıdır.

def gen(a, b):
  min_possible = a
  max_possible = b

  while True:
    floor_min_possible = floor(min_possible)
    floor_max_possible = floor(max_possible)
    if max_possible.is_integer():
      floor_max_possible -= 1

    if floor_max_possible == floor_min_possible:
      return floor_max_possible

    mid = (min_possible + max_possible)/2
    if coin_flip():
      min_possible = mid
    else:
      max_possible = mid

Not: Bu kodu kabul et / reddet yöntemine karşı test ettim ve her ikisi de tek biçimli dağılımları verdim. Bu kod, b - a'nın sonraki 2 gücüne yakın olması dışında, reddetmeyi kabul etmekten daha az bozuk para basması gerektirir. Bu kodun ortalama olarak kabul / reddetmeden 2 jeton fişi kullanmayacağını kanıtlayabildim. Okumamdan, Knuth ve Yao (1976) bu sorunu çözmek için bir yöntem verdiler ve yöntemlerinin beklenen madeni para basma sayısında en uygun olduğunu kanıtladılar. Ayrıca, beklenen sayısının dağılımın Shannon entropisinden daha büyük olması gerektiğini de kanıtladılar. Ancak, metnin bir kopyasını bulamadım ve yöntemlerinin ne olduğunu görmek isterdim. (Güncelleme: burada Knuth Yao 1976'nın bir tanıtımını buldum:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf ama henüz okumadım). Ayrıca birileri daha genel olduğu anlaşılan ve önyargılı bir madeni para kullanarak çözen Han Hoshi'den de bahsetti. Literatür hakkında iyi bir tartışma için ayrıca http://paper.ijcsns.org/07_book/200909/20090930.pdf , Pae (2009) tarafından bakınız .


1

Basit cevap?

Eğer b-bir 2 bir güç değil, sonra ürettikten sonra r, aralık içinde olup olmadığını kontrol edin ve değilse tekrar oluşturun.

Yeniden oluşturmanız gerekecek en muhtemel zaman r ne zaman b-bir=2n+1 bazı tamsayılar için n, ancak o zaman bile, her nesilde menzile girme olasılığı% 50'den fazla olacaktır.


Bu tam görünmüyor.
Dave Clarke

1

Bu, b - a'nın 2 ^ k'ye eşit olmadığı durum için önerilen bir çözümdür. Sabit sayıda adımda çalışması gerekiyor (beklenen aralığın dışındaki adayları atmaya gerek yok).

Ancak bunun doğru olduğundan emin değilim. Lütfen bu rasgele sayı üretecindeki (eğer varsa) tam tekdüzelikliği ve nasıl ölçüleceğini / ölçüleceğini tarif etmenize yardım edin.

Öncelikle [0, z-1] aralığında homojen dağılmış rasgele sayılar üretme eşdeğer sorununa dönüşür, z = b - a.

Ayrıca, m = 2 ^ k'nin 2> = z en düşük gücü olmasına izin verin.

Yukarıdaki çözeltiye göre, zaten [0, m-1] aralığında tek biçimli bir şekilde dağıtılmış rasgele sayı üretecine sahip R (m) 'ye sahibiz (her bit için bir tane olmak üzere k sikkelerinin atılmasıyla yapılabilir).

    Keep a random seed s and initialize with s = R(m).   

    function random [0, z-1] :
        x = R(m) + s 
        while x >= z:
            x -= z
        s = x
        return x

While döngüsü en fazla 3 kez çalışır ve sabit adımda bir sonraki rasgele sayı verir (en iyi durum = en kötü durum).

Buradaki sayılar [0,2] için bir test programına bakın: http://pastebin.com/zuDD2V6H


Bu tek tip değil. almakz=3. Sen almakm=4 ve olasılıklar 1/2,1/4,1/4.
Yuval Filmus

Lütfen sözde koda ve bağlantılı koda daha yakından bir göz atın. Neredeyse eşit frekansta 0, 1 ve 2 yayar ...
vpathak

Haklısın, çıkışlara ayrı ayrı baktığınızda, bunlar tekdüze (durağan olasılık tekdüze). Ama bağımsız değiller. Eğer sadece çıktınız varsa0, diyelim, sonra bir sonraki çıktı olasılıkla sıfırdır 1/2 ve olasılıkla bir veya iki 1/4her.
Yuval Filmus

Tüm işlevi tek bir satırla değiştirebilirsiniz: return s = (s + R (m))% z;
Yuval Filmus

1

Teorik olarak en uygun algoritma

İşte gönderdiğim diğer cevapların bir gelişimi. Diğer cevap, birinden diğerinden ayrı bir dağılım oluşturmak için daha genel olan duruma genişletmenin daha kolay olması avantajına sahiptir. Aslında, diğer cevap Han ve Hoshi kaynaklı algoritmanın özel bir örneğidir.

Burada tarif edeceğim algoritma Knuth ve Yao'ya (1976) dayanmaktadır. Makalelerinde, bu algoritmanın mümkün olan en az beklenen madeni para basma sayısına ulaştığını da kanıtladılar.

Bunu göstermek için, diğer cevaplarla açıklanan Reddetme örnekleme yöntemini göz önünde bulundurun. Örnek olarak, eşit olarak 5 sayıdan birini oluşturmak istediğinizi varsayalım [0, 4]. 2'nin bir sonraki gücü 8'dir, bu nedenle madalyonu 3 kez çevirir ve 8'e kadar rastgele bir sayı oluşturur. Sayı 0 ila 4 ise, o zaman geri verirsiniz. Aksi takdirde, onu atıp 8'e kadar başka bir sayı oluşturup başarılı oluncaya kadar tekrar deneyin. Ama sayıyı attığın zaman, biraz entropi harcıyorsun. Bunun yerine attığınız numaraya, beklenti içinde ihtiyaç duyacağınız gelecekteki jeton atma sayısını azaltmak için şart koyabilirsiniz. Somut olarak, [0, 7] sayısını oluşturduğunuzda, eğer [0, 4] ise, geri dönün. Aksi takdirde, 5, 6 veya 7 olur ve her durumda farklı bir şey yaparsınız. 5 ise, yazı turayı tekrar çeviriniz ve yazı tipine göre 0 ya da 1 döndürünüz. 6 ise, yazı turasını çevirin ve 2 veya 3 döndürün. eğer kafalarsa, 4 döndür, eğer kuyruklar yeniden başlarsa.

İlk başarısızlık girişimimizden kalan entropi bize 3 vaka (5, 6 veya 7) verdi. Eğer bunu bir kenara atarsak, log2 (3) yazı turalarını fırlatıp atarız. Bunun yerine onu saklıyoruz ve 6 muhtemel vaka (5H, 5T, 6H, 6T, 7H, 7T) oluşturmak için başka bir çevirinin sonucuyla birleştiriyoruz. .

İşte kod:

# returns an int from [0, b)
def __gen(b):
  rand_num = 0
  num_choices = 1

  while True:
    num_choices *= 2
    rand_num *= 2
    if coin.flip():
      rand_num += 1

    if num_choices >= b:
      if rand_num < b:
        return rand_num
      num_choices -= b
      rand_num -= b

# returns an int from [a, b)
def gen(a, b):
  return a + __gen(b - a)
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.