views:

231

answers:

6

I'm looking for a random number that always generates the same "random" number for a given seed. The seed is defined by x + (y << 16), where x and y are positions on a heightmap.

I could create a new instance of System.Random every time with my seed, but thats a lot of GC pressure. Especially since this will be called a lot of times.

EDIT: "A lot" means half a million times.

Thanks to everyone that answered! I know I was unclear, but I learned here that a hash function is exactly what I want.

+3  A: 

I could create a new instance of System.Random every time with my seed

Do that.

but thats a lot of GC pressure. Especially since this will be called a lot of times.

How many times do you call it? Does it verifiably perform badly? Notice, the GC is optimized to deal with lots of small objects with short life time. It should deal with this easily.

And, what would be the alternative that takes a seed but doesn’t create a new instance of some object? That sounds rather like a badly designed class, in fact.

Konrad Rudolph
The alternative would be an object that you can just hand a seed value without requiring a constructor.
Robert Harvey
@Robert: yes, I subsumed that unter “bad design”: it needs a rather good justification to make an object resettable instead of relying on `new`: resettable objects make it a lot harder to reason about state.
Konrad Rudolph
@Konrad: I take it you always use immutable objects. Some of us still program using conventional OOP using mutable objects, and we don't consider it bad design, especially if we're not using multiple threads.
Robert Harvey
The best thing would be a simple function like int Random(int seed)
Hannesh
@Hannesh: no, that would be a really bad thing because it would obviously always return the same value – not random at all.
Konrad Rudolph
That is exactly what I want, that the number is the same for a given seed. If I created a sequence by calling this function with seeds from 1 to 10, the numbers within that sequence should appear like a random sequence. But revisting the same seed must give the same result.
Hannesh
@Hannesh: then please try to use the correct words. Terms like “random number generator” are well-defined and what you want is clearly something completely unrelated. As others have suggested, you are probably looking for some kind of hash function, not a random number generator.
Konrad Rudolph
@Konrad Apologies, I'm relatively new to programming in general. A hash function is what I want, I didn't know what it was before. I know I'm a bit unclear on what I want sometimes.
Hannesh
+1  A: 

See Simple Random Number Generation for C# source code. The state is just two unsigned integers, so it's easy to keep up with between calls. And the generator passes standard tests for quality.

John D. Cook
Looks great. Two unsigned integers is exactly what I happen to have!
Hannesh
A: 

I don't think a "random number generator" is actually what you're looking for. Simply create another map and pre-populate it with random values. If your current heightmap is W x H, the simplest solution would be to create a W x H 2D array and just fill each element with a random value using System.Random. You can then look up the pre-populated random value for a particular (x, y) coordinate whenever you need it.

Alternatively, if your current heighmap actually stores some kind of data structure, you could modify that to store the random value in addition to the height value.

A side benefit that this has is that later, if you need to, you can perform operations over the entire "random" map to ensure that it has certain properties. For example, depending on the context (is this for a game?) you may find later that you want to smooth the randomness out across the map. This is trivial if you precompute and store the values as I've described.

Nick Aceves
The map needs to be infinitely continuous, and musn't change if I revist an area. Because it needs to be infinitely continuous, I need a function for height at (x, y). I've worked out most of this, all I need now is the random number.
Hannesh
+2  A: 

Since a hash function is apparently closer to what you want, consider a variation of the following:

int Hash(int n) {
    const int prime = 1031;
    return (((n & 0xFFFF) * prime % 0xFFFF)) ^ (n >> 16);
}

This XORs the least significant two bytes with the most significant two bytes of a four-byte number after shuffling the least significant two byte around a little bit by multiplication with a prime number. The result is thus in the range 0 < 0x10000 (i.e. it fits in an Int16).

This should “shuffle” the input number a bit, reliably produces the same value for the same input and looks “random”. Now, I haven’t done a stochastic analysis of the distribution and if ever a statistician was to look at it, he would probably go straight into anaphylactic shock. (In fact, I have really written this implementation off the top of my head.)

If you require something less half-baked, consider using an established check sum (such as CRC32).

Konrad Rudolph
I was disappointed initially since writing the Hashed values for n = 1 - 10 gave a set of numbers where each was 1031 bigger than the last. Works better for higher numbers (obviously), but I got satisfying results when inputting the value back into the function (ie. n = Hash(Hash(n)) ).
Hannesh
If a statistician looks at it, he'll probably just consider the function not really random, and the results not really normally distributed. But neither of these things was the point of the question, so you don't have to fear for casualities. ;-)
Joris Meys
@Hannesh: You could just use an MD5 hash, truncate it to four bytes, and turn those bytes into a number. Or use any other hash in the same way.
Brian
MD5 hash is pretty expensive, it only needs to hashed enough that a human can't really see a pattern. I'm happy with the results from Konrads hash generator.
Hannesh
+1  A: 

What about storing a Dictionary<int, int> the provides the first value returned by a new Random object for a given seed?

class RandomSource
{
    Dictionary<int, int> _dictionary = new Dictionary<int, int>();

    public int GetValue(int seed)
    {
        int value;
        if (!_dictionary.TryGetValue(seed, out value))
        {
            value = _dictionary[seed] = new Random(seed).Next();
        }

        return value;
    }
}

This incurs the GC pressue of constructing a new Random instance the first time you want a value for a particular seed, but every subsequent call with the same seed will retrieve a cached value instead.

Dan Tao
A: 

CSharpCity provides source to several random number generators. You'll have to experiment to see whether these have less impact on performance than System.Random.

ExtremeOptimization offers a library with several generators. They also discuss quality and speed of the generators and compare against System.Random.

Finally, what do you mean by GC pressure? Do you really mean memory pressure, which is the only context I've seen it used in? The job of the GC is to handle the creation and destruction of gobs of objects very efficiently. I'm concerned that you're falling for the premature optimization temptation. Perhaps you can create a test app that gives some cold, hard numbers.

ebpower
I'm perfectly fine with the .NET's generator if used the way it's meant to be used, but i'd be recreating instances of System.Random half a million times since I wanted to set the seed. But it turns out what I really wanted was a hash function.
Hannesh