14

I am looking for a pseudo random number generator which would be specialized to work fast when it is given a seed before generating each number. Most generators I have seen so far assume you set seed once and then generate a long sequence of numbers. The only thing which looks somewhat similar to I have seen so far is Perlin Noise, but it generates too "smooth" data - for similar inputs it tends to produce similar results.

The declaration of the generator should look something like:

int RandomNumber1(int seed);

Or:

int RandomNumber3(int seedX, int seedY, int seedZ);

I think having good RandomNumber1 should be enough, as it is possible to implement RandomNumber3 by hashing its inputs and passing the result into the RandomNumber1, but I wrote the 2nd prototype in case some implementation could use the independent inputs.

The intended use for this generator is to use it for procedural content generator, like generating a forest by placing trees in a grid and determining a random tree species and random spatial offsets for each location.

The generator needs to be very efficient (below 500 CPU cycles), because the procedural content is created in huge quantities in real time during rendering.

MrValdez
  • 8,515
  • 10
  • 56
  • 79
Suma
  • 33,181
  • 16
  • 123
  • 191
  • The reason Perlin noise is similar to what you're asking for is that Perlin noise uses a deterministic (repeatable) pseudorandom function to do part of its job (and then smooths the result). If you look at a Perlin noise implementation, especially the earlier pre-"improved" ones, you will often find the type of efficient, repeatable "random" function you're looking for, though the language, domain and range will vary. E.g. `RandomNumber(vec2 seed, float x, float y) { return fract(sin(dot(seed + vec2(fx, fy), vec2(12.9898,78.233))) * 43758.5453); }` (GLSL ES) – LarsH Jan 05 '17 at 16:18
  • 2
    I've been trying to research this question too, and have come to the conclusion that the word "generator" implies the sequential, streaming behavior that we're trying to avoid. This is why a PRN**G** is usually understood as providing stateful "functions", not strictly deterministic ones. Maybe we'd have better success in research if we searched for PRNF (function) rather than PRNG. https://blogs.unity3d.com/2015/01/07/a-primer-on-repeatable-random-numbers/ calls them "random hash functions." – LarsH Jan 05 '17 at 16:27

6 Answers6

21

Seems like you're asking for a hash-function rather than a PRNG. Googling 'fast hash function' yields several promising-looking results.

For example:

uint32_t hash( uint32_t a)
    a = (a ^ 61) ^ (a >> 16);
    a = a + (a << 3);
    a = a ^ (a >> 4);
    a = a * 0x27d4eb2d;
    a = a ^ (a >> 15);
    return a;
}

Edit: Yep, some hash functions definitely look more suitable than others.

For your purposes, it should be sufficient to eyeball thefunction and check that a single-bit change in the input will propagate to lots of output bits.

  • I hope this is a good direction to go. At first look it seems to me while hash functions do have one important property (uniform distribution), I am not quite sure if its output can be considered "random" - how do I know for a particular function how much does its output resemble noise? – Suma Oct 03 '08 at 18:30
  • 4
    One test for a good hash function is to give it the sequence of integers 0, 1, 2.. and test the output for 'randomness' using pseudo random number generator tests. – Aaron Oct 03 '08 at 22:45
  • 3
    Good answer, though I disagree with "hash function *rather than* a PRNG." In general, hash functions are not always good random number generators (they're designed more for other properties: https://en.wikipedia.org/wiki/Hash_function#Properties), and the OP *does* need a certain quality of randomness, or his forests will look fake. That being said, some hash functions make "random-enough" PRNGs, and they're certainly deterministic as the OP is asking for. – LarsH Jan 05 '17 at 16:01
9

Yep, you are looking for a fast integer hash algorithm rather than a PRNG.

This page has a few algorithms, I'm sure you'll find plenty more now you know the correct search terms.

Edit: The original page has been removed, a live version can be found on GitHub.

Zano
  • 2,595
  • 27
  • 33
Matt Howells
  • 40,310
  • 20
  • 83
  • 102
4

Here's a small random number generator developed by George Marsaglia. He's an expert in the field, so you can be confident the generator has good statistical properties.

v = 36969*(v & 65535) + (v >> 16);
u = 18000*(u & 65535) + (u >> 16);
return (v << 16) + (u & 65535);

Here u and v are unsigned ints. Initialize them to any non-zero values. Each time you generate a random number, store u and v somewhere. You could wrap this in a function to match your signature above (except the ints are unsigned.)

Jarrod Moldrich
  • 405
  • 5
  • 6
John D. Cook
  • 29,517
  • 10
  • 67
  • 94
  • 1
    Unfortunately this does not match the question. I need to provide my own U and V each time, not to have them stored somewhere and updated between iterations. The function needs to always produce the same output given the same inputs. – Suma Oct 23 '08 at 11:20
  • @Suma: Why can't you provide your own U and V each time if you just pass them as parameters to this function? And having the same U and same V each time will also always produce the same result. It matches your question exactly! – Mecki Jan 21 '09 at 10:42
  • 2
    I tried this and did not get good results. Varying u by 1 does not vary the output significantly. – Aranda Jun 23 '13 at 08:05
2

see std::tr1::ranlux3, or other random number generators that are part of TR1 additions to the standard C++ library. I suggested mt19937 initialially, but then saw your note that it needs to be very fast. TR1 is should be available on Microsoft VC++ and GCC, and can also be found in the boost libraries which support even more compilers.

example adapted from boost documentation:

#include <random>
#include <iostream>
#include <iterator>
#include <functional>
#include <algorithm>
#include <ctime>
using namespace std;
using namespace std::tr1;
int main(){
    random_device trueRand;
    ranlux3 rng(trueRand);  // produces randomness out of thin air
                            // see pseudo-random number generators
    uniform_int<> six(1,6); // distribution that maps to 1..6
                            // see random number distributions
    variate_generator<ranlux3&, uniform_int<> >
           die(rng, six);   // glues randomness with mapping

    // simulate rolling a die
    generate_n( ostream_iterator<int>(cout, " "), 10, ref(die));
}

example output:

2 4 4 2 4 5 4 3 6 2

Any TR1 random number generator can seed any other random number generator. If you need higher quality results, consider feeding the output of mt19937 (which is slower, but higher quality) into a minstd_rand or randlux3, which are faster generators.

Aaron
  • 3,454
  • 23
  • 26
0

If memory is not really an issue and speed is of utmost importance then you can prebuild a large array of random numbers and just iterate through it at runtime. For example have a seperate program generate 100,000 random numbers and save it as it's own file like

unsigned int randarray []={1,2,3,....}

then include that file into your compile and at runtime your random number function only needs to pull numbers from that array and loop back to the start when it hits the end.

KPexEA
  • 16,560
  • 16
  • 61
  • 78
  • Computing a simple hash as in http://stackoverflow.com/questions/167735/#167764 will almost always be faster than accessing a huge array (huge array will not fit into the cache, therefore accessing it is slow) – Suma Oct 06 '08 at 11:07
  • I just profiled it on my PC and using my lookup table method vs the hash function, the lookup table is 13x faster. – KPexEA Oct 06 '08 at 19:09
  • The lookup table can be faster when it is small enough to fit into L2 cache, and when you are not using the L2 cache for anything else - which was most likely the case in your test. If you want to test real world performance, you need to perform some significant data processing between the lookups. – Suma Oct 08 '08 at 11:39
0

I use the following code in my Java random number library - this has worked pretty well for me. I also use this for generating procedural content.

/**
 * State for random number generation
 */
private static volatile long state=xorShift64(System.nanoTime()|0xCAFEBABE);

/**
 * Gets a long random value
 * @return Random long value based on static state
 */
public static long nextLong() {
    long a=state;
    state = xorShift64(a);
    return a;
}

/**
 * XORShift algorithm - credit to George Marsaglia!
 * @param a initial state
 * @return new state
 */
public static final long xorShift64(long a) {
    a ^= (a << 21);
    a ^= (a >>> 35);
    a ^= (a << 4);
    return a;
}
mikera
  • 105,238
  • 25
  • 256
  • 415