C ++ rand () neden yalnızca aynı büyüklük sırasına sahip sayılar üretiyor gibi görünüyor?


145

C / C ++ ile yazılmış küçük bir uygulamada, randişlev ve belki tohumla ilgili bir sorunla karşı karşıyayım :

Farklı sıralarda, yani farklı logaritma değerlerinde (2 tabanında) rasgele sayılar üretmek istiyorum. Ama görünen o ki, üretilen tüm sayılar aynı sırada, 2 ^ 25 ile 2 ^ 30 arasında dalgalanıyor.

Çünkü mı rand()artık nispeten büyük sayıdır Unix zamanla ekilir? Neyi unutuyorum rand()Başlangıcında yalnızca bir kez tohumlama yapıyorum main().


7
FWIW öyleyse, C veya C ++ mı? C / C ++ ile gerçekten C ++ kullanabileceğinizi kastediyorsanız ve C'den bahsetmek rastgele ise, belki bu en.cppreference.com/w/cpp/numeric/random/binomial_distribution yardımcı olabilir.
R. Martinho Fernandes

9
Ne yazık ki yanlış ata bahis oynuyordunuz. Tohum senin sorunun olmamalı. Sizin sorununuz yanlış beklenen dağıtımdı. Tarafsız programcı, rand()tekdüze dağıtılmış sayılar getirmeyi bekleyeceğinden (yüksek Google sıralamasına sahip belgeler açıkça öyle söylüyor) Bu sorunun gelecekteki okuyucular için yararlı olduğunu düşünmüyorum. Bu yüzden olumsuz oy kullanın, ancak bunun sizi SO kullanmaktan caydırmasına izin vermeyin.
Emperor Orionii

12
@ doug65536 "... hiçbir sayı tekrarlanmaz" - bu rastgele değil! Rand () zarım, mümkün olan her sayı iade edilene kadar aynı sayıyı asla iki kez geri döndürmeseydi, emekliliğimi barbut masasında finanse edebilirdim.
Chris Gregg

6
@GalacticCowboy Periyodikliği tek tek sayılarla karıştırmayın. Alıntı yaptığınız Wikipedia makalesinden: "tekrarlanan bir sonuç, iç durumu çıktısından daha büyük olabileceğinden, dönemin sonuna ulaşıldığı anlamına gelmez." Bir PRNG'nin bir değer üretmesi ve daha sonra tüm değerler iade edilene kadar bu değeri tekrar üretmemesi garanti edilmesi çok, çok kötü olurdu.
Chris Gregg

12
Doug65536, kimse kavga etmiyor. Sadece doğru bir şekilde yanıldığınızı söylüyorlar. 1 ile 10 arasında bir RAND isteseydim, bir PRNG çok mutlu bir şekilde aşağıdakileri çıkarabilirdi: 2 4 7 2 8 1 5 9 7 3 Birden fazla 2 ve 7'ye rağmen bu tamamen geçerli olacaktır. Sanırım PRNG'yi iPhone'unuzdaki karıştırma özelliği ile karıştırıyorsunuz.
Kıbrıs'ta Rahatlama

Yanıtlar:


479

1 ile 2 30 arasındaki sayıların sadece% 3'ü vardır ve bunlar 2 25 ile 2 30 arasında DEĞİLDİR . Yani, bu oldukça normal geliyor :)

2 için 25 /2 30 = 2 -5 = 1/32 = 0.03125 = 3.125%


36
Evet, iyi nokta! 2 ^ 25 ile 2 ^ 30 arasında 1 ile 2 ^ 25 arasındakinden 31 kat daha fazla sayı vardır :) hızlı yanıt için teşekkürler. O zaman programı yeniden düşünmem gerekiyor. Soru cevaplandı.
Tallaron Mathias

1
@TallaronMathias Bit kaydırma yoluyla >>sayıyı kırpmayı düşünün - bu size daha küçük sayılar verecektir. (Ya da bir modül alarak %.)
Sean Allred

13
Bunun çoğu programcı için açık olmasını bekliyorum: 2 ^ 25'ten küçük herhangi bir işaretsiz tamsayı ilk 7 bitine eşit olmalıdır 0- ve eğer her bit rastgele ise ...
BlueRaja - Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft - olasılıklar açık olsaydı, kumarhaneler işsiz kalırdı.
Brett Hale

26
@BrettHale - Programcıların bir kumarhanenin hedef demografisi olduğunu düşünmüyorum.
EkoostikMartin

271

Daha açık yeşil, 0 ile 2 25 arasındaki bölgedir ; koyu yeşil ise 2 25 ile 2 30 arasındaki bölgedir . Keneler 2'nin katlarıdır.

dağıtım


42

Daha kesin olmalısınız: Farklı temel 2 logaritma değerleri istiyorsunuz, ancak bunun için hangi dağılımı istiyorsunuz? Standart rand () işlevleri tek tip bir dağılım üretir, bu çıktıyı istediğiniz dağıtımla ilişkili kuantil işlevi kullanarak dönüştürmeniz gerekir .

Bize dağıtımı söylerseniz, quantileihtiyacınız olan işlevi size söyleyebiliriz .


13
+1, dağıtım çok önemli bir terimdir. Dağılım hakkında hiçbir şey bilinmiyorken rastgele sayılardan bahsetmek gerçekten mantıklı değil. Üniforma önemli de olsa özel bir durumdur. C ++ 11 standart kitaplığından çeşitli dağıtımlara işaret etmek için iyi bir yer olabilir.
2013

18

Farklı büyüklük dereceleri istiyorsanız, neden sadece denemiyorsunuz pow(2, rand())? Veya Harold'ın önerdiği gibi, sırayı doğrudan rand () olarak mı seçmelisiniz?


3
iyi fikir, ancak cevabınızı ^ yerine pow kullanarak düzeltmelisiniz (C dilinde güç değil, mantıksal xor operatörüdür).
kriss

6
'E rand()kadar çıkabildiğine göre RAND_MAX, gerçekten rastgele sayınızı ölçeklendirmeniz gerekiyor ki sonuç taşmasın ...
Floris

@Floris: ancak çok geniş bir aralıkta küçük bir sayılabilir aralığı ölçeklendirirseniz, LOTS deliğiniz olacaktır, ki bu muhtemelen OP'nin beklediği şey değildir.
André Caron

13

@ C4stor harika bir noktaya değindi. Ancak, daha genel bir durum için ve insan için anlaşılması daha kolay (10 tabanı) için: 1 ile 10 ^ n arasında, sayıların ~% 90'ı 10 ^ (n-1) ile 10 ^ n arasındadır. Sayıların ~% 99'u 10 ^ (n-2) ile 10 ^ n arasındadır. İstediğiniz kadar ondalık sayı eklemeye devam edin.

Komik matematik, bunu n için yapmaya devam ederseniz, bu yöntemle 1'den 10 ^ n'ye, 99.9999 ...% =% 100 sayıların 10 ^ 0'dan 10 ^ n'ye kadar olduğunu görebilirsiniz.

Şimdi kodla ilgili olarak, 0 ile 10 ^ n arasında rasgele büyüklük sırasına sahip rastgele bir sayı istiyorsanız, şunları yapabilirsiniz:

  1. 0'dan n'ye kadar küçük bir rasgele sayı oluşturun

  2. N'nin sahip olduğu aralığı biliyorsanız, büyük bir rasgele 10 ^ k sırası üretin, burada k> maks {n}.

  3. Bu büyük rasgele sayının n basamağını elde etmek için daha uzun rasgele sayıyı kesin.


46
Tamamen haklısınız, ancak GERÇEKTEN kolay anlaşılır bir cevap için, OP kendisine 1 ile 100 arasındaki rastgele sayıların% 90'ının neden iki basamaklı olduğunu sormalıdır.
Monica'yı Sor

13

Temel (ve doğru) cevap yukarıda zaten verilmiş ve kabul edilmişti: 0 ile 9 arasında 10 sayı, 10 ile 99 arasında 90 sayı, 100 ile 999 arasında 900 vb.

Yaklaşık logaritmik dağılıma sahip bir dağılım elde etmenin hesaplama açısından verimli bir yolu için , rastgele sayınızı rastgele bir sayıyla sağa kaydırmak istersiniz:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Mükemmel değil, ancak bilgi işlemden çok daha hızlı pow(2, rand()*scalefactor). Bir faktör 2 içindeki sayılar için dağılımın tekdüze olması anlamında "topaklı" olacaktır (128 ila 255 için tek tip, 256 ila 1023 için yarı yoğunluk, vb.).

İşte 0 ile 31 arasındaki sayıların sıklığının histogramı (1 milyon numunede):

görüntü açıklamasını buraya girin


nitpick: Bu, beklenenden çok küçük sayıları teşvik eder. Sıfır alma olasılığı 10'dan önemli ölçüde daha yüksek.
Mooing Duck 25.09.2013

Pekala - bunun amacı küçük sayıları teşvik etmektir, bu yüzden işe yaradığına sevindim! Bir Monte Carlo simülasyonu çalıştırdım ve bu bana sayılar iki katına çıktıkça olasılıkta 2 faktör düşüşü veriyor - log dağılımından farklı olarak. Bir resimle güncellenmiş cevap.
Floris

hayır, demek istediğim, rand()>>(rand()&31);sezgisel olarak sayıların 1 / 32'sinin 32 bit, 1 / 32'sinin 31 bit ve 1 / 32'nci sayıların 30 bit olması beklenir. Ama bu elde ettiğiniz sonuçlar değil , sayıların sadece 1 / 64'ünden fazlası 32 bit ile sonuçlanırken neredeyse yarısı 0 olmalıdır. bu dışarı.
Mooing Duck

2
Kodunuzun yanlış olduğunu söylemek istemiyorum. Muhtemelen yapacağım şey bu. Sonuçların beklendiği gibi tam olarak dağıtılmadığı konusunda bir uyarıyı hak ediyor .
Mooing Duck

1
Bence sorun, 0'ı 1 bitlik bir sayı olarak düşünmekten kaynaklanıyor ... Bu tam sayıları ve logaritmaları karıştırdığınızda karşılaştığınız türden bir muamma. Yine de iyi bir egzersiz oldu ve bana düşünmem için bir şey verdin. "Algoritmanızın sınırlarını test edin" - asla eskimez.
Floris

5

0 ile 2 ^ 29 ve 2 ^ 29 ile 2 ^ 30 arasında tam olarak eşit sayıda sayı vardır.

Probleme bakmanın başka bir yolu: ürettiğiniz rastgele sayının ikili temsilini, en yüksek bitin 1'in 1 / 2'ye eşit olma olasılığını düşünün ve bu nedenle, yarım durumda 29. sırayı alırsınız. İstediğiniz, 2 ^ 25'in altında bir sayı görmektir, ancak bu, en yüksek 5 bitin hepsinin sıfır olduğu anlamına gelir, bu da 1/32 gibi düşük bir olasılıkla gerçekleşir. Muhtemelen, uzun bir süre çalıştırsanız bile, 15'in altındaki sıralamayı hiç görmeyeceksiniz (olasılık, arka arkaya 6 kez 6 kez yuvarlanmak gibi bir şeydir).

Şimdi, sorunuzun tohumla ilgili kısmı. Hayır, çekirdek muhtemelen sayıların üretildiği aralığı belirleyemez, sadece ilk, ilk öğeyi belirler. Rand () 'ı, aralıktaki tüm olası sayıların bir dizisi olarak düşünün (önceden belirlenmiş permütasyon). Çekirdek, diziden sayıları nereden çizmeye başlayacağınızı belirler. Bu nedenle (sözde) rasgelelik istiyorsanız, diziyi başlatmak için şimdiki zamanı kullanırsınız: Başladığınız konumun tekdüze dağıtılmış olmaması umrunda değil, önemli olan tek şey asla aynı konumdan başlamamanızdır.


2

kullanmak pow(2,rand()) cevapları istenen büyüklük sırasına göre verecektir !!


2

Çevrimiçi bir hizmetten rastgele sayılar kullanmak istiyorsanız bunun için wget kullanabilirsiniz, rastgele sayı üretmeniz için random.org gibi hizmetleri de kullanabileceğinizi görmek isteyebilirsiniz, wget kullanarak onları yakalayabilir ve ardından sayıları okuyabilirsiniz. indirilen dosya

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


SO'ya hoş geldiniz. lütfen bağlantıları yanıt olarak göndermekten kaçının. Bağlantılar aracılığıyla okunacak ayrıntıları bırakarak bir cevabın ayrıntılı bir taslağını sunabilirsiniz.
Shai
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.