Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

C#’ta Rastgele Sayı Üretimi Sorunsalı

Merhaba,

Yazılım olgusu, bilgisayara kullanıcının isteği doğrultusunda algoritmik bir senfoni eşliğinde komutlar ile taleplerde bulunulan ve bu taleplere karşı verilen cevaplara yahut olayların gidişatı kontrolünde beklenen durumlara göre süreci amaca uygun bir şekilde işleterek bir veriyi doğru ve beklendiği şekilde işleyebilme durumudur. Evet, olaya biraz kuramsal yaklaşmak istemiş olabilirim. Ama bu yaklaşımın ardından bilgisayar denen aygıtın hala günümüzde rastgele bir sayı üretecek kadar gelişmemiş olması gerçeği ise bu girişi sanırım oldukça trajikomik bir hale getirmektedir.

Yazılımlar insanların yahut belirli makinelerin ihtiyaçlarına cevap verebilecek donanımı kendilerinde dolaylı yoldan barındırabilirken, bu yazılımı kullanan ve kendine o anlık zihinsel karar mekanizması olarak tanıyan bilgisayar denen aygıt nasıl oluyor da bir rastgele sayı üretebilecek kapasitede olamıyor şaşılacak şey değil!

Evet, piyasada dolanan sorulara istinaden kaleme(doğrusu klavyeye) almış olduğum bu makalemizde rastgele sayı üretimi üzerine hala fikir birliğine varılamayan birkaç husus üzerine elden geldiğince aydınlatıcı bir teori ve açıklamada bulunacağım…

Tabi öncelikle bir bilgisayarda, hazır Random sınıfı olmasaydı nasıl rastgele sayı üretebiliriz bu durum üzerine istişare etmekte fayda görüyorum.

Şimdilik aklıma iki ama etkili yöntem gelmektedir.

Bunlardan birincisi, rastgele üretilecek sayıları önceden bir koleksiyon içerisinde tutarak istendiği zaman bunlardan herhangi birini istemciye göndermek olabilirdi. Biliyorum çok vasat ama etkili ve net çözüm 🙂

İkinci yöntem ise, bağıntısı oluşturulduğu zaman muhtemelen bir benzer sonucu üretemeyecek bilgisayarın tarihsel verileri üzerinden yapılacak çalışmalar neticesinde üretilen sayılardır. Örneğin; bu günün tarihi verilerinden, saat ve milisaniye çarpım sonucunu yıla bölerek çıkan sonuca ay değerinin 3/4 alıp, karesini eklersek eğer karşımıza çıkan sonucun bir saniye sonra bu işlemin aynısını yaptığımızda çıkan sonuçla dağlar kadar farklı olduğunu görebiliriz. İşte size sonsuza denk farklı sonuc üretecek bir algoritma.

Zaten .NET mimarisinde ki Random sınıfıda sistemdeki tarih bilgilerini kullanarak ortaya rastgele bir sayı üretmektedir. İşte tüm meselenin sırrı budur. Hangi meselenin hoca la? Birazdan piyasada dolaşan Random sayı üretimiyle ilgili sıkıntıları masaya yatıracağız. Dolayısıyla bu sıkıntılardan birinin kaynağı Random sınıfının sistemdeki tarih bilgisini kullanmasından kaynaklandığını peşinen belirtiyorum.

Velhasıl madem lafını etmişken ilgili sıkıntıyı ilk olarak ele alalım.

#Soru 1#
Random Sınıfı Sürekli Aynı Sayıyı Üretiyor
Bu sorunun temel kaynağı yukarıda da belirttiğim gibi Random sınıfının sistemdeki tarih bilgisini kullanıyor olmasıdır. Bir diğer kaynağı ise Random sınıfının kullanım tarzıdır. Şimdi bu durumları programatik olarak ele alacağız;

            int counter = 1;
            while (counter < 100)
            {
                Random rst = new Random();
                Console.WriteLine(rst.Next());
                counter++;
            }

Yukarıdaki kod bloğunun ekran çıktısı aşağıdaki gibi olacaktır.
C#'ta Rastgele Sayı Üretimi Sorunsalı
Evet… Kodu ve neticeyi incelersek eğer görünen o ki beklenen sonuç elde edilememiş ve üretilen sayılar birbirlerini tekrar eden cinsten artık rastgele değil tahmin edilebilir bir vaziyette elde edilir olmuş.

Yukarıda bahsettiğim gibi Random sınıfı tarih bilgilerini kullanarak bir değer üretmektedir. Dolayısıyla bu işlemi constructer metoduyla gerçekleştirmektedir. Eğer ki Random sınıfının parametresiz constructerını kullanırsanız ilgili sınıf direkt olarak sistem zamanını değerlendirecek ve tarihsel bilgilerden bir bağıntı ile rastgele sayı üretecektir. Ama burada while döngüsü zamandan oldukça hızlı çalıştığından dolayı sürekli aynı zaman üzerine odaklanılacak ve o an ki milisaniye değeri değişene denk aynı sonuç üretilecektir.

Soruna sebep olan diğer durum ise Random sınıfını kullanım tarzıdır. Dolayısıyla yukarıdaki kullanım tarzı her bir döngüde yeni bir Random nesnesi talep edecek ve izahatte bulunduğumuz hataya sebebiyet verecektir. O halde algoritma inşasını aşağıdaki gibi düzeltmemiz hatayı ortadan kaldıracaktır.

            int counter = 1;
            Random rst = new Random();
            while (counter < 100)
            {
                Console.WriteLine(rst.Next());
                counter++;
            }

En nihayetinde Random sınıfını bir kere üretip ardından döngü ile var olan nesne üzerinden rastgele sayı talebinde bulunursak eğer her bir döngü için farklı bir değer üretilecektir.
Elde edilen sonuç;
C#'ta Rastgele Sayı Üretimi Sorunsalı

Buradan çıkaracağımız sonuç Random sınıfının başlangıç değeri olarak neyi kabul ettiğidir. Eğer ki, döngü dışında üretilen Random nesnesine constructer aracılığıyla bir seed değeri verilirse sanki döngü içerisinde sürekli aynı zaman değerlerinde üretilen Random nesnesiymiş gibi yine aynı hatayla karşılaşılacaktır. Ama bu sefer üretilen değerler farklı olsada her periyotta aynı olacaktır.

Anlatmaya çalıştığımı örneklendirmek için;

            int counter = 1;
            Random rst = new Random(1111);
            while (counter < 10)
            {
                Console.WriteLine(rst.Next() + " ");
                counter++;
            }

komutunu iki kere çalıştırmamız yeterlidir.
C#'ta Rastgele Sayı Üretimi Sorunsalı
Bu şekilde bir kullanım ile ard arda çalıştırmalarda sürekli aynı sonuçları aldığımızı görüyoruz.

1. Çalıştırma 2. Çalıştırma
302295548
1729603735
738047416
1488758225
1732353655
986442359
1496834943
1747084034
916554424
302295548
1729603735
738047416
1488758225
1732353655
986442359
1496834943
1747084034
916554424

Neticede Random sınıfı, aslında rastgele değerleri sade ve sadece parametresiz constructerı çalıştırırken ürettiğini söyleyebiliriz. Gerisi, üretilen ilk sayıya ucundan kıyısından bir oranda bağlı olarak üretilen sayılardır diyebiliriz. Bunun dışında parametre olarak seed değeri verildiğinde üretilen sayılar kanımca sıkı ve dikkatli bir takiple tahmin edilebilir olduğu kanaatindeyim.

#Soru 2#
Tekrarsız Rastgele Sayı Üretmek İstiyorum
Aslında basit bir mantıkta çözüm üretebileceğimiz bir sorundur. Yapılması gereken üretilen değerleri bir koleksiyonda toplamak ve önceki değerler arasında varsa koleksiyona eklemeksizin yerine yeni değer üretip aynı kontrol eşliğinde tekrar koleksiyona atmak olacaktır.

Tabi bu işlemi yaparken yöntem olarak doğru seçimde bulunmak oldukça kritik arz etmektedir. Nihayetinde milyarlarca sayı için bu işlemi yaptığınızı düşünürseniz performans ve maliyet açısından lüzumsuz yere bonkör davranmanız söz konusu olabilir.

Bu tarz işlemlerde HashSet koleksiyon tipini kullanmanız en doğrusu olacaktır. HashSet koleksiyonunun kullandığı kovalama yöntemi sayesinde daha performanslı bir şekilde bu işlemi gerçekleştirebiliriz. Ayriyetten ilgili koleksiyon ISet interfaceini implement ettiğinden dolayı yeni üretilen değeri koleksiyondaki önceki değerlerle kıyaslama gibi bir derdimiz kalmayacaktır. Nihayetinde ISet interfaceini uygulayan koleksiyonlarda verile unique’dir ya da bir başka deyişle aynı elemandan birden fazla bulunmayacaktır. Eklenmeye çalışılan eleman koleksiyonda mevcutsa eğer Add metodu false değeri döndürecektir.

Şimdi gelin bir normal koleksiyon ile bir de HashSet koleksiyonu ile buradaki ihtiyaca dönük işlevlerimizi gerçekleştirelim.
Normal koleksiyon ile;

            int counter = 1;
            List<int> sayilar = new List<int>();
            Random rst = new Random();
            while (counter < 100)
            {
                int rstSayi = rst.Next();
                if (!sayilar.Contains(rstSayi))
                    sayilar.Add(rstSayi);
                counter++;
            }

HashSet ile;

            int counter = 1;
            HashSet<int> sayilar = new HashSet<int>();
            Random rst = new Random();
            while (counter < 100)
            {
                sayilar.Add(rst.Next());
                counter++;
            }

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

6 Cevaplar

  1. Umut Sun dedi ki:

    Offf gerçekten yazı o kadar ilgimi çekti ki anlatamam elinize sağlık sitenizi favori sitelerime ekledim takipteyim

  2. Furkan dedi ki:

    Selam bir şey sormak istiyorum bir sınıfım var bu sınıfın kurucu fonksiyonunda rastgele sayı ürettiyorum ve ürettiği sayıyı sınıf içindeki bir elemana eşitliyorum.bunun haricinde bu sınıftan kalıtım alan bir kaç sınıfım daha var.Diğer sınıflardan bir nesne ürettiğimde kalıtım alınan sınıf çağrılıyor ve sayı üretilip sınıflardaki elemana eşitleniyo.fakat sorun şudur ki üretilen sayı tüm diğer sınıflarda aynı oluyor.Mesela Kurucu fonksiyonda üretilen sayı 23 olsun.Televizyon sınıfındaki stok adedi 23 ‘e eşit oluyo, sonra laptop sınıfından bir nesne oluşturuyorum kurucu fonksiyon gene aynı sayıyı veriyo

    • Gençay dedi ki:

      Merhaba,
      İmlaya uymayan mesajlardaki çilenin hali başka 🙂

      Öncelikle aşağıdaki kavramsal düzeltmeyi yapalım;
      “…içindeki bir elemana eşitliyorum…” cümlesi eşitleme olarak değil atama olarak tabir edilir.

      Cevap olarak ise gayet normal bir sonuç olduğunu ifade ederek başlayalım. Nihayetinde kalıtımsal durumlarda bir sınıftan nesne talep ettiğinde öncelikle o sınıfın miras aldığı sınıfların nesneleri hiyerarşik olarak türetilmektedir. Haliyle hangi sınıftan nesne üretirsen üret o sınıfa ismini belirtmediğin ama muhtemelen stok adedi olan property değeri, atalarındaki o ilgili yine ismini belirtmediğin sınıf tarafından gönderileceği için tüm nesnelerde aynı değeri verecektir.

      Sevgiler.

  3. Gül dedi ki:

    merhaba
    Yukarıdaki sorun bende de oluyor.Örneğin televizyon.StokAdedi şeklinde çağırdığımda yine tüm sınıflardaki değerler eşit oluyor cepTelefonu.StokAdedi de aynı şekilde olmuş oluyor.Tek bir değer döndürüyor.

  4. Mehmet YILDIZ dedi ki:

    Süper bir makale, teşekkürler hocam.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.