Hoe genereer je een willekeurig getal in C++?

Ik probeer een spel met dobbelstenen te maken, en ik moet er willekeurige getallen in hebben (om de zijkanten van de dobbelsteen te simuleren. Ik weet hoe ik het tussen 1 en 6 moet maken). Met behulp van

#include <cstdlib> 
#include <ctime> 
#include <iostream>
using namespace std;
int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

werkt niet zo goed, want als ik het programma een paar keer uitvoer, krijg ik hier de uitvoer:

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

Dus ik wil een commando dat elke keer een ander willekeurig getal genereert, niet 5 keer achter elkaar hetzelfde. Is er een commando dat dit zal doen?


Antwoord 1, autoriteit 100%

Het meest fundamentele probleem van uw testtoepassing is dat u eenmaal srand aanroept en vervolgens eenmaal rand aanroept en afsluit.

Het hele punt van de functie srand is om de reeks pseudo-willekeurige getallen te initialiseren met een willekeurige seed.

Het betekent dat als u dezelfde waarde doorgeeft aan srand in twee verschillende toepassingen (met dezelfde srand/rand implementatie) dan krijg je exact dezelfde reeks van rand() waarden die daarna in beide applicaties worden gelezen.

In uw voorbeeldtoepassing bestaat de pseudo-willekeurige reeks echter uit slechts één element: het eerste element van een pseudo-willekeurige reeks gegenereerd uit seed gelijk aan de huidige tijd van 1 sec precisie. Wat verwacht je dan te zien aan output?

Als je de applicatie toevallig op dezelfde seconde uitvoert – je gebruikt dezelfde seed-waarde – is je resultaat natuurlijk hetzelfde (zoals Martin York al zei in een opmerking bij de vraag).

Eigenlijk zou je srand(seed) één keer moeten aanroepen en dan rand() vele keren moeten aanroepen en die reeks moeten analyseren – het zou eruit moeten zien willekeurig.

AMENDEMENT 1 – voorbeeldcode:

Oké, ik snap het.
Blijkbaar is verbale beschrijving niet genoeg (misschien taalbarrière of zoiets… 🙂 ).

Ouderwets C-codevoorbeeld gebaseerd op dezelfde srand()/rand()/time()-functies die in de vraag werden gebruikt:

#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;
}

^^^ DAT reeks van een enkele uitvoering van het programma hoort er willekeurig uit te zien.

Houd er rekening mee dat ik het gebruik van de functies rand/srand niet aanbeveel om de hieronder uiteengezette redenen, en dat doe ik absoluut niet. t raden aan om de functie time te gebruiken als een willekeurige seed om de redenen dat IMO al vrij duidelijk zou moeten zijn. Die zijn prima voor educatieve doeleinden en soms om het punt te illustreren, maar voor serieus gebruik zijn ze meestal nutteloos.

AMENDEMENT 2 – gedetailleerde uitleg:

Het is belangrijk om te begrijpen dat er vanaf nu geen C- of C++-standaardfuncties (bibliotheekfuncties of klassen) zijn die daadwerkelijk willekeurige gegevens definitief produceren (d.w.z. gegarandeerd door de standaard om daadwerkelijk willekeurig te zijn). De enige standaardfunctie die dit probleem benadert, is std::random_device dat biedt helaas nog steeds geen garanties voor daadwerkelijke willekeur.

Afhankelijk van de aard van de applicatie moet je eerst beslissen of je echt willekeurige (onvoorspelbare) gegevens nodig hebt. Een opmerkelijk geval wanneer je zeker echte willekeur nodig hebt is informatiebeveiliging – b.v. genereren van symmetrische sleutels, asymmetrische privésleutels, zoutwaarden, beveiligingstokens, enz.

Maar willekeurige getallen van beveiligingskwaliteit zijn een aparte branche die een apart artikel waard is. Ik bespreek ze kort in dit antwoord van mij.

In de meeste gevallen is Pseudo-Random Number Generator voldoende – b.v. voor wetenschappelijke simulaties of games. In sommige gevallen is zelfs een consistent gedefinieerde pseudo-willekeurige reeks vereist – b.v. in games kun je ervoor kiezen om tijdens runtime exact dezelfde kaarten te genereren om te voorkomen dat er veel gegevens in je distributie worden opgeslagen.

De oorspronkelijke vraag en de steeds terugkerende veelheid van identieke/vergelijkbare vragen (en zelfs vele misleidende “antwoorden” daarop) geven aan dat het in de eerste plaats belangrijk is om willekeurige getallen te onderscheiden van pseudo-willekeurige getallen EN om te begrijpen wat pseudo-willekeurig is nummerreeks in de eerste plaats EN om te beseffen dat pseudo-willekeurige nummergeneratoren NIET op dezelfde manier worden gebruikt als echte generatoren van willekeurige getallen.

Intuïtief wanneer u een willekeurig getal aanvraagt ​​- het geretourneerde resultaat mag niet afhankelijk zijn van eerder geretourneerde waarden en zou niet moeten afhangen van of
iedereen heeft eerder iets aangevraagd en zou niet moeten afhangen op welk moment
en door welk proces en op welke computer en van welke generator en
in welk sterrenstelsel het werd gevraagd. Dat is wat woord “willekeurig” betekent
immers – onvoorspelbaar en onafhankelijk van wat dan ook –
anders is het niet meer willekeurig, toch? Met deze intuïtie is het
alleen natuurlijk om op internet te zoeken naar een aantal magische spreuken om te gebruiken om te krijgen
zo’n willekeurig getal in elke mogelijke context.

^^^ DAT soort intuïtieve verwachtingen IS ERG VERKEERD en schadelijk in alle gevallen met betrekking tot Pseudo-willekeurige nummergeneratoren – ondanks dat ze redelijk zijn voor echte willekeurige getallen.

Hoewel het zinvolle begrip “willekeurig getal” bestaat (soort van) – bestaat er niet zoiets als “pseudo-willekeurig getal”. Een Pseudo-Random Number Generator produceert in feite pseudo-willekeurige reeks .

Pseudo-willekeurige volgorde is in feite altijd deterministisch (vooraf bepaald door zijn algoritme en initiële parameters) – d.w.z. er is eigenlijk niets willekeurigs aan.

Als experts praten over de kwaliteit van PRNG, hebben ze het eigenlijk over statistische eigenschappen van de gegenereerde reeks (en zijn opmerkelijke subreeksen). Als u bijvoorbeeld twee PRNG’s van hoge kwaliteit combineert door ze beide om de beurt te gebruiken, kunt u een slechte resulterende reeks produceren, ondanks dat ze elk afzonderlijk goede reeksen genereren (die twee goede reeksen kunnen eenvoudig met elkaar correleren en dus slecht combineren).

Specifiek rand()/srand(s) paar functies zorgen voor een enkelvoudig per-proces niet-thread-safe(!) pseudo-willekeurige nummerreeks gegenereerd met implementatiegedefinieerd algoritme. Functie rand() produceert waarden in het bereik [0, RAND_MAX].

Citaat uit de C11-standaard (ISO/IEC 9899:2011):

De functie srand gebruikt het argument als een seed voor een nieuwe reeks van
pseudo-willekeurige nummers die moeten worden geretourneerd door volgende oproepen naar rand. Indien
srand wordt dan aangeroepen met dezelfde seed-waarde, de reeks van
pseudo-willekeurige getallen worden herhaald. Als rand eerder wordt aangeroepen
aanroepen naar srand zijn gedaan, wordt dezelfde volgorde gegenereerd als:
wanneer srand voor het eerst wordt aangeroepen met een seed-waarde van 1.

Veel mensen verwachten redelijkerwijs dat rand() een reeks semi-onafhankelijke uniform verdeelde getallen zou produceren in het bereik 0 tot RAND_MAX. Nou, het zou zeker moeten (anders is het nutteloos) maar helaas vereist niet alleen standaard dat niet – er is zelfs een expliciete disclaimer die stelt “er is geen garantie met betrekking tot de kwaliteit van de geproduceerde willekeurige reeks” .
In sommige historische gevallen was de implementatie van rand/srand inderdaad van zeer slechte kwaliteit. Ook al is het in moderne implementaties waarschijnlijk goed genoeg – maar het vertrouwen is geschonden en niet gemakkelijk te herstellen.
Naast de niet-thread-safe aard maakt het veilige gebruik ervan in multi-threaded applicaties lastig en beperkt (nog steeds mogelijk – je kunt ze gewoon gebruiken vanuit één speciale thread).

Nieuw klassjabloon std::mersenne_twister_engine<> ( en zijn gemak typedefs – std::mt19937/std::mt19937_64 met een goede combinatie van sjabloonparameters) biedt per-object pseudo-willekeurige nummergenerator gedefinieerd in de C++11-standaard. Met dezelfde sjabloonparameters en dezelfde initialisatieparameters zullen verschillende objecten exact dezelfde uitvoervolgorde per object genereren op elke computer in elke toepassing die is gebouwd met een C++11-compatibele standaardbibliotheek. Het voordeel van deze klasse is de voorspelbare uitvoervolgorde van hoge kwaliteit en volledige consistentie tussen implementaties.

Er zijn ook meer PRNG-engines gedefinieerd in de C++11-standaard – std ::linear_congruential_engine<> (van oudsher gebruikt als srand/rand-algoritme van redelijke kwaliteit in sommige C-standaardbibliotheekimplementaties) en std::subtract_with_carry_engine<>. Ze genereren ook volledig gedefinieerde parameterafhankelijke uitvoerreeksen per object.

Moderne C++11 voorbeeldvervanging voor de verouderde C-code hierboven:

#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;
}

De versie van de vorige code die gebruikmaakt van std::uniform_int_distribution<>

#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;
}

Antwoord 2, autoriteit 94%

Het gebruik van modulo kan leiden tot vertekening in de willekeurige getallen, afhankelijk van de generator voor willekeurige getallen. Bekijk deze vraag voor meer informatie. Het is natuurlijk perfect mogelijk om herhalende getallen in een willekeurige volgorde te krijgen.

Probeer enkele C++11-functies uit voor een betere distributie:

#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;
}

Bekijk deze vraag/antwoord voor meer informatie over C++11 willekeurige getallen. Het bovenstaande is niet de enige manier om dit te doen, maar het is een manier.


Antwoord 3, autoriteit 11%

Als je boost bibliotheken gebruikt, kun je op deze manier een willekeurige generator verkrijgen:

#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;
    }
}

Waarbij de functie current_time_nanoseconds() de huidige tijd in nanoseconden geeft die als seed wordt gebruikt.


Hier is een meer algemene klasse om willekeurige gehele getallen en datums in een bereik te krijgen:

#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);
    }
};

Antwoord 4, autoriteit 10%

Telkens wanneer u een eenvoudige zoekopdracht op internet uitvoert voor random number generation in de programmeertaal C++, is deze vraag meestal de eerste die opduikt! Ik wil mijn hoed in de ring gooien om hopelijk het concept van pseudo-willekeurige nummergeneratie in C++ beter te verduidelijken voor toekomstige programmeurs die onvermijdelijk dezelfde vraag op internet zullen zoeken!

De basis

Het genereren van pseudo-willekeurige getallen omvat het proces van het gebruik van een deterministisch algoritme dat een reeks van getallen waarvan de eigenschappen ongeveer lijken op willekeurige getallen. Ik zeg ongeveer lijken, omdat echte willekeur een nogal ongrijpbaar mysterie in wiskunde en informatica. Daarom wordt de term pseudo-willekeurig gebruikt om pedanter correct te zijn!

Voordat u daadwerkelijk een PRNG kunt gebruiken, d.w.z. pseudo-random number generator, moet u het algoritme een beginwaarde geven die ook wel de seed wordt genoemd. De seed moet echter slechts één keer voor worden ingesteld met het gebruik van het algoritme zelf!

/// 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...

Dus, als je een goede reeks getallen wilt, dan moet je een voldoende zaad voor de PRNG leveren!

De oude C-manier

De achterwaarts compatibele standaardbibliotheek van C die C++ heeft, gebruikt een zogenaamde lineaire congruentiële generator gevonden in het cstdlib headerbestand! Deze PRNG werkt via een discontinue stukgewijze functie die gebruikmaakt van modulaire rekenkunde, d.w.z. een snel algoritme dat graag de modulo operator '%' gebruikt. Het volgende is een algemeen gebruik van deze PRNG, met betrekking tot de oorspronkelijke vraag van @Predictability:

#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;
}

Het algemene gebruik van de PRNG van C brengt een hele reeks problemen met zich mee, zoals:

  1. De algehele interface van std::rand() is niet erg intuïtief voor de juiste generatie van pseudo-willekeurige getallen tussen een bepaald bereik, bijv. het produceren van getallen tussen [1, 6] de manier waarop @Voorspelbaarheid gewenst was.
  2. Het algemene gebruik van std::rand() elimineert de mogelijkheid van een uniforme verdeling van pseudo-willekeurige getallen, vanwege de Pigeonhole-principe.
  3. De gebruikelijke manier waarop std::rand() via std::srand( ( unsigned int )std::time( nullptr ) ) wordt geplaatst, is technisch gezien niet correct, omdat time_t wordt beschouwd als een beperkt type. Daarom is de conversie van time_t naar unsigned int niet gegarandeerd!

Voor meer gedetailleerde informatie over de algemene problemen van het gebruik van C’s PRNG en hoe u deze mogelijk kunt omzeilen, raadpleegt u Gebruik van rand() (C/C++): advies voor de functie rand() van de C-standaardbibliotheek!

De standaard C++-manier

Sinds de ISO/IEC 14882:2011-standaard werd gepubliceerd, d.w.z. C++11, maakt de random-bibliotheek al een tijdje geen deel uit van de C++-programmeertaal. Deze bibliotheek is uitgerust met meerdere PRNG’s en verschillende distributietypes zoals: uniforme verdeling, normale verdeling, binominale distributie, enz. Het volgende voorbeeld van de broncode toont een zeer basaal gebruik van de random bibliotheek, met betrekking tot de oorspronkelijke vraag van @Predictability:

#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;
}

De 32-bits Mersenne Twister-engine, met een uniforme distributie van gehele waarden werd gebruikt in het bovenstaande voorbeeld. (De naam van de engine in de broncode klinkt raar, omdat de naam komt van de periode van 2^19937-1 ). Het voorbeeld gebruikt ook std::random_device om de engine te seeden, die zijn waarde verkrijgt van het besturingssysteem (als je een Linux-systeem gebruikt, dan retourneert std::random_device een waarde uit /dev/urandom).

Houd er rekening mee dat u std::random_device niet hoeft te gebruiken om elke engine te seeden. Je kunt constanten of zelfs de chrono bibliotheek gebruiken! Je hoeft ook niet de 32-bits versie van de std::mt19937 engine te gebruiken, er zijn andere opties! Ga voor meer informatie over de mogelijkheden van de random-bibliotheek naar cplusplus.com

Al met al zouden C++-programmeurs std::rand() niet meer moeten gebruiken, niet omdat het slecht is, maar omdat de huidige standaard betere alternatieven biedt die meer eenvoudig en betrouwbaar. Hopelijk vinden velen van jullie dit nuttig, vooral degenen onder jullie die onlangs op internet hebben gezocht op generating random numbers in c++!


Antwoord 5, autoriteit 7%

#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/ willekeurig/random


Antwoord 6, autoriteit 4%

Kan volledige Randomer klassecode krijgen voor het genereren van willekeurige getallen vanaf hier!

Als je willekeurige getallen in verschillende delen van het project nodig hebt, kun je een aparte klasse Randomer maken om alle random-dingen erin op te nemen.

Zoiets:

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_);
    }
};

Zo’n les zou later handig zijn:

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

Je kunt deze link als voorbeeld bekijken hoe ik gebruik zo’n klasse Randomer om willekeurige tekenreeksen te genereren. Je kunt desgewenst ook Randomer gebruiken.


Antwoord 7, autoriteit 3%

Genereer elke keer een ander willekeurig getal, niet zes keer achter elkaar hetzelfde.

Gebruiksscenario

Ik vergeleek het probleem van Voorspelbaarheid met een zak met zes stukjes papier, elk met een waarde van 0 tot 5 erop. Elke keer dat er een nieuwe waarde nodig is, wordt er een stuk papier uit de zak getrokken. Als de zak leeg is, worden de nummers terug in de zak gedaan.

…hieruit kan ik een soort algoritme maken.

Algoritme

Een tas is meestal een Collection. Ik koos een bool[] (ook wel bekend als een boolean array, bit plane of bitmap) om de rol van de tas op zich te nemen.

De reden dat ik een bool[] heb gekozen, is omdat de index van elk item al de waarde van elk stuk papier is. Als er iets anders op de papieren was geschreven, had ik in plaats daarvan een Dictionary<string, bool> gebruikt. De booleaanse waarde wordt gebruikt om bij te houden of het nummer al is getrokken of niet.

Een teller genaamd RemainingNumberCount wordt geïnitialiseerd op 5 die aftelt als een willekeurig getal wordt gekozen. Dit bespaart ons dat we moeten tellen hoeveel stukjes papier er nog over zijn elke keer dat we een nieuw nummer willen tekenen.

Om de volgende willekeurige waarde te selecteren, gebruik ik een for..loop om door de zak met indexen te bladeren, en een teller om af te tellen wanneer een index is false genaamd NumberOfMoves.

NumberOfMoves wordt gebruikt om het volgende beschikbare nummer te kiezen. NumberOfMoves is eerst ingesteld op een willekeurige waarde tussen 0 en 5, omdat er 0..5 beschikbare stappen zijn die we door de tas kunnen maken . Bij de volgende iteratie wordt NumberOfMoves ingesteld op een willekeurige waarde tussen 0 en 4, omdat er nu 0..4 stappen zijn die we kunnen maken door de zak. Naarmate de getallen worden gebruikt, nemen de beschikbare getallen af, dus gebruiken we in plaats daarvan rand() % (RemainingNumberCount + 1) om de volgende waarde voor NumberOfMoves te berekenen.

Als de NumberOfMoves-teller nul bereikt, moet de for..loop als volgt:

  1. Stel de huidige waarde in op dezelfde waarde als de index van for..loop.
  2. Stel alle cijfers in de tas in op false.
  3. Breek uit de for..loop.

Code

De code voor de bovenstaande oplossing is als volgt:

(zet de volgende drie blokken achter elkaar in het .cpp-bestand)

#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;
    }
};

Een consoleklasse

Ik maak deze Console-klasse omdat het gemakkelijk is om uitvoer om te leiden.

Hieronder in de code…

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

…kan worden vervangen door…

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

…en dan kan deze Console-klasse desgewenst worden verwijderd.

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

Hoofdmethode

Voorbeeld van gebruik als volgt:

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");
}

Voorbeelduitvoer

Toen ik het programma uitvoerde, kreeg ik de volgende uitvoer:

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 . . .

Slotverklaring

Dit programma is geschreven met Visual Studio 2017 en ik heb ervoor gekozen om er een Visual C++ Windows Console Application-project van te maken met .Net 4.6.1.

Ik doe hier niets bijzonders, dus de code zou ook moeten werken op eerdere versies van Visual Studio.


Antwoord 8, autoriteit 3%

Deze code produceert willekeurige getallen van n tot m.

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

voorbeeld:

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

Antwoord 9, autoriteit 2%

Hier is een oplossing. Maak een functie die het willekeurige getal retourneert en plaats het
buiten de hoofdfunctie om het globaal te maken. Ik hoop dat dit helpt

#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;
}

Antwoord 10, autoriteit 2%

Ik weet hoe ik een willekeurig getal in C++ kan genereren zonder headers, compiler-intrinsieken of wat dan ook te gebruiken.

#include <cstdio> // Just for printf
int main() {
    auto val = new char[0x10000];
    auto num = reinterpret_cast<unsigned long long>(val);
    delete[] val;
    num = num / 0x1000 % 10;
    printf("%llu\n", num);
}

Ik kreeg de volgende statistieken na een tijdje hardlopen:

0: 5268
1: 5284
2: 5279
3: 5242
4: 5191
5: 5135
6: 5183
7: 5236
8: 5372
9: 5343

Ziet er willekeurig uit.

Hoe het werkt:

  • Moderne compilers beschermen je tegen bufferoverloop met behulp van ASLR (randomisatie van adresruimte-indeling).
  • Je kunt dus willekeurige getallen genereren zonder bibliotheken te gebruiken, maar het is gewoon voor de lol. Gebruik ASLR niet op die manier.

Antwoord 11

voor willekeurig elk RUN-bestand

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);
}

Antwoord 12

Hier is een eenvoudige willekeurige generator met ca. gelijke kans op het genereren van positieve en negatieve waarden rond 0:

  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;
   }

LEAVE A REPLY

Please enter your comment!
Please enter your name here

three × 5 =

Other episodes