Quale dei motori di numeri casuali di <random> si dovrebbe effettivamente usare in pratica? std :: mt19937?


21

Supponiamo di voler utilizzare le funzionalità C ++ <random>in un programma pratico (per alcune definizioni di "pratico" - i vincoli qui fanno parte di questa domanda). Il codice è approssimativamente simile a questo:

int main(int argc, char **argv) {
    int seed = get_user_provided_seed_value(argc, argv);
    if (seed == 0) seed = std::random_device()();
    ENGINE g(seed);  // TODO: proper seeding?
    go_on_and_use(g);
}

La mia domanda è: per quale tipo dovresti usare ENGINE?

  • Lo dicevo sempre std::mt19937perché era veloce digitare e aveva il riconoscimento del nome. Ma in questi giorni sembra che tutti stiano dicendo che il Mersenne Twister è molto pesante e ostile alla cache e non supera nemmeno tutti i test statistici che altri fanno.

  • Vorrei dire std::default_random_engineperché è l'ovvio "default". Ma non so se varia da piattaforma a piattaforma e non so se sia statisticamente utile.

  • Dal momento che ognuno è su una piattaforma a 64-bit in questi giorni, dovremmo almeno essere utilizzando std::mt19937_64over std::mt19937?

  • Vorrei dirlo pcg64o xoroshiro128perché sembrano rispettati e leggeri, ma non esistono <random>affatto.

  • Io non so niente di minstd_rand, minstd_rand0, ranlux24, knuth_b, ecc - di certo si deve essere buono per qualcosa?

Ovviamente ci sono alcuni vincoli concorrenti qui.

  • Forza del motore ( <random>non ha PRNG crittograficamente forti, ma alcuni di quelli standardizzati sono "più deboli" di altri, giusto?)

  • sizeof il motore.

  • La sua velocità operator().

  • Facilità di semina. mt19937è notoriamente difficile seminare correttamente perché ha così tanto stato da inizializzare.

  • Portabilità tra i fornitori di biblioteche. Se un fornitore foo_engineproduce numeri diversi da quelli di un altro fornitore foo_engine, ciò non va bene per alcune applicazioni. (Speriamo che questo non escluda nulla tranne forse default_random_engine.)

Pesando tutti questi vincoli nel miglior modo possibile, quale diresti sia la migliore risposta "best practice restando all'interno della libreria standard"? Devo continuare a usare std::mt19937o cosa?


2
All'ultimo punto, tutti gli adattatori motore standard sono specificati per restituire un determinato valore su una particolare chiamata consecutiva di quella costruita di default, quindi dovrebbero essere portatili.
— 1201 Programma Allarme

Risposte:


15

Riferimenti C ++ elenca tutti i motori casuali attualmente forniti da C ++. Tuttavia, la selezione dei motori lascia molto a desiderare (ad esempio, vedere il mio elenco di generatori casuali di alta qualità ). Per esempio:

  • default_random_engine è definito dall'implementazione, quindi non è noto se il motore abbia difetti statistici che potrebbero interessare all'applicazione.
  • linear_congruential_engineimplementa generatori congruenziali lineari. Tuttavia, tendono ad avere una qualità scadente a meno che il modulo non sia primo e molto grande (almeno 64 bit). Inoltre, non possono ammettere più semi del loro modulo.
  • minstd_rand0e minstd_randammettere solo circa 2 ^ 31 semi. knuth_bavvolge un minstd_rand0e fa un riordino di Bays-Durham.
  • mt19937e mt19937_64potrebbero ammettere molti più semi se fossero inizializzati meglio (ad es. inizializzando un std::seed_seqcon output multipli di random_device, non solo uno), ma usano circa 2500 byte di stato.
  • ranlux24e ranlux48usano circa 577 bit di stato ma sono lenti (funzionano mantenendo alcuni e scartando altre uscite pseudocasuali).

Tuttavia, C ++ ha anche due motori che avvolgono un altro motore per migliorare potenzialmente le sue proprietà di casualità:

  • discard_block_engine scarta alcune delle uscite di un determinato motore casuale.
  • shuffle_order_engine implementa un riordino Bays-Durham di un determinato motore casuale.

Per esempio, è possibile, ad esempio, di avere un riordino Baie-Durham di mt19937, ranlux24o un costume linear_congruential_enginecon shuffle_order_engine. Forse il motore avvolto è di qualità migliore di quello originale. Tuttavia, è difficile prevedere la qualità statistica del nuovo motore senza testarlo .

Pertanto, in attesa di tali test, sembra che mt19937sia il motore più pratico nello standard C ++ per ora. Sono a conoscenza, tuttavia, di almeno una proposta di aggiungere un altro motore di numeri casuali alle future versioni di C ++ (vedere il documento C ++ P2075 ).


1

Secondo C ++ Reference , default_random_engine:

È la selezione dell'implementazione della libreria di un generatore che fornisce un comportamento del motore almeno accettabile per un uso relativamente casuale, inesperto e / o leggero.

Quindi per un uso leggero non devi preoccuparti di nulla, semi default_random_enginecon Epoch Time (time(0))e sarebbe abbastanza bene;)


Credo che il problema qui sia la portabilità. Sebbene l'impostazione predefinita possa essere un motore che funziona bene, potrebbe non essere riproducibile su un'altra piattaforma.
— bremen_matt,

@bremen_matt Hmm ... Bene, perché dobbiamo riprodurre un numero "casuale"?
— Farbod Ahmadian,

2
Testing. A scopo di test, sono necessari input riproducibili. Allo stesso tempo, potresti voler o avere bisogno che quegli input siano casuali. Ad esempio, la maggior parte degli algoritmi di machine learning presuppone che i parametri vengano inizializzati in modo casuale. Ransac, CNN, DNN, ... molti algoritmi richiedono parametri casuali.
— bremen_matt il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.