7

Given a consistently seeded Random:

Random r = new Random(0);

Calling r.Next() consistently produces the same series; so is there a way to quickly discover the N-th value in that series, without calling r.Next() N times?

My scenario is a huge array of values created via r.Next(). The app occasionally reads a value from the array at arbitrary indexes. I'd like to optimize memory usage by eliminating the array and instead, generating the values on demand. But brute-forcing r.Next() 5 million times to simulate the 5 millionth index of the array is more expensive than storing the array. Is it possible to short-cut your way to the Nth .Next() value, without / with less looping?

with
  • 306
  • 3
  • 15
  • It clearly depends on the algorithm used. I believe most PRNG use a function that calculates the next value from the previous one. There might be algorithms that allow calculating the N'th value directly though. The algorithm used by [System.Random](http://msdn.microsoft.com/en-us/library/system.random.aspx) is probably not among them. – dtb Mar 22 '11 at 00:48

4 Answers4

7

I don't know the details of the PRNG used in the BCL, but my guess is that you will find it extremely difficult / impossible to find a nice, closed-form solution for N-th value of the series.

How about this workaround:

Make the seed to the random-number generator the desired index, and then pick the first generated number. This is equally 'deterministic', and gives you a wide range to play with in O(1) space.

static int GetRandomNumber(int index)
{
    return new Random(index).Next();
} 
Ani
  • 111,048
  • 26
  • 262
  • 307
  • +1 I like it. This is also quite amendable to [LCGs](http://en.wikipedia.org/wiki/Linear_congruential_generator) with a single forward-seed. However then it's essential just a fixed map per LCG/generator.. :( –  Mar 22 '11 at 00:52
  • A couple of caveats. (1) Seeding the RNG may be expensive, so this may make your random number generation a lot slower than it otherwise would have been. (2) The iteration operation that the RNG does may not be all that random, so to speak. (For instance, linear congruential generations aren't all *that* bad, though they've rightly gone rather out of fashion, but if you apply this recipe to an LCG you get a very non-random sequence indeed.) For the .NET `Random` class, I think #1 applies but #2 probably doesn't. – Gareth McCaughan Mar 22 '11 at 00:56
  • I'd considered this but dismissed it assuming new Random() cost would be too expensive (as Gareth mentioned). However reading the other responses this sounds like the only way to "virtualize" a random number array using System.Random, so I'll rig it up and see what the performance is really like in my specific app. Failing that sounds like I should explore LCG. – with Mar 22 '11 at 01:32
2

In theory if you knew the exact algorithm and the initial state you'd be able to duplicate the series but the end result would just be identical to calling r.next().

Depending on how 'good' you need your random numbers to be you might consider creating your own PRNG based on a Linear congruential generator which is relatively easy/fast to generate numbers for. If you can live with a "bad" PRNG there are likely other algorithms that may be better to use for your purpose. Whether this would be faster/better than just storing a large array of numbers from r.next() is another question.

uesp
  • 6,194
  • 20
  • 15
  • I'm honestly not sure how "bad" of a PRNG I can get away with, I'm making Perlin noise so the only requirement is that the output visually look like Perlin noise. I'll read about LCG, thanks for the link. – with Mar 22 '11 at 01:39
  • 1
    There is also http://stackoverflow.com/questions/167735/fast-pseudo-random-number-generator-for-procedural-content SO question which has a few other ideas for fast PRNGs which may be useful to you. – uesp Mar 22 '11 at 01:46
  • that SO link was very helpful - good call. Integer hashing the would-be array index with a "world" seed (and then applying that hash result as a seed for a basic LCG) appears to be extremely fast and very likely to be perfectly adequate for my needs. – with Mar 22 '11 at 12:39
1

No, I don't believe there is. For some RNG algorithms (such as linear congruential generators) it's possible in principle to get the n'th value without iterating through n steps, but the Random class doesn't provide a way of doing that.

I'm not sure whether the algorithm it uses makes it possible in principle -- it's a variant (details not disclosed in documentation) of Knuth's subtractive RNG, and it seems like the original Knuth RNG should be equivalent to some sort of polynomial-arithmetic thing that would allow access to the n'th value, but (1) I haven't actually checked that and (2) whatever tweaks Microsoft have made might break that.

If you have a good enough "scrambling" function f then you can use f(0), f(1), f(2), ... as your sequence of random numbers, instead of f(0), f(f(0)), f(f(f(0))), etc. (the latter being roughly what most RNGs do) and then of course it's trivial to start the sequence at any point you please. But you'll need to choose a good f, and it'll probably be slower than a standard RNG.

Gareth McCaughan
  • 19,888
  • 1
  • 41
  • 62
-1

You could build your own on-demand dictionary of 'indexes' & 'random values'. This assumes that you will always 'demand' indexes in the same order each time the program runs or that you don't care if the results are the same each time the program runs.

Random rnd = new Random(0);
Dictionary<int,int> randomNumbers = new Dictionary<int,int>();
int getRandomNumber(int index)
{
    if (!randomNumbers.ContainsKey(index))
        randomNumbers[index] = rnd.Next();
    return randomNumbers[index];
}
Robert Levy
  • 28,747
  • 6
  • 62
  • 94