C ++ 'da rastgele bir sayı nasıl oluşturulur?


150

Zar ile bir oyun yapmaya çalışıyorum ve içinde rastgele sayılar olması gerekiyor (kalıbın kenarlarını simüle etmek için. 1 ile 6 arasında nasıl yapacağımı biliyorum). kullanma

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

Çok iyi çalışmıyor, çünkü programı birkaç kez çalıştırdığımda, elde ettiğim çıktı:

6
1
1
1
1
1
2
2
2
2
5
2

Bu yüzden her seferinde farklı rasgele bir sayı üretecek bir komut istiyorum , üst üste 5 kez aynı değil. Bunu yapacak bir komut var mı?


56
Dağıtım sorunları bir yana, rastgele sayılarla aynı sonucu arka arkaya birkaç kez alma olasılığının geldiğini unutmayın. Aynı sayıyı arka arkaya iki kez almamanız durumunda sonuçlar gerçekten rasgele olmaz, değil mi?
Kasım'da cdhowie

5
Bu sayıların rastgele olmadığını düşündüren nedir? Gerçekten bir kalıp atın ve çok iyi bir sonuç elde edebilirsiniz. Her atış arasında farklı olmaları garantilendiyse, rastgele olmazdı.
mattjgalloway

2
Ayrıca , modül operatörünü kullanmanın neden iyi bir fikir olmadığının eternallyconfuzzled.com/arts/jsw_art_rand.aspx adresini de okuyun .
Benjamin Bannier

4
Bir yoruma veya hatta bir cevaba sığabilecek birden fazla yanlış anlama yapıyorsunuz. Bağımsız olarak, sahte rasgele sayı üreteçleri, tohumlar hakkında, gerçekten rastgele bir tohum seçmenin önemi ve düzgün dağılımlar hakkında bilgi edinmeniz gerekir.
Kerrek SB

20
Zamanla tohumladığında. Bu ayrıca, programınızı saniyede birden fazla çalıştırırsanız aynı sayıyı alacağınız anlamına gelir.
Martin York

Yanıtlar:


79

Test uygulamanızın en temel sorunu, bir srandkez aramanız ve sonra randbir kez aramanız ve çıkmanızdır.

srandFonksiyonun tüm noktası, rasgele bir tohumla sözde rasgele sayılar dizisini başlatmaktır .

Bu geçtiğiniz takdirde demektir aynı değeri için srand(aynı olan iki farklı uygulamalarda srand/ randuygulaması) daha sonra tam olarak aynı diziyi alacak ait rand()değerleri her iki uygulamada da bundan sonra okuyun.

Bununla birlikte, örnek uygulamanızda sözde rasgele sıra yalnızca tek bir öğeden oluşur - tohumdan üretilen sözde rasgele sıralamanın ilk öğesi, geçerli secondhassasiyet zamanına eşittir . Peki çıktıda ne görmeyi bekliyorsunuz?

Açıkçası uygulamayı aynı saniyede çalıştırdığınızda - aynı tohum değerini kullanıyorsunuz - sonuçlarınız elbette aynıdır (Martin York'un soruya bir yorumda daha önce bahsettiği gibi).

Aslında srand(seed)bir kez aramalı ve sonra rand() birçok kez aramalı ve bu diziyi analiz etmelisiniz - rastgele görünmelidir.

DÜZENLE:

Ah anladım. Görünüşe göre sözlü açıklama yeterli değil (belki dil engeli veya başka bir şey ... :)).

TAMAM. Soruda srand()/rand()/time()kullanılan işlevlerin aynısını temel alan eski moda C kodu örneği :

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^ ^ ^ Programın tek bir işleminden bu dizinin rastgele görünmesi bekleniyor.

EDIT2:

C veya C ++ standart kütüphanesini kullanırken, şu andan itibaren gerçekten rastgele veri üreten (standart tarafından garanti edilen) tek bir standart fonksiyon veya sınıfın olmadığını anlamak önemlidir. Bu soruna yaklaşan tek standart araç, maalesef hala gerçek rastgelelik garantisi vermeyen std :: random_device'dir .

Uygulamanın doğasına bağlı olarak, gerçekten gerçekten rasgele (öngörülemeyen) verilere ihtiyacınız olup olmadığına karar vermelisiniz. Kesinlikle gerçek rastgelelik istediğinizde dikkate değer bir durum bilgi güvenliğidir - örneğin simetrik anahtarlar, asimetrik özel anahtarlar, tuz değerleri, güvenlik belirteçleri vb.

Ancak güvenlik sınıfı rasgele sayılar, ayrı bir makaleye değer ayrı bir endüstridir.

Çoğu durumda Sözde Rastgele Sayı Üreteci yeterlidir - örneğin bilimsel simülasyonlar veya oyunlar için. Bazı durumlarda tutarlı bir şekilde tanımlanmış sözde rastgele sıra bile gereklidir - örneğin oyunlarda çok fazla veri depolamaktan kaçınmak için çalışma zamanında tam olarak aynı haritaları oluşturmayı seçebilirsiniz.

Orijinal soru ve aynı / benzer soruların tekrarlayan çok sayıda (ve hatta onlara yanlış yönlendirilmiş birçok "cevap"), ilk önce ve en önemlisi rastgele sayıları sahte rastgele sayılardan ayırmanın ve sahte rastgele sayı dizisinin ne olduğunu anlamanın önemli olduğunu gösterir. ilk sırada VE sözde rasgele sayı üreteçlerinin gerçek rastgele sayı üreteçlerini kullanabileceğiniz şekilde KULLANILMADIĞINI fark etmek.

Sezgisel olarak rastgele sayı talep ettiğinizde - döndürülen sonuç önceden döndürülen değerlere bağlı olmamalı ve daha önce herhangi bir şey talep edip etmediğine bağlı olmamalı ve hangi anda ve hangi süreçte ve hangi bilgisayarda ve hangi jeneratörden ve hangi jeneratörden ve hangi galaksi istendi. Sonuçta "rastgele" kelimesi ne anlama geliyor - tahmin edilemez ve herhangi bir şeyden bağımsız olmak - aksi halde artık rastgele değil, değil mi? Bu sezgiyle, herhangi bir olası bağlamda bu kadar rasgele bir sayı elde etmek için web'de bazı sihirli büyüleri aramak doğaldır.

Bu tür sezgisel beklentiler , gerçek rastgele sayılar için makul olmasına rağmen , Sahte Rastgele Sayı Üreteçleri içeren tüm durumlarda ÇOK YANLIŞ ve zararlıdır .

"Rastgele sayı" nın anlamlı kavramı mevcut olsa da, "sözde rastgele sayı" diye bir şey yoktur. Bir Sahte Rastgele Sayı Üreteci aslında sahte rastgele sayı dizisi üretir .

Uzmanlar PRNG'nin kalitesi hakkında konuştuğunda, oluşturulan sekansın (ve onun önemli alt sekanslarının) istatistiksel özellikleri hakkında konuşurlar. Örneğin, iki yüksek kaliteli PRNG'yi her ikisini de sırayla kullanarak birleştirirseniz - her biri ayrı ayrı iyi sekanslar üretmelerine rağmen kötü sonuçlanan sekans üretebilirsiniz (bu iki iyi sekans birbiriyle kolayca ilişkili olabilir ve böylece kötü bir şekilde birleşebilir).

Yalancı rasgele dizi aslında her zaman belirleyicidir (algoritması ve başlangıç ​​parametreleri tarafından önceden belirlenir) yani aslında rastgele bir şey yoktur.

Spesifik olarak rand()/ srand(s)fonksiyon çifti, uygulama tanımlı algoritmayla üretilen, işlem başına tekil, iş parçacığı için güvenli olmayan (!) Sözde rastgele bir sayı dizisi sağlar. İşlev rand()aralıkta değerler üretir [0, RAND_MAX].

C11 standardından alıntı:

srandİşlev sonraki çağrıları tarafından döndürülecek yalancı rasgele sayılar yeni dizisi için bir tohum olarak bağımsız değişken kullanır rand. Daha srandsonra aynı tohum değeri ile çağrılırsa, sözde rasgele sayılar dizisi tekrarlanmalıdır. Herhangi randbir çağrı srandyapılmadan srandönce çağrılırsa, ilk olarak 1 tohum değeri ile çağrıldığı zamanki gibi aynı sıra oluşturulmalıdır .

Birçok kişi makul bekliyoruz rand()aralığında yarı bağımsız düzgün yayılı bir sayı dizisi üretecektir 0için RAND_MAX. Eh kesinlikle (aksi takdirde işe yaramaz) gerekir ama maalesef sadece standart gerektirmez - "üretilen rastgele dizinin kalitesi konusunda hiçbir garanti yoktur" diye açık bir feragatname vardır . Bazı tarihsel durumlarda rand/ sranduygulama gerçekten çok kötü nitelikteydi. Modern uygulamalarda olsa bile, muhtemelen yeterince iyidir - ancak güven kırılır ve kurtarılması kolay değildir. İş parçacığı için güvenli olmayan doğasının yanı sıra, çok iş parçacıklı uygulamalarda güvenli kullanımını zor ve sınırlı hale getirir (yine de mümkündür - bunları sadece tek bir iş parçacığından kullanabilirsiniz).

Yeni sınıf şablonu std :: mersenne_twister_engine <> (ve kolaylık typedefs - std::mt19937/ std::mt19937_64iyi şablon parametreleri kombinasyonu ile) C ++ 11 standardında tanımlanan nesne başına sahte rastgele sayı üreteci sağlar. Aynı şablon parametreleri ve aynı başlatma parametreleriyle, farklı nesneler, C ++ 11 uyumlu standart kitaplıkla oluşturulmuş herhangi bir uygulamadaki herhangi bir bilgisayarda tam olarak aynı nesne başına çıktı dizisini üretecektir. Bu sınıfın avantajı, öngörülebilir yüksek kaliteli çıktı dizisi ve uygulamalar arasında tam tutarlılığıdır.

Ayrıca C ++ 11 standardında tanımlanan daha fazla PRNG motoru vardır - std :: linear_congruential_engine <> (tarihsel olarak srand/randbazı C standart kütüphane uygulamalarında adil kalite algoritması olarak kullanılır ) ve std :: subtract_with_carry_engine <> . Ayrıca, tamamen tanımlanmış parametreye bağlı nesne başına çıkış dizileri oluştururlar.

Yukarıdaki eski C kodu için modern C ++ 11 örnek değişimi:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

Önceki kodun std :: uniform_int_distribution <> kullanan sürümü

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

Burada benzer bir soru sordum bağlantı ama hala edemedim henüz net bir cevap bulmak. "Aslında bir kez srand (tohum) çağırmalı ve sonra rand ()" çağırmalısınız kodları ile çünkü zaten söylediklerini yaptım ama düzgün çalışmıyor.
bashburak

2
@bashburak Görünüşe göre bu cevabın amacını tamamen kaçırdınız. Alıntımı neden tam olarak kestin? Cevabımda tam anlamıyla "Aslında srand (tohum) 'u bir kez çağırmalı ve sonra birçok kez rand ()' ı aramalı ve bu diziyi analiz etmelisiniz - rastgele görünmeli." Single srand (...) çağrısından sonra rand () MANY TIMES çağırmanız gerektiğini fark ettiniz mi? Bağlantınızdaki sorunuz, tam olarak aynı yanlış anlama ile bu sorunun aynısı.
Serge Dundich

Bu eski bir yanıt, ancak Google "C ++ rasgele sayı üretimi" ne zaman google görünür. C ++ programcıları için kötü bir tavsiye, çünkü rand()ve kullanmanızı önerir srand(). Güncelleyebilir misiniz?
Yakk - Adam Nevraumont

@ Yakk-AdamNevraumont Aslında kullanılması tavsiye edilmez rand()ve srand(). Aslında sadece verilen açıklama ile soruyu cevaplar. Açıklamadan ( rand/ kullanan srand), sözde rasgele sayılar üretmenin temel kavramlarının, sözde rasgele sıralamanın ve tohumunun anlamı gibi açıklanması gerektiği açıktır . Tam olarak bunu yapmaya ve en basit ve tanıdık rand/ srandkombinasyonu kullanmaya çalışıyorum . Komik olan şey, diğer bazı cevapların - çok büyük derecelendirmeye sahip olsa bile - sorunun yazarı ile aynı yanlış anlamalardan muzdarip olmasıdır.
Serge Dundich

@ Yakk-AdamNevraumont Tavsiyeni aldım ve cevabımı en yeni C ++ eklemeleri hakkında bazı bilgilerle değiştirdim. Bu biraz konu dışı düşünmek rağmen - ama öneri yanı sıra diğer bazı cevaplar, hem iyi eski std::rand/std::srandAND yeni C ++ kütüphane özellikleri std::random_device<>, std :: mersenne_twister_engine <> ve rastgele dağıtım çok sayıda bazı açıklama gerektirir gösterir.
Serge Dundich

216

Modulo kullanımı, rastgele sayı üretecine bağlı olarak rastgele sayılara sapma getirebilir. Daha fazla bilgi için bu soruya bakın. Tabii ki, tekrarlayan sayıları rastgele bir sırayla elde etmek mükemmel bir şekilde mümkündür.

Daha iyi dağıtım için bazı C ++ 11 özelliklerini deneyin:

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

C ++ 11 rasgele sayılar hakkında daha fazla bilgi için bu soruya / cevaba bakınız. Yukarıdakiler bunu yapmanın tek yolu değil, bir yoludur.


7
Kullanarak ortaya çıkan önyargı miktarı %6kaybolarak azdır. Las Vegas'ta kullanılacak bir craps oyunu yazıyorsanız, ancak hemen hemen başka hiçbir bağlamda bir sonucu yoksa önemli olabilir.
Hot Licks

9
HotLicks: kabul etti, ancak zaten destekleyen random_deviceve mt19937zaten C ++ sürümünü kullanıyorsanız , kelimenin tam anlamıyla dışarı çıkıp standardı da kullanmamanız için hiçbir neden yokuniform_int_distribution .
Quuxplusone

4
Tüm programcılar, bölünme kullandığından ve yüzlerce saat döngüsüne mal olan ve uygulama zamanlamanıza zarar verebilecek ve / veya çok fazla pil gücü yakabileceği için insanlara veba gibi modulodan kaçınmasını tavsiye etmelidir.

3
Rng "aralık" için mi?
Christoffer

4
@ ChristofferHjärtström: Bunun için bu R Andom n toprak rengi g enerator.
Cornstalks

11

Eğer kullanıyorsanız artırmak kütüphanelerini bu şekilde rastgele jeneratör edinebilirsiniz:

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

Fonksiyonun current_time_nanoseconds()tohum olarak kullanılan nanosaniye cinsinden geçerli zamanını verdiği yerde .


Bir aralıktaki rastgele tamsayıları ve tarihleri ​​almak için daha genel bir sınıf:

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

1
Artık standardın bir parçası olarak rasgele olduğumuza göre, gerçekten eski bir derleyici kullanmadığınız sürece boost sürümünün kullanılmasını önermem.
Martin York

9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand


Soru yazarının kodu ile arasındaki fark nedir? (Kullanmamanız dışında %6.) Ve C kütüphanesi fonksiyonunun std::randC ++ API'sini kullanmaya karar verdiyseniz, randneden kullanmayın std::timeve std::srandC ++ stil tutarlılığı uğruna?
Serge Dundich

4

RandomerBuradan rastgele sayılar üretmek için tam sınıf kodu alabilirsiniz !

Projenin farklı bölümlerinde rastgele sayılara ihtiyacınız varsa Randomer, randomiçindeki tüm şeyleri kapsamak için ayrı bir sınıf oluşturabilirsiniz .

Bunun gibi bir şey:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

Böyle bir sınıf daha sonra şu konularda kullanışlı olacaktır:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

Rastgele dizeler oluşturmak için böyle bir Randomersınıfı nasıl kullandığımı örnek olarak bu bağlantıyı kontrol edebilirsiniz . İsterseniz de kullanabilirsiniz Randomer.


Jeneratörü tüm Randomer nesneleriniz için tekrar kullanmak istemez misiniz? Özellikle başlangıç ​​durumuna getirmek ve durumunu korumak nispeten pahalı olduğu için.
Martin York

3

Her seferinde altı kez aynı değil, her seferinde farklı bir rastgele sayı oluşturun.

Kullanım senaryosu

Öngörülebilirlik sorununu, her biri 0 ila 5 arasında yazılı olan altı bitlik bir torbaya benzettim. Her yeni değer gerektiğinde torbadan bir parça kağıt çekilir. Torba boşsa, numaralar torbaya geri konur.

... bundan bir çeşit algoritma oluşturabilirim.

Algoritma

Bir çanta genellikle a Collection. bool[]Torbanın rolünü almak için (aksi takdirde boolean dizi, bit düzlemi veya bit haritası olarak bilinir) seçtim .

A'yı seçmemizin nedeni bool[], her öğenin dizininin zaten her bir kağıt parçasının değeri olmasıdır. Eğer kağıtlar üzerlerinde yazılı başka bir şey isteseydi Dictionary<string, bool>, onun yerine bir tane kullanırdım. Boole değeri, numaranın henüz çizilip çizilmediğini takip etmek için kullanılır.

Çağrılan bir sayaç RemainingNumberCount, 5rastgele bir sayı seçildikçe geri sayılacak şekilde başlatılır . Bu, her yeni sayı çekmek istediğimizde kaç kağıt parçası kaldığını saymaktan kurtarır.

Ben kullanıyorum bir sonraki rastgele değerini seçmek için for..loopbir zaman kapalı saymak endeksler çanta ve bir sayaç taramak indexedilir falsedenilen NumberOfMoves.

NumberOfMovessonraki kullanılabilir sayıyı seçmek için kullanılır. NumberOfMovesilk önce 0ve arasında rastgele bir değer olacak şekilde ayarlanmıştır 5, çünkü torbada yapabileceğimiz 0..5 kullanılabilir adım vardır. Bir sonraki yinelemede ve NumberOfMovesarasında rastgele bir değer olacak şekilde ayarlanmıştır , çünkü şimdi torbadan yapabileceğimiz 0..4 adım vardır. Sayılar kullanıldıkça, kullanılabilir sayılar azalır, bunun yerine bir sonraki değeri hesaplamak için kullanırız .04rand() % (RemainingNumberCount + 1)NumberOfMoves

Ne zaman NumberOfMovessayacı ulaşır sıfır, for..loopaşağıdaki gibi olmalıdır:

  1. Mevcut Değeri for..loop'nin diziniyle aynı olacak şekilde ayarlayın .
  2. Torbanın içindeki tüm sayıları olarak ayarlayın false.
  3. Kırmak for..loop.

kod

Yukarıdaki çözümün kodu aşağıdaki gibidir:

(aşağıdaki üç bloğu birbiri ardına ana .cpp dosyasına koyun)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

Bir Konsol sınıfı

Çıkışı yeniden yönlendirmeyi kolaylaştırdığı için bu Konsol sınıfını oluşturuyorum.

Kodun altında ...

Console::WriteLine("The next value is " + randomBag.ToString());

... ile değiştirilebilir ...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

... ve sonra bu Consolesınıf istenirse silinebilir.

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

Ana yöntem

Aşağıdaki gibi örnek kullanım:

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

Örnek çıktı

Programı çalıştırdığımda, aşağıdaki çıktıyı aldım:

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

Kapanış bildirimi

Bu program Visual Studio 2017 kullanılarak yazılmıştır ve bunu Visual C++ Windows Console Applicationkullanarak bir proje yapmayı seçtim .Net 4.6.1.

Burada özel bir şey yapmıyorum, bu yüzden kod Visual Studio'nun önceki sürümlerinde de çalışmalıdır.


Bu VS 2017 ise, standart kütüphanenin en son sürümünü kullanmalısınız: en.cppreference.com/w/cpp/numeric/random . Şu anda bu örnekte C rasgele kütüphane fonksiyonları ve "Üretilen rasgele sekansın kalitesi konusunda hiçbir garanti yoktur" kullanılmaktadır.
Robert Andrzejuk

3

random number generationC ++ programlama dilinde temel bir web araması yaptığınızda, bu soru genellikle ilk açılır! Bu aynı soruyu kaçınılmaz olarak web'de arayacak gelecek kodlayıcılar için C ++'daki sözde rasgele sayı üretme kavramını umarım daha iyi açıklığa kavuşturmak için şapkamı ringe atmak istiyorum !

Temeller

Yalancı rasgele sayı üretimi, özellikleri yaklaşık olarak rasgele sayılara benzeyen bir sayı dizisi üreten deterministik bir algoritmanın kullanılması sürecini içerir . Ben söylemek yaklaşık benzeyen gerçek rasgelelik oldukça çünkü, zor gizem matematik ve bilgisayar bilimleri. Bu nedenle, yalancı rasgele terimi neden daha bilgiçlikle doğru olmak için kullanılır!

Bir PRNG'yi gerçekten kullanabilmeniz pseudo-random number generatoriçin, algoritmaya genellikle tohum olarak da adlandırılan bir başlangıç ​​değeri sağlamalısınız . Bununla birlikte, tohum algoritmanın kendisini kullanmadan önce sadece bir kez ayarlanmalıdır !

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

Böylece, iyi bir sayı dizisi istiyorsanız, PRNG'ye geniş bir tohum sağlamalısınız!

Eski C Yolu

C ++ 'ın sahip olduğu C'nin geriye dönük uyumlu standart kütüphanesi , başlık dosyasında bulunan doğrusal bir eşlenik oluşturucu olarak adlandırılanı kullanır cstdlib! Bu PRNG, modüler aritmetik kullanan süreksiz parçalı bir işlevle, yani modulo operator '%'. @ Öngörülebilirlik tarafından sorulan orijinal soru ile ilgili olarak bu PRNG'nin yaygın kullanımı aşağıdadır:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

C'nin PRNG'sinin ortak kullanımı aşağıdaki gibi bir dizi konuyu barındırır:

  1. Genel arayüzü, std::rand()belirli bir aralık arasında uygun rasgele sayıların üretilmesi için çok sezgisel değildir, örneğin, @ Tahmin edilebilirliğin istediği şekilde [1, 6] arasında sayılar üretmek.
  2. Ortak kullanım , Güvercin Deliği İlkesi nedeniyle sözde rastgele sayıların düzgün bir şekilde dağılmasıstd::rand() olasılığını ortadan kaldırır .
  3. Teknik yolla std::rand()tohumlanmanın yaygın yolu std::srand( ( unsigned int )std::time( nullptr ) )doğru değildir, çünkü kısıtlı bir türtime_t olarak kabul edilir . Bu nedenle, ' den dönüşüme garanti edilmez!time_tunsigned int

C'nin PRNG'sini kullanmayla ilgili genel sorunlar ve bunların nasıl atlanacağı hakkında daha ayrıntılı bilgi için lütfen rand () (C / C ++) kullanma: C standart kitaplığının rand () işlevi için öneri !

Standart C ++ Yolu

ISO / IEC 14882: 2011 standardı, yani C ++ 11 yayınlandığından randomberi , kütüphane bir süredir C ++ programlama dilinden ayrıdır. Bu kütüphane ile donatılmış olarak geliyor birden PRNGs ve farklı dağıtım türleri gibi: tekdüze dağılım , normal dağılımın , binom dağılımı , vb Aşağıdaki kaynak kod örneği bir çok temel kullanımını gösteren randomöngörülebilirlik orijinal soruya @ açısından, kütüphane:

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

Yukarıdaki örnekte , tamsayı değerlerinin muntazam dağılımına sahip 32-bit Mersenne Twister motoru kullanılmıştır. (Kaynak koddaki motorun adı garip geliyor, çünkü adı 2 ^ 19937-1 döneminden geliyor ). Bu örnek , değerini işletim sisteminden alan motoru tohumlamak için de kullanılır (Linux sistemi kullanıyorsanız, bir değer döndürür ).std::random_devicestd::random_device/dev/urandom

Herhangi bir motorustd::random_device tohumlamak için kullanmanız gerekmediğini unutmayın . Sen kullanabilirsiniz sabitleri ve hatta kütüphane! Ayrıca motorun 32 bit sürümünü kullanmak zorunda değilsiniz , başka seçenekler de var ! Kütüphanenin özellikleri hakkında daha fazla bilgi için lütfen cplusplus.com adresine bakın.chronostd::mt19937random

Sonuçta, C ++ programcıları std::rand()artık kullanmamalı , kötü olduğu için değil, mevcut standart daha basit ve daha güvenilir olan daha iyi alternatifler sağladığı için . Umarım, birçoğunuz bunu yararlı buluyor, özellikle de son zamanlarda web'de arama yapanlarınız generating random numbers in c++!


2

İşte bir çözüm. Rasgele sayıyı döndüren bir işlev oluşturun ve onu global yapmak için ana işlevin dışına yerleştirin. Bu yardımcı olur umarım

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

2

Bu kod rastgele sayılar üreten netmek m.

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

misal:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
Bu soruya gerçekten cevap vermiyor.
HolyBlackCat

1
Düzeltmedin. Sorunun amacı, programı saniyede birkaç kez çalıştırırsanız, aynı rastgele değerleri üretmesidir. Kodunuz da bunu yapar.
HolyBlackCat

1
@HolyBlackCat Birden fazla çalışma için kontrol ettim, çalışıyor. Daha srand(time(0))önce ana fonksiyona eklediniz random(n, m)mi?
Amir Fo

1
srand(time(0))Döngü için değil veya işlev uygulaması içinde ana işleve eklemeniz gerekir .
Amir Fo

1
Kodunuzu kelimesi kelimesine kopyaladım. Saniyede birkaç kez çalıştırdınız mı?
HolyBlackCat

1

her RUN dosyası rastgele

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

1
Jeneratörü birçok kez yaratmanız gerekmez. Bir grup durumu korur, böylece uygun dağılıma sahip rasgele sayılar dizisi oluşturur (rastgele görünmesini sağlamak için).
Martin York

-2

İşte yakl. 0 civarında pozitif ve negatif değerler üretme olasılığı:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
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.