1

I am looking for a random number generator with 3 inputs for a terrain generator. The inputs are an x, y (position) and a seed value. And the function returns back a random number from 0-1.

So far I found this question, but this has 2 inputs. Although I can combine x and y to a single number, to get two inputs, this will restrict the choices I will have for x, y as they need to be sufficiently large (the terrain is infinite).

Is there any random function that takes in 3 inputs, or would I need to end up using the 2 input version?

  • What type are the three inputs? uint32/uint64/double/something else? – Leo Mar 10 '22 at 10:57
  • uint32 is the type @Leo – Kotetsu Chan Mar 10 '22 at 11:11
  • 1
    Your intention to pass a seed value for every invocation of your generator indicates a conceptual misunderstanding of how PRNGs work. Frequent manual seeding will not increase the quality of the pseudo-random numbers, and in fact usually does the opposite. – pjs Mar 10 '22 at 14:21
  • 1
    @pjs I don't think the intention is to increase the quality of the numbers. It is to have a repeatable way to generate a 2D map from different seeds while being able to generate the points in an arbitrary order (like when the player moves around in directions they pick). Kind of like a CTR mode of a cipher but non-cryptographic. – Leo Mar 10 '22 at 14:29
  • 1
    This is similar to an [earlier SO question #61969876](https://stackoverflow.com/questions/61969876/random-number-quality-with-given-seed-in-haskell). You can combine x and y with the [Cantor pairing function](https://en.wikipedia.org/wiki/Pairing_function). Then use the result of the pairing function as an offset into some pseudo-random serie. Pseudo-random generators that efficiently support this arbitrary access into their serie can be CTRs, or MRGs such as [MRG32k3a](https://www.isi.edu/nsnam/ns/doc/node267.html). That way, only a single seed value is required for the whole game. – jpmarinier Mar 10 '22 at 16:43
  • @Leo Reproducibility makes sense, but the design approach seems flawed to me. If the seed is used for reproducibility at the level of a map, then it seems to me that a seed should be statically associated with a given map rather than an argument which is passed in with each invocation. – pjs Mar 10 '22 at 17:52

1 Answers1

2

Something like this should work. It takes three 32-bit integers, and outputs a single 32-bit integer. If you want to turn the output into a double between 0 and 1, just divide by UINT32_MAX.

The input and output sizes can be adjusted.

You can also tweak the balance between output quality and speed. You'll notice that the middle section is just repeated 3 lines, remove or add more of them to make the output more or less biased.

Here's the C code.

uint32_t rotl32(uint32_t n, uint8_t k) {
  uint32_t a = n << k;
  uint32_t b = n >> (32 - k);
  return a | b;
}

uint32_t three_input_random(uint32_t x, uint32_t y, uint32_t z) {
  uint32_t a = x;
  uint32_t b = y;
  uint32_t c = z;

  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);
  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);
  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);

  return a + b + c + x + y + z;
}
Leo
  • 1,273
  • 9
  • 14
  • I found many implementations of [noise functions in GLSL](https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl) that can be used for this purpose as well. – Anderson Green Jun 10 '22 at 15:04