Menu

C'de Rastgele Sayılar: rand, srand ve Bir Aralıkta Sayı Üretmek

C'de rand() ve RAND_MAX ile rastgele sayıların nasıl üretileceği, neden tam olarak bir kez srand(time(NULL)) ile tohumladığınız, % ile bir aralığa eşlemek ve getirdiği küçük yanlılık, rastgele double'lar ve yeniden üretilebilir diziler.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

C'nin rastgele sayıları <stdlib.h> içindeki iki fonksiyondan gelir: sonraki değeri üreten rand() ve başlangıç noktasını ayarlayan srand(). Gerçekten rastgele değildirler - bir tohumdan belirlenimci biçimde hesaplanan sözderastgele bir dizidir - ki bu kriptografi için bir kısıtlama, test için bir özelliktir.

rand() ve RAND_MAX

rand(), 0 ile RAND_MAX arasında (ikisi dahil) bir yerde bir int döndürür. RAND_MAX, en az 32767 olması garanti edilen bir makrodur; Linux ve macOS'ta 2147483647'dir.

Bunu iki kez çalıştırın. Sayılar her iki seferde de özdeştir - ve bu bir hata değildir.

srand ile Tohumlamak

srand çağrısı olmadan dizi, srand(1) çağırmışsınız gibi davranır. Aynı tohum, aynı dizi, her çalıştırmada. Çalıştırma başına farklı sayılar almak için değişen bir şeyle tohumlayın - geleneksel olarak mevcut zamanla:

<time.h>'deki time(NULL), 1970 başından beri geçen saniyeleri döndürür, dolayısıyla her çalıştırma farklı bir tohum alır. unsigned int dönüşümü, time_t'nin daraltılmasıyla ilgili bir uyarıyı susturur.

Tohumlamayla ilgili, insanların hepsini yanlış yaptığı üç kural:

Tam olarak bir kez tohumlayın, main'in başında. Her rand()'dan önce srand çağırmak klasik anti-kalıptır - bir saniyeden kısa sürede biten bir döngünün içinde time(NULL) her yinelemede aynı değeri döndürür, dolayısıyla aynı sayıyla yeniden tohumlarsınız ve rand() her seferinde aynı ilk değeri döndürür. Çıktı, özdeş "rastgele" sayılardan oluşan bir sütundur.

Rastgeleliği "iyileştirmek" için yeniden tohumlamayın. Üretecin kalitesi iç durumunun ilerlemesinden gelir; o durumu sıfırlamak diziyi çöpe atar.

time(NULL)'ın çözünürlüğü bir saniyedir. Aynı saniyede başlatılan iki program aynı diziyi alır. Bu bir oyun için sorunsuz, bağımsızlığın önemli olduğu her yer için yanlıştır.

Bir Aralıkta Sayı

Standart deyim kalan operatörünü kullanır:

rand() % n            /* 0 ile n-1 arası */
rand() % n + min      /* min ile min+n-1 arası */

min ile max arasını (ikisi dahil) almak için olası değer sayısı max - min + 1'dir:

+ 1, bir-eksik/bir-fazla hatalarının yaşadığı yerdir. rand() % 6, 0 ile 5 arasını verir, dolayısıyla bir zar atışı rand() % 6 + 1'dir. "6'yı dahil etmek" için rand() % 7 + 1 yazmak size yedi yüzlü bir zar verir.

Mod Yanlılığı Hakkında Dürüst Bir Not

rand() % n, n, RAND_MAX + 1'i tam bölmedikçe kusursuz biçimde düzgün değildir.

Bunu küçük sayılarla düşünün. RAND_MAX 9 olsaydı - yani rand() eşit olasılıklı on değer olan 0 ile 9 arasını döndürseydi - rand() % 3, 0,3,6,9'u 0'a; 1,4,7'yi 1'e; ve 2,5,8'i 2'ye eşlerdi. Sonuç 0, ondan dört yolla; sonuç 1 ve 2 ise üçer yolla ortaya çıkar. Sıfır %33 daha olasıdır.

Aynı sapma gerçek RAND_MAX ile de vardır, yalnızca çok daha küçüktür: artan değerler ilk (RAND_MAX + 1) % n sonuçtur ve her biri kabaca 2,1 milyarda fazladan bir şans kazanır. Bir zar atışı, karılmış bir deste ya da bir benzetim için bu ölçülemez - % kullanın ve devam edin.

Önemli olduğu yerde - istatistiksel işler, güvenlikle ilgili her şey - artan değerleri içeri katmak yerine reddedin:

Döngü, sapmaya yol açacak küçük değer aralığını çöpe atar ve yeniden çeker. Hızla sonlanır - reddedilen dilim bütünün yok olacak kadar küçük bir kesridir.

Gerçekten güvenlik açısından hassas rastgelelik için rand(), ne kadar dikkat gösterirseniz gösterin yanlış araçtır: macOS ve BSD'de arc4random_buf, Linux'ta getrandom() ya da Windows'ta BCryptGenRandom kullanın.

Rastgele Double'lar

[0.0, 1.0] aralığına inmek için RAND_MAX'a bölün, sonra ölçekleyin:

(double) rand() dönüşümü şarttır. Onsuz rand() / RAND_MAX tam sayı bölmesidir ve neredeyse her zaman 0, iki milyarda bir maksimuma çarpma ihtimalinde 1 değerini alır - "rastgele double'larımın hepsi sıfır" gibi görünen bir hata. Nedeni için bkz. tür dönüşümü.

Yeniden Üretilebilir Diziler

Sabit bir tohum her çalıştırmada özdeş diziyi verir ki bu bir test, bir hata ayıklama oturumu ya da paylaşılabilir seviye kodları olan bir oyun için tam olarak istediğiniz şeydir:

42 tohumu, kullanıldığı her seferde - bu çalıştırmada ve aynı kütüphanedeki başka herhangi birinde - aynı beş sayıyı üretir. O yeniden üretilebilirlik, bir benzetimin tohumun seçilmesine izin vermesi gerektiğinin sebebidir: normalde saatle çalıştırın, bir hatayı yeniden üretirken sabit bir tohum geçirin.

Bir uyarı: belirli bir tohum için dizi taşınabilir değildir. Farklı C kütüphaneleri farklı üreteçler kullanır, dolayısıyla glibc'de 42 tohumu ile Windows'ta 42 tohumu farklı sayılar verir. Tek bir makinede yeniden üretilebilir, makineler arasında değil.

Bir Zar Oyunu

Hepsi bir arada - bir kez tohumlama, aralık için bir yardımcı ve sonuçları sayan bir dizi:

Histogram 7'de tepe yapmalı ve 2 ile 12'ye doğru incelmelidir - 7 yapmanın altı yolu, 2 ya da 12 yapmanın birer yolu vardır. Burada düz bir dağılım üreten bir üreteç bozuk olurdu.

İlgili iki sayfa: standart kütüphane, <stdlib.h>'nin gerisini haritalıyor ve matematik fonksiyonları, rastgele değerler gerçek hesaplara beslenir beslenmez isteyeceğiniz <math.h>'yi ele alıyor.

Sıkça Sorulan Sorular

C'de nasıl rastgele sayı üretirim?

<stdlib.h>'yi ekleyin, main'in başında bir kez srand((unsigned) time(NULL)) ile tohumlayın (bu <time.h> ister), sonra her değer için rand() çağırın. rand(), 0 ile RAND_MAX arasında (ikisi dahil) bir int döndürür.

C'de iki değer arasında nasıl rastgele sayı alırım?

rand() % (max - min + 1) + min kullanın. 1 ile 6 arasında bir zar atışı için bu rand() % 6 + 1'dir. % n, sonucu 0..n-1 aralığına eşler ve min eklemek pencereyi kaydırır - yalnızca sayının her iki uç noktayı da içerdiğinden emin olun; + 1'in yaptığı budur.

C programım neden her seferinde aynı rastgele sayıları yazdırıyor?

Çünkü srand'ı hiç çağırmadınız. Tohum olmadan rand(), 1 ile tohumlanmış gibi davranır, dolayısıyla her çalıştırma özdeş diziyi üretir. Program başlangıcında bir kez srand((unsigned) time(NULL)) çağırın - bir kez, her rand() çağrısından önce değil; o işleri daha da kötüleştirir.

Rastgele sayı üretiminde mod yanlılığı nedir?

rand() % n, yalnızca n, RAND_MAX + 1'i tam böldüğünde kusursuz biçimde düzgündür. Aksi hâlde ilk birkaç değer tüm aralıkta fazladan bir kez ortaya çıkar ve çok az daha olası olur. RAND_MAX 2147483647 iken ve küçük bir n ile sapma, bir oyunun ya da benzetimin fark edeceğinin çok altındadır; ama kriptografi ya da istatistik için bir reddetme döngüsü ya da düzgün bir üreteç kullanın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA