Random Number Generator Recommendations for Applications

Peter Occil

Most apps that use random numbers care about either unpredictability, high quality, or repeatability. This article explains the three kinds of RNGs and gives recommendations on each kind.


Many applications rely on random number generators (RNGs); however, it's not enough for random numbers to merely "look random". But unfortunately, most popular programming languages today—

so that as a result, many applications use RNGs, especially built-in RNGs, that have little assurance of high quality or security. That is why this document discusses high-quality RNGs and suggests existing implementations of them.

This document covers:

This document does not cover:

About This Document

This is an open-source document; for an updated version, see the source code or its rendering on GitHub. You can send comments on this document either on CodeProject or on the GitHub issues page.



In this document:


Cryptographic RNGs

Cryptographic RNGs (also known as "cryptographically strong" or "cryptographically secure" RNGs) seek to generate random numbers that not only "look random", but are cost-prohibitive to guess. An application should use a cryptographic RNG whenever the application—

See "Cryptographic RNGs: Requirements" for requirements.
See "Existing RNG APIs in Programming Languages" for existing APIs.
For cryptographic RNGs, an application should use only one thread-safe instance of the RNG for the entire application to use.

Examples: A cryptographic RNG is recommended—

Noncryptographic PRNGs

Noncryptographic PRNGs vary widely in the quality of randomness of the numbers they generate. For this reason, a noncryptographic PRNG should not be used—

Noncryptographic PRNGs can be automatically seeded (a new seed is generated upon PRNG creation) or manually seeded (the PRNG uses a predetermined seed).

Manually-Seeded PRNGs

A given pseudorandom number generator (PRNG) generates the same sequence of "random" numbers for the same "seed". Some applications care about reproducible "randomness" and thus could set a PRNG's seed manually for reproducible "random" numbers.

When to Use a Manually-Seeded PRNG

By seeding a PRNG manually for reproducible "randomness", an application will be tied to that PRNG or its implementation. For this reason, an application should not use a manually-seeded PRNG (rather than a cryptographic or automatically-seeded RNG) unless—

  1. the application might need to generate the same "random" result multiple times,
  2. the application either—
    • makes the seed (or a "code" or "password" based on the seed) accessible to the user, or
    • finds it impractical to store or distribute the "random" numbers or "random" content, rather than the seed, for later use (e.g., to store those numbers to "replay" later, to store that content in a "save file", or to distribute that content rather than a seed to networked users), and
  3. any feature that uses such a PRNG to generate that "random" result is reproducible, in that it produces the same "random" result for the same seed for as long as the feature is still in use by the application.

Manually-Seeded PRNG Recommendations

If an application chooses to use a manually-seeded PRNG for reproducible "randomness", the application—

For advice on generating seeds for the PRNG, see "Seed Generation for Noncryptographic PRNGs").

Example: An application could implement a manually-seeded PRNG using a third-party library that specifically says it implements a high-quality PRNG algorithm, and could initialize that PRNG using a bit sequence from a cryptographic RNG. The developers could also mention the use of the specific PRNG chosen on any code that uses it, to alert other developers that the PRNG needs to remain unchanged.

Manually-Seeded PRNG Use Cases

Use cases for manually-seeded PRNGs include the following:

Manually-Seeded PRNGs in Games

Many kinds of game software generate seemingly "random" game content that might need to be repeatedly regenerated, such as—

In general, the bigger that "random" content is, the greater the justification to use a manually-seeded PRNG and a custom seed to generate that content. The following are special cases:

  1. If the game needs reproducible "random" content only at the start of the game session (e.g., a "random" game board or a "random" order of virtual cards) and that content is small (say, no more than a hundred numbers):
    • The game should not use a manually-seeded PRNG unless the seed is based on a "code" or "password" entered by the user. This is a good sign that the game ought to store the "random" content instead of a seed.
  2. In a networked game where multiple computers (e.g., multiple players, or a client and server) have a shared view of the game state and random numbers are used to update that game state:
    • The game should not use a manually-seeded PRNG where predicting a random outcome could give a player a significant and unfair advantage (e.g., the random number is the result of a die roll, or the top card of the draw pile, for a board or card game). The game may use such a PRNG in other cases to ensure the game state is consistent among computers, including in physics simulations and AI.


  1. Suppose a game generates a map with random terrain (which uses lots of random numbers) and shows the player a "code" to generate that map (such as a barcode or a string of letters and digits). In this case, the game—

    • may change the algorithm it uses to generate random maps, but
    • should use, in connection with the new algorithm, "codes" that can't be confused with "codes" it used for previous algorithms, and
    • should continue to generate the same random map using an old "code" when the player enters it, even after the change to a new algorithm.
  2. Suppose a game implements a chapter that involves navigating a randomly generated dungeon with randomly scattered monsters and items. If the layout of the dungeon, monsters, and items has to be the same for a given week and for all players, the game can seed a PRNG with a hash code generated from the current week, the current month, the current year, and, optionally, a constant sequence of bits.

Single Random Value

If an application requires only one random value, with a fixed number of bits, then the application can pass the seed to a hash function rather than a PRNG. Examples of this include the following:

Ensuring Reproducibility

To ensure that a manually-seeded PRNG delivers reproducible "random" numbers across computers, across runs, and across application versions, an application needs to take special care. Reproducibility is often not achievable if the application relies on features or behavior outside the application's control, including any of the following:

Thus, an application ought to use manually-seeded PRNGs only when necessary, to minimize the need for reproducible "randomness". Where reproducibility is required, the application ought to avoid floating-point numbers, nondeterministic features, and other behavior outside its control, and ought to stick to the same versions of algorithms it uses.

As for reproducible PRNGs, java.util.Random is one example of a PRNG with consistent behavior, but none of the following is such a PRNG:

Nondeterministic Sources and Seed Generation

RNGs ultimately rely on so-called nondeterministic sources; without such sources, no computer can produce random numbers.

What Is a Nondeterministic Source?

A nondeterministic source is a source that doesn't give the same output for the same input each time (for example, a clock that doesn't always give the same time). There are many kinds of them, but sources useful for random number generation have hard-to-guess output (that is, they have high entropy; see the next section). They include—

RFC 4086, "Randomness Requirements for Security", section 3, contains a survey of nondeterministic sources.

Note: Online services that make random numbers available to applications, as well as the noise registered by microphone and camera recordings (see RFC 4086 sec. 3.2.1, (Liebow-Feeser 2017a)(12), and (Liebow-Feeser 2017b)(13)), are additional nondeterministic sources. However, online services require Internet or other network access, and some of them require access credentials. Also, many mobile operating systems require applications to declare network, camera, and microphone access to users upon installation. For these reasons, these kinds of sources are not recommended if other approaches are adequate.

Example: A program could ask users to flip coins or roll dice and type in their results. If users do so, the results typed this way will have come from nondeterministic sources (here, coins or dice).

What Is Entropy?

Entropy is a value that describes how hard it is to guess a nondeterministic source's output, compared to ideal random data; this is generally the size in bits of the ideal random data. (For example, a 64-bit output with 32 bits of entropy is as hard to guess as an ideal random 32-bit data block.) NIST SP 800-90B recommends min-entropy as the entropy measure. Characterizing a nondeterministic source's entropy is nontrivial and beyond the scope of this document. See also RFC 4086 section 2.

Seed Generation

In general, there are two steps to generate an N-bit seed for a PRNG(14):

  1. Gather enough data from independent nondeterministic sources to reach N bits of entropy or more.
  2. Then, condense the data into an N-bit number, a process called randomness extraction.

See my Note on Randomness Extraction. It should be mentioned, though, that in information security applications, unkeyed hash functions should not be used by themselves in randomness extraction.

Seed Generation for Noncryptographic PRNGs

In general, to generate a seed allowed by a noncryptographic PRNG, an application ought to use a cryptographic RNG or a method described in the previous section.

It is not recommended to seed PRNGs with timestamps, since they can carry the risk of generating the same "random" number sequence accidentally.(15)

Seeding Multiple Processes

Some applications require multiple processes (including threads, tasks, or subtasks) to use reproducible "random" numbers for the same purpose. An example is multiple instances of a simulation with random starting conditions. However, noncryptographic PRNGs tend to produce number sequences that are correlated to each other, which is undesirable for simulations in particular.

To reduce this correlation risk, the application can choose a high-quality PRNG that supports streams of uncorrelated sequences (sequences that behave like independent random number sequences and don't overlap) and has an efficient way to assign a different stream to each process. For example, in some PRNGs, these streams can be formed—

Multiple processes can be seeded for random number generation as follows.(17)

  1. Stream case. If the PRNG supports streams as described above: Generate a seed (or use a predetermined seed), then:

    1. Create a PRNG instance for each process.
    2. Hash the seed and a fixed identifier to generate a new seed allowed by the PRNG.
    3. For each process, advance the PRNG to the next stream (unless it's the first process), then give that process a copy of the PRNG's current internal state.
  2. General case. For other PRNGs, or if each process uses a different PRNG design, the following is a way to seed multiple processes for random number generation, but it carries the risk of generating seeds that lead to overlapping, correlated, or even identical number sequences, especially if the processes use the same PRNG.(18) Generate a seed (or use a predetermined seed), then:

    1. Create a PRNG instance for each process. The instances need not all use the same PRNG design or the same parameters; for example, some can be SFC64 and others xoroshiro128**.
    2. For each process, hash the seed, a unique number for that process, and a fixed identifier to generate a new seed allowed by the process's PRNG, and initialize that PRNG with the new seed.
  3. Leapfrogging (Bauke and Mertens 2007)(19). The following is an alternative way to initialize a PRNG for each process if the number of processes (N) is small. Generate a seed (or use a predetermined seed), then:

    1. Create one PRNG instance. Hash the seed and a fixed identifier to generate a new seed allowed by the PRNG.
    2. Give each process a copy of the PRNG's state. Then, for the second process, discard 1 output from its PRNG; for the third process, discard 2 outputs from its PRNG; and so on.
    3. Now, whenever a PRNG created this way produces an output, it then discards the next N minus 1 outputs before finishing.

Note: The steps above include hashing several things to generate a new seed. This has to be done with either a hash function of N or more bits (where N is the PRNG's maximum seed size), or a so-called "seed sequence generator" like C++'s std::seed_seq.(20)


  1. Philox4×64-7 is a counter-based PRNG that supports one stream per seed. To seed two processes based on the seed "seed" and this PRNG, an application can—

    • take the SHA2-256 hash of "seed-mysimulation" as a new seed,
    • initialize the first process's PRNG with the new seed and a counter of 0, and
    • initialize the second process's PRNG with 1 plus the new seed and a counter of 0.
  2. Some dynamic threading (task-parallel) platforms employ task schedulers where tasks or subtasks (sometimes called strands or fibers) are not assigned to a particular operating system process or thread. To ensure reproducible "randomness" in these platforms, PRNGs have to be assigned to tasks (rather than system processes or threads) and are not shared between tasks, and each task's PRNG can be initialized as given in the "general case" steps above (where the task's unique number is also known as a pedigree) (Leierson et al., 2012)(10).

Existing RNG APIs in Programming Languages

As much as possible, applications should use existing libraries and techniques for cryptographic and high-quality RNGs. The following table lists application programming interfaces (APIs) for such RNGs for popular programming languages.

Language Cryptographic High-Quality
.NET (incl. C# and VB.NET) (H) RandomNumberGenerator.Create() in System.Security.Cryptography namespace; airbreather/AirBreather.Common library (CryptographicRandomGenerator) XoshiroPRNG.Net package (XoRoShiRo128starstar, XoShiRo256plus, XoShiRo256starstar); Data.HashFunction.MurmurHash or Data.HashFunction.CityHash package (hash the string seed + "_" + counter)
C/C++ (G) (C) xoroshiro128plusplus.c; xoshiro256starstar.c
Python (A) secrets.SystemRandom (since Python 3.6); os.urandom() ihaque/xorshift library (default seed uses os.urandom()); numpy.random.Generator with Philox or SFC64 (since ver. 1.7); hashlib.md5(b"%d_%d" % (seed, counter)).digest(), hashlib.sha1(b"%d_%d" % (seed, counter)).digest()
Java (A) (D) (C); java.security.SecureRandom (F) it.unimi.dsi/dsiutils artifact (XoRoShiRo128PlusPlusRandom, XoRoShiRo128StarStarRandom, XoShiRo256StarStarRandom, XorShift1024StarPhiRandom); org.apache.commons/commons-rng-simple artifact (RandomSource of SFC_64, XO_RO_SHI_RO_128_PP, XO_RO_SHI_RO_128_SS, XO_SHI_RO_256_PP, or XO_SHI_RO_256_SS)
JavaScript (B) crypto.randomBytes(byteCount) (node.js only); random-number-csprng package (node.js only); crypto.getRandomValues() (Web) xoroshiro128starstar package; md5 package (md5(seed+"_"+counter, {asBytes: true})); murmurhash3js package (murmurhash3js.x86.hash32(seed+"_"+counter)); crypto.createHash("sha1") (node.js only)
Ruby (A) (E) (C); SecureRandom.rand() (0 or greater and less than 1) (E); SecureRandom.rand(N) (integer) (E) (for both, require 'securerandom'); sysrandom gem Digest::MD5.digest("#{seed}_#{counter}"), Digest::SHA1.digest("#{seed}_#{counter}") (for both, require 'digest')
PHP (A) random_int(), random_bytes() (both since PHP 7) md5($seed.'_'.$counter, true); sha1($seed.'_'.$counter, true)
Go crypto/rand package md5.Sum in crypto/md5 package or sha1.Sum in crypto/sha1 package (for both, hash the byte array seed + "_" + counter)
Rust (C) rand_xoshiro crate (Xoroshiro128PlusPlus, Xoshiro256PlusPlus, Xoshiro256StarStar, Xoshiro512StarStar)
Perl Crypt::URandom module Crypt::Digest::MD5 module (md5($seed.'_'.$counter)); Digest::SHA module (sha1($seed.'_'.$counter)); Digest::MurmurHash3 module (murmurhash3($seed.'_'.$counter))
Other Languages (C) Hash the string seed + "_" + counter with MurmurHash3, xxHash64, CityHash, MD5, or SHA-1

Hash Functions

A hash function is a function that takes an arbitrary input of any size (such as an array of 8-bit bytes or a sequence of characters) and returns an output with a fixed number of bits. That output is also known as a hash code.

For random number generation purposes:

The use of hash functions for other purposes (such as data lookup and data integrity) is beyond the scope of this document. See my note on hash functions.

Procedural Noise Functions

Noise is a randomized variation in images, sound, and other data.(26)

A noise function is similar to a hash function; it takes an n-dimensional point and, optionally, additional data, and outputs a seemingly random number.(27) Noise functions generate procedural noise such as cellular noise, value noise, and gradient noise (including Perlin noise). If the noise function takes additional data, that data—

Pseudorandom Functions

A pseudorandom function is a kind of hash function that takes—

and outputs a seemingly random number. (If the output is encryption keys, the function is also called a key derivation function; see NIST SP 800-108.) Some pseudorandom functions deliberately take time to compute their output; these are designed above all for cases in which the secret is a password or is otherwise easy to guess — examples of such functions include PBKDF2 (RFC 2898), scrypt (RFC 7914), and Ethash. Pseudorandom functions are also used in proofs of work such as the one described in RFC 8019 sec. 4.4.

RNG Topics

This section discusses several important points on the use and selection of RNGs, including things to consider when shuffling or generating "unique" random numbers.


In a list with N different items, there are N factorial (that is, 1 * 2 * ... * N, or N!) ways to arrange the items in that list. These ways are called permutations(28).

In practice, an application can shuffle a list by doing a Fisher–Yates shuffle, which is unfortunately easy to mess up — see (Atwood 2007)(29) — and is implemented correctly in another document of mine.

However, if a PRNG admits fewer seeds than the number of permutations, then there are some permutations that that PRNG can't choose when it shuffles that list. (This is not the same as generating all permutations of a list, which, for a list big enough, can't be done by any computer in a reasonable time.)

On the other hand, for a list big enough, it's generally more important to have shuffles act random than to choose from among all permutations.

An application that shuffles a list can do the shuffling—

  1. using a cryptographic RNG, preferably one with a security strength of b bits or greater, or
  2. if a noncryptographic RNG is otherwise appropriate, using a high-quality PRNG that—
    • admits b-bit seeds without shortening or compressing those seeds, and
    • is initialized with a seed derived from data with at least b bits of entropy, or "randomness".

For shuffling purposes, b can usually be calculated by taking n factorial minus 1 (where n is the list's size) and calculating its bit length. A Python example is b = (math.factorial(n)-1).bit_length(). See also (van Staveren 2000, "Lack of randomness")(30). For shuffling purposes, an application may limit b to 256 or greater, in cases when variety of permutations is not important. For other sampling tasks, the following Python examples show how to calculate b:

Unique Random Identifiers

Some applications require generating unique identifiers, especially to identify database records or other shared resources. Examples of unique values include auto-incremented numbers, sequentially assigned numbers, primary keys of a database table, and combinations of these. Applications have also generated unique values at random.

The following are some questions to consider when generating unique identifiers:

  1. Can the application easily check identifiers for uniqueness within the desired scope and range (e.g., check whether a file or database record with that identifier already exists)(31)?
  2. Can the application tolerate the risk of generating the same identifier for different resources(32)?
  3. Do identifiers have to be hard to guess, be simply "random-looking", or be neither?
  4. Do identifiers have to be typed in or otherwise relayed by end users(33)?
  5. Is the resource an identifier identifies available to anyone who knows that identifier (even without being logged in or authorized in some way)?(34)
  6. Do identifiers have to be memorable?

Some applications may also care about "unique random" values. Generally, however, values that are both unique and random are impossible. Thus, applications that want "unique random" values have to either settle for numbers that merely "look random"; or check for or tolerate possible duplicates; or pair random numbers with unique ones.

If the application can settle for "random-looking" unique integers:

An application that generates unique identifiers should do so as follows:

This section doesn't discuss how to format a unique value into a text string (such as a hexadecimal or alphanumeric string), because ultimately, doing so is the same as mapping unique values one-to-one with formatted strings (which will likewise be unique).

Verifiable Random Numbers

Verifiable random numbers are random numbers (such as seeds for PRNGs) that are disclosed along with all the information necessary to verify their generation. Usually, such information includes random numbers and/or uncertain data to be determined and publicly disclosed in the future. Techniques to generate verifiable random numbers (as opposed to cryptographic RNGs alone) are used whenever one party alone can't be trusted to produce a number at random. Verifiable random numbers that are disclosed publicly should not be used as encryption keys or other secret parameters.


  1. Generating verifiable randomness has been described in RFC 3797, which describes the selection process for the Nominations Committee (NomCom) of the Internet Engineering Task Force.
  2. Verifiable delay functions calculate an output as well as a proof that the output was correctly calculated; these functions deliberately take much more time to calculate the output (e.g., to generate a seemingly random number from public data) than to verify its correctness.(36) In many cases, such a function deliberately takes much more time than the time allowed to contribute randomness to that function.(37)
  3. In a so-called commitment scheme, one computer generates data to be committed (e.g. a random number or a chess move), then reveals its hash code or digital signature (commitment), and only later reveals to all participants the committed data (along with other information needed, if any, to verify that the data wasn't changed in between). Examples of commitment schemes are hash-based commitments.(37)
  4. So-called mental card game (mental poker) schemes can be used in networked games where a deck of cards has to be shuffled and dealt to players, so that the identity of some cards is known to some but not all players.(37)

Guidelines for New RNG APIs

This section contains guidelines for those seeking to implement RNGs designed for wide reuse (such as in a programming language's standard library). As mentioned earlier, an application should use existing RNG implementations whenever possible.

This section contains suggested requirements on cryptographic and high-quality RNGs that a new programming language can choose to adopt.

Cryptographic RNGs: Requirements

A cryptographic RNG generates random bits that behave like independent uniform random bits, such that an outside party has no more than negligible advantage in correctly guessing prior or future unseen output bits of that RNG even after knowing how the RNG works and/or extremely many outputs of the RNG, or prior unseen output bits of that RNG after compromising its security, such as reading its internal state.(38)

If a cryptographic RNG implementation uses a PRNG:

A cryptographic RNG is not required to reseed itself.

Examples: The following are examples of cryptographic RNGs:

High-Quality RNGs: Requirements

A PRNG is a high-quality RNG if—

The high-quality PRNG should admit any of 2127 or more seeds.

Every cryptographic RNG is also a high-quality RNG.

Examples: Examples of high-quality PRNGs include xoshiro256**, xoroshiro128**, xoroshiro128++, Philox4×64-7, and SFC64. I give additional examples in a separate page.

Designs for PRNGs

The following are some ways a PRNG can be implemented:

Implementing New RNG APIs

A programming language API designed for reuse by applications could implement RNGs using the following guidelines:

  1. The RNG API can include a method that fills one or more memory units (such as 8-bit bytes) completely with random bits. See example 1.
  2. If the API implements an automatically-seeded RNG, it should not allow applications to initialize that same RNG with a seed for reproducible "randomness"(42) (it may provide a separate PRNG to accept such a seed). See example 2.
  3. If the API provides a PRNG that an application can seed for reproducible "randomness", it should document that PRNG and any methods the API provides that use that PRNG (such as shuffling and Gaussian number generation), and should not change that PRNG or those methods in a way that would change the "random" numbers they deliver for a given seed. See example 2.
  4. A new programming language's standard library ought to include the following methods for generating numbers that behave like independent uniform random numbers (see my document on random number generation methods for details).
    • Four methods for random integers: 0 to n including n, 0 to n excluding n, a to b including b, and a to b excluding b.
    • Four methods for random numbers bounded by 0 and 1, with and without the endpoints.
    • Four methods for random numbers bounded by a and b, with and without the endpoints.



  1. A C language RNG method for filling memory could look like the following: int random(uint8_t[] bytes, size_t size);, where bytes is a pointer to an array of 8-bit bytes, and size is the number of random 8-bit bytes to generate, and where 0 is returned if the method succeeds and nonzero otherwise.
  2. A Java API that follows these guidelines can contain two classes: a RandomGen class that implements an unspecified but general-purpose RNG, and a RandomStable class that implements an SFC64 PRNG that is documented and will not change in the future. RandomStable includes a constructor that takes a seed for reproducible "randomness", while RandomGen does not. Both classes include methods described in point 4, but RandomStable specifies the exact algorithms to those methods and RandomGen does not. At any time in the future, RandomGen can change its implementation to use a different RNG while remaining backward compatible, while RandomStable has to use the same algorithms for all time to remain backward compatible, especially because it takes a seed for reproducible "randomness".


I acknowledge—


An alternative for GLSL and other fragment or pixel shaders to support randomness is to have the shader sample a "noise texture" with random data in each pixel; for example, C. Peters, "Free blue noise textures", Moments in Graphics, Dec. 22, 2016, discusses how so-called "blue noise" can be sampled this way.

See also N. Reed, "Quick And Easy GPU Random Numbers In D3D11", Nathan Reed's coding blog, Jan. 12, 2013.

(A) "Post-Spectre Threat Model Re-Think" in the Chromium source code repository (May 29, 2018).
(B) Bernstein, D.J. "Entropy Attacks!", Feb. 5, 2014.
(C) Everspaugh, A., Zhai, Y., et al. "Not-So-Random Numbers in Virtualized Linux and the Whirlwind RNG", 2014.
(D) Ristenpart, T., Yilek, S. "When Good Randomness Goes Bad: Virtual Machine Reset Vulnerabilities and Hedging Deployed Cryptography", 2010.
For a detailed notion of a secure RNG, see Coretti, Dodis, et al., "Seedless Fruit is the Sweetest: Random Number Generation, Revisited", 2019.


Any copyright to this page is released to the Public Domain. In case this is not possible, this page is also licensed under Creative Commons Zero.