How can I best generate a static array of random number on demand?

后端 未结 8 786
北海茫月
北海茫月 2021-01-14 06:56

An application I\'m working on requires a matrix of random numbers. The matrix can grow in any direction at any time, and isn\'t always full. (I\'ll probably end up re-imple

相关标签:
8条回答
  • 2021-01-14 07:07

    As far as I see, there are 2 basic algorithms possible here:

    • Generate a new random number based on func(seed, coord) for each coord
    • Generate a single random number from seed, and then transform it for the coord (something like rotate(x) + translate(y) or whatever)

    In the first case, you have the problem of always generating new random numbers, although this may not be as expensive as you fear.

    In the second case, the problem is that you may lose randomness during your transformation operations. However, in either case the result is reproducible.

    0 讨论(0)
  • 2021-01-14 07:10

    What you're talking about is typically called "Perlin Noise", here's a link for you: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

    The most important thing in that article is the noise function in 2D:

      function Noise1(integer x, integer y)
        n = x + y * 57
        n = (n<<13) ^ n;
        return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);    
      end function
    

    It returns a number between -1.0 and +1.0 based on the x and y coordonates alone (and a hard coded seed that you can change randomly at the start of your app or just leave it as it is).

    The rest of the article is about interpolating these numbers, but depending on how random you want these numbers, you can just leave them as it is. Note that these numbers will be utterly random. If you instead apply a Cosine Interpolator and use the generated noise every 5-6 indexes, interpolating inbetween, you get heightmap data (which is what I used it for). Skip it for totally random data.

    0 讨论(0)
  • 2021-01-14 07:10

    I think your first idea of instantiating a new Random object seeded by some deterministic hash of (x-coordinate, y-coordinate, LazyRandomMatrix seed) is probably reasonable for most situations. In general, creating lots of small objects on the managed heap is something the CLR is very good at handling efficiently. And I don't think Random.ctor() is terribly expensive. You can easily measure the performance if it's a concern.

    A very similar solution which may be easier than creating a good deterministic hash is to use two Random objects each time. Something like:

    public int this[int x, int y]
    {
        get
        {
            Random r1 = new Random(_seed * x);
            Random r2 = new Random(y);
            return (r1.Next() ^ r2.Next());
        }
    }
    
    0 讨论(0)
  • 2021-01-14 07:12

    Here is a solution based on a SHA1 hash. Basically this takes the bytes for the X, Y and Seed values and packs this into a byte array. Then a hash for the byte array and the first 4 bytes of the hash used to generate an int. This should be pretty random.

    public class LazyRandomMatrix 
    {
      private int _seed;
      private SHA1 _hashProvider = new SHA1CryptoServiceProvider();
    
      public LazyRandomMatrix(int seed)
      {
        _seed = seed;
      }
    
      public int this[int x, int y]
      {
        get
        {
          byte[] data = new byte[12];
          Buffer.BlockCopy(BitConverter.GetBytes(x), 0, data, 0, 4);
          Buffer.BlockCopy(BitConverter.GetBytes(y), 0, data, 4, 4);
          Buffer.BlockCopy(BitConverter.GetBytes(_seed), 0, data, 8, 4);
    
          byte[] hash = _hashProvider.ComputeHash(data);
          return BitConverter.ToInt32(hash, 0);
        }
      }     
    }
    
    0 讨论(0)
  • 2021-01-14 07:19

    A pseudo-random number generator is essentially a function that deterministically calculates a successor for a given value.

    You could invent a simple algorithm that calculates a value from its neighbours. If a neighbour doesn't have a value yet, calculate its value from its respective neighbours first.

    Something like this:

    • value(0,0) = seed
    • value(x+1,0) = successor(value(x,0))
    • value(x,y+1) = successor(value(x,y))

    Example with successor(n) = n+1 to calculate value(2,4):

     \ x  0      1      2
    y  +-------------------
     0 | 627    628    629
     1 |               630
     2 |               631
     3 |               632
     4 |               633
    

    This example algorithm is obviously not very good, but you get the idea.

    0 讨论(0)
  • 2021-01-14 07:23

    You want a random number generator with random access to the elements, instead of sequential access. (Note that you can reduce your two coordinates into a single index i.e. by computing i = x + (y << 16).)

    A cool example of such a generator is Blum Blum Shub, which is a cryptographically secure PRNG with easy random-access. Unfortunately, it is very slow.

    A more practical example is the well-known linear congruential generator. You can easily modify one to allow random access. Consider the definition:

    X(0) = S
    X(n) = B + X(n-1)*A (mod M)
    

    Evaluating this directly would take O(n) time (that's pseudo linear, not linear), but you can convert to a non-recursive form:

    //Expand a few times to see the pattern:
    X(n) = B + X(n-1)*A (mod M)
    X(n) = B + (B + X(n-2)*A)*A (mod M)
    X(n) = B + (B + (B + X(n-3)*A)*A)*A (mod M)
    //Aha! I see it now, and I can reduce it to a closed form:
    X(n) = B + B*A + B*A*A + ... + B*A^(N-1) + S*A^N (mod M)
    X(n) = S*A^N + B*SUM[i:0..n-1](A^i) (mod M)
    X(n) = S*A^N + B*(A^N-1)/(A-1) (mod M)
    

    That last equation can be computed relatively quickly, although the second part of it is a bit tricky to get right (because division doesn't distribute over mod the same way addition and multiplication do).

    0 讨论(0)
提交回复
热议问题