16 Temmuz 2018 Pazartesi

C Dili rand metodu - Kullanmayın

Giriş
C ile srand() ve rand() metodları kullanılarak rastgele sayılar üretilebilir. Üretici ilklendirmek için srand() metodu yazısına bakabilirsiniz.

rand artık kullanılmamalı
Açıklaması şöyle.
Use of rand therefore continues to be non-portable, with unpredictable and oft-questionable quality and performance.
rand ve global state
rand() metodu global state kullanır. Bu yüzden önceden belirlenmiş bir diziye ihtiyacımız varsa kesinlikle kullanılmamalıdır.

rand Gerçekleştirimi Nasıldır?
rand() Gerçekleştirimi İçin Algoritmalar yazısına taşıdım.

Aynı Sayı Tekrar Üretilebilir mi ?
Sorunun cevabı evet.

Örnek
Şöyle yaparız
assert(rand() != rand());
Bu kodda koşulunun sağlanmadığı görülebilir.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
  unsigned int i;
  for(i = 0; ; i++) {
    int r = rand();
    if (r == rand()) {
        printf("Oops. rand() = %d; i = %d\n", r, i);
        break;
    }
  }
  return 0;
}
Örnek
rand()'ın ne kadar çift sayı ürettiğini görmek için şöyle yaparız
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main() {
  size_t size = ((size_t)RAND_MAX) + 1;
  char *randoms = calloc(size, sizeof(char));
  int dups = 0;
  srand(time(0));
  for (int i = 0; i < RAND_MAX; i++) {
    int r = rand();
    if (randoms[r]) {
      // printf("duplicate at %d\n", r);
      dups++;
    }
    randoms[r] = 1;
  }
  printf("duplicates: %d\n", dups);
}
rand() ne Döndürür
rand() metodu genellikle 15 bitlik sayılar üretir. 15 bit olması garanti değildir. Bu yüzden RAND_MAX macrosunu kullanmak gerekir. Yani rand() 0 - RAND_MAX arasında bir sayı üretir, negatif sayı üretmez.

RAND_MAX'ın en az 32767 değerine eşit olması garanti edilmiştir. Windows üzerinde RAND_MAX 32767 (5 haneli rakam) değerine eşittir. Linux üzerinde ise 2147483647 (10 haneli rakam) değerine eşittir.

How to store a random number into a null-terminated character array? sorusunda uzunluk farkına dikkat çekiliyor.

rand() Thread Safe Midir ?
Posix sistemlerde rand() meteodu thread-safe değildir.
The function rand() is not reentrant or thread-safe, since it uses hidden state that is modified on each call.
Bu yüzden aşağıda anlatılan rand_r() metodunu (bilinçsiz kullanılırsa yine thread-safe olmaz!) kullanmak gerekir.Windows'ta ise rand_s()'i kullanmak gerekir.

Üretilen Sayı Dağılımı Nasıldır?
Bir diğer önemli nokta ise rand() Uniform Distribution (tek düze dağılım) sayı üretir. Bir üretecin Uniform Distribution olduğunun nasıl ispatlandığını anlamak için buraya göz atabilirsiniz
.
Dağılım Sınıfları Var Mıdır?
Dağılımı ayarlamak için bir çok kütüphane dağılım sınıfları sunmakta. Maalesef C kütüphanesinde dağılım sınıfı yok.

Dağılımda Bias'a Dikkat
Şu kod tavsiye edilmiyor. Bu tarz kodlar yerine std::uniform_int_distribution sınıfını kullamak daha iyi.
int x = 7;
while(x > 6) 
  x = 1 + std::rand()/((RAND_MAX + 1u)/6);  // Note: 1+rand()%6 is biased
Uniform Real Distribution
Şöyle yaparız.
double probability = (rand()/(double)(RAND_MAX + 1));

Ağırlığa Göre Dağılım
Örnek
Şöyle yaparız. %33 oranında BLUE, %66 oranında ise WHITE çıkan bir dağılım var. rand() %3 [0,2] arasında bir değer döner. 0 gelirse (%33) BLUE, 1 ve iki gelirse (%66) oranında WHITE döner.
#define BLUE 1
#define WHITE 2

int whichBall()
{
  int val = rand() % 3;
  if (val == 0)
    return BLUE;
  return WHITE;
}
Örnek
Benzer bir şeyi Java'da şöyle yaparız. 0 - 1000 arasında sayı ütetilir. Bu sayı 10 ile bölünerek 0 - 100 arasına getirilir. %60'ı 0 olur, %40'ı ise 10 ile gölünmüş hali olur.
Stream<Integer> boxed = random.ints(0, 1000).map(r -> r%10 < 6 ? 0 : r/10).boxed();
Random Byte Üretmek
Kriptografik üreteçlerde olduğu gibi random byte üretmek için şöyle yaparız.
#include <stdlib.h>
#include <limits.h>
#include <math.h>

/* Assumes srand() has been called with an appropriate seed at some point
   Code assumes C99 is available; minor tweaks needed for older compilers.
 */
int gen_random_int() {
  const int BITS_PER_RAND = (int)log2(RAND_MAX + 1);
  int ret = 0;
  for (int i = 0; i < sizeof(int) * CHAR_BIT; i += BITS_PER_RAND) {
    ret <<= BITS_PER_RAND;
    ret |= rand();
  }
  return ret;
}


Hiç yorum yok:

Yorum Gönder