来自种子的随机数

我有一个应用程序,如果我的程序使用具有基于其种子的模式的RNG,它会变得非常明显,因为它根据景观的x坐标构建了景观。 如果你每次调用Next()Random运行良好,我每次使用相同的输入时都需要能够输出相同的输出,因此不能依赖Next() 。 相反,我尝试每次使用输入种子简单地创建一个新的Random 。 我知道,这不是一个好主意,它表明了。 模式非常明显,具有交替的高值和低值,并且在整个景观中具有明显的整体趋势。 我不想每次都制作新的生成器,但即便如此,我还是查看了加密安全的RandomNumberGenerator ,看看我是否至少可以暂时使用它。 但是,正如预期的那样,我无法播种它,让我没有任何可重复的输出(这是RandomNumberGenerator )。

简而言之,两个常见的RNG似乎都不适合我的目的。 我需要能够接收一个数字并根据该值返回一个随机数,而输出中没有明显的模式。 有没有其他方法可以使用上述两种,或者是否有一种我以前没用过的方法更符合我的目的?

为清楚起见,我试图编写的方法如下:

 public int RandomInt(int input) { int randomOutput; //Be random return randomOutput; } 

每次给出相同的input时,它将返回相同的值。

Mersenne Twister 可能会给出更好的结果。

这是一个示例实现,您应该能够相当快速地尝试:

 using System; namespace Random { /* C# Version Copyright (C) 2001 Akihilo Kramot (Takel). */ /* C# porting from a C-program for MT19937, originaly coded by */ /* Takuji Nishimura, considering the suggestions by */ /* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ /* This library is free software under the Artistic license: */ /* */ /* You can find the original C-program at */ /* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html */ /* */ ///  /// Implements a Mersenne Twister Random Number Generator. This class provides the same interface /// as the standard System.Random number generator, plus some additional functions. ///  public class MersenneTwister: System.Random { /* Period parameters */ private const int N = 624; private const int M = 397; private const uint MATRIX_A = 0x9908b0df; /* constant vector a */ private const uint UPPER_MASK = 0x80000000; /* most significant wr bits */ private const uint LOWER_MASK = 0x7fffffff; /* least significant r bits */ /* Tempering parameters */ private const uint TEMPERING_MASK_B = 0x9d2c5680; private const uint TEMPERING_MASK_C = 0xefc60000; private static uint TEMPERING_SHIFT_U( uint y ) { return ( y >> 11 ); } private static uint TEMPERING_SHIFT_S( uint y ) { return ( y << 7 ); } private static uint TEMPERING_SHIFT_T( uint y ) { return ( y << 15 ); } private static uint TEMPERING_SHIFT_L( uint y ) { return ( y >> 18 ); } private uint[] mt = new uint[N]; /* the array for the state vector */ private uint seed_; private short mti; private static uint[] mag01 = { 0x0, MATRIX_A }; ///  /// Create a twister with the specified seed. All sequences started with the same seed will contain /// the same random numbers in the same order. ///  /// The seed with which to start the twister. public MersenneTwister( uint seed ) { Seed = seed; } ///  /// Create a twister seeded from the system clock to make it as random as possible. ///  public MersenneTwister() : this( ( (uint) DateTime.Now.Ticks ) ) // A random initial seed is used. { } ///  /// The seed that was used to start the random number generator. /// Setting the seed resets the random number generator with the new seed. /// All sequences started with the same seed will contain the same random numbers in the same order. ///  public uint Seed { set { seed_ = value; /* setting initial seeds to mt[N] using */ /* the generator Line 25 of Table 1 in */ /* [KNUTH 1981, The Art of Computer Programming */ /* Vol. 2 (2nd Ed.), pp102] */ mt[0] = seed_ & 0xffffffffU; for ( mti = 1; mti < N; mti++ ) { mt[mti] = ( 69069 * mt[mti - 1] ) & 0xffffffffU; } } get { return seed_; } } ///  /// Generate a random uint. ///  /// A random uint. protected uint GenerateUInt() { uint y; /* mag01[x] = x * MATRIX_A for x=0,1 */ if ( mti >= N ) /* generate N words at one time */ { short kk; for ( kk = 0; kk < N - M; kk++ ) { y = ( mt[kk] & UPPER_MASK ) | ( mt[kk + 1] & LOWER_MASK ); mt[kk] = mt[kk + M] ^ ( y >> 1 ) ^ mag01[y & 0x1]; } for ( ; kk < N - 1; kk++ ) { y = ( mt[kk] & UPPER_MASK ) | ( mt[kk + 1] & LOWER_MASK ); mt[kk] = mt[kk + ( M - N )] ^ ( y >> 1 ) ^ mag01[y & 0x1]; } y = ( mt[N - 1] & UPPER_MASK ) | ( mt[0] & LOWER_MASK ); mt[N - 1] = mt[M - 1] ^ ( y >> 1 ) ^ mag01[y & 0x1]; mti = 0; } y = mt[mti++]; y ^= TEMPERING_SHIFT_U( y ); y ^= TEMPERING_SHIFT_S( y ) & TEMPERING_MASK_B; y ^= TEMPERING_SHIFT_T( y ) & TEMPERING_MASK_C; y ^= TEMPERING_SHIFT_L( y ); return y; } ///  /// Returns the next uint in the random sequence. ///  /// The next uint in the random sequence. public virtual uint NextUInt() { return this.GenerateUInt(); } ///  /// Returns a random number between 0 and a specified maximum. ///  /// The upper bound of the random number to be generated. maxValue must be greater than or equal to zero. /// A 32-bit unsigned integer greater than or equal to zero, and less than maxValue; that is, the range of return values includes zero but not MaxValue. public virtual uint NextUInt( uint maxValue ) { return (uint) ( this.GenerateUInt() / ( (double) uint.MaxValue / maxValue ) ); } ///  /// Returns an unsigned random number from a specified range. ///  /// The lower bound of the random number returned. /// The upper bound of the random number returned. maxValue must be greater than or equal to minValue. /// A 32-bit signed integer greater than or equal to minValue and less than maxValue; /// that is, the range of return values includes minValue but not MaxValue. /// If minValue equals maxValue, minValue is returned. public virtual uint NextUInt( uint minValue, uint maxValue ) /* throws ArgumentOutOfRangeException */ { if (minValue >= maxValue) { if (minValue == maxValue) { return minValue; } else { throw new ArgumentOutOfRangeException("minValue", "NextUInt() called with minValue >= maxValue"); } } return (uint) ( this.GenerateUInt() / ( (double) uint.MaxValue / ( maxValue - minValue ) ) + minValue ); } ///  /// Returns a nonnegative random number. ///  /// A 32-bit signed integer greater than or equal to zero and less than int.MaxValue. public override int Next() { return (int) ( this.GenerateUInt() / 2 ); } ///  /// Returns a nonnegative random number less than the specified maximum. ///  /// The upper bound of the random number to be generated. maxValue must be greater than or equal to zero. /// A 32-bit signed integer greater than or equal to zero, and less than maxValue; /// that is, the range of return values includes zero but not MaxValue. public override int Next( int maxValue ) /* throws ArgumentOutOfRangeException */ { if ( maxValue <= 0 ) { if ( maxValue == 0 ) return 0; else throw new ArgumentOutOfRangeException( "maxValue", "Next() called with a negative parameter" ); } return (int) ( this.GenerateUInt() / ( uint.MaxValue / maxValue ) ); } ///  /// Returns a signed random number from a specified range. ///  /// The lower bound of the random number returned. /// The upper bound of the random number returned. maxValue must be greater than or equal to minValue. /// A 32-bit signed integer greater than or equal to minValue and less than maxValue; /// that is, the range of return values includes minValue but not MaxValue. /// If minValue equals maxValue, minValue is returned. public override int Next( int minValue, int maxValue ) /* ArgumentOutOfRangeException */ { if (minValue >= maxValue) { if (minValue == maxValue) { return minValue; } else { throw new ArgumentOutOfRangeException("minValue", "Next() called with minValue > maxValue"); } } return (int) ( this.GenerateUInt() / ( (double) uint.MaxValue / ( maxValue - minValue ) ) + minValue ); } ///  /// Fills an array of bytes with random numbers from 0..255 ///  /// The array to be filled with random numbers. public override void NextBytes( byte[] buffer ) /* throws ArgumentNullException*/ { int bufLen = buffer.Length; if ( buffer == null ) throw new ArgumentNullException("buffer"); for ( int idx = 0; idx < bufLen; idx++ ) buffer[idx] = (byte) ( this.GenerateUInt() / ( uint.MaxValue / byte.MaxValue ) ); } ///  /// Returns a double-precision random number in the range [0..1[ ///  /// A random double-precision floating point number greater than or equal to 0.0, and less than 1.0. public override double NextDouble() { return (double) this.GenerateUInt() / uint.MaxValue; } } } 

我讨厌回答我自己的问题,但是我的一个朋友在StackOverflow上提出了这个建议,我觉得最好把它包含在这里作为后代。

所要求的实际上只是一个散列函数。 如果通过适当的强哈希算法运行输入并将输出转换为int,则将生成与其输入对应的随机输出值。

如果您尝试使输出可重现,那么您只需要使用固定种子将Random种子一次

您可以在程序中将此种子作为另一个输入。 这样你就会知道Next返回的数字序列在程序的两次执行中是相同的(使用相同的种子)。

你绝对应该每次都重新初始化随机生成器。

  Random rnd1 = new Random(12); Random rnd2 = new Random(12); 

调用Next时,这两个生成器将始终输出相同的结果。 在声明代码的位置无关紧要。 或者何时。 唯一重要的是seed (这里是12)是一样的。

如果你想要另一组可重复的值,与rnd1产生的值相同,你需要做的就是实例化rnd2

一种可能的方法似乎是为一次运行存储random.Next()值,并将它们映射到每个输入。 在数据存储上拥有这些值,在下一个应用程序运行时将它们缓存,然后开始提供它们。 实际上,在给定输入的情况下,您将获得相同的随机输出。

如果你想要’相同的种子 – >相同的数字’。 看这个。

这很简单。

 class MyRandom { private static Random Rand = new Random(); private static Dictionary LookupTable = new Dictionary(); public static int RandomInt( int seed ) { try { return LookupTable[ seed ]; } catch ( Exception e ) { int retNum = Rand.Next(); LookupTable.Add( seed, retNum ); return retNum; } } } class Program { static void Main( string[] args ) { Console.WriteLine( MyRandom.RandomInt( 3 ) ); Console.WriteLine( MyRandom.RandomInt( 1 ) ); Console.WriteLine( MyRandom.RandomInt( 3 ) ); } } 

Perlin Noise或更新的Simplex Noise适用于景观生成。

如果我正确理解算法,它的工作原理是将不同频率的噪声梯度(随机点之间的线性插值)加在一起。 我还找到了更详细的解释 。

我在Google Code上找到了一个Simplex Noise库,

并实施:

 // SimplexNoise for C# // Author: Heikki Törmälä //This is free and unencumbered software released into the public domain. //Anyone is free to copy, modify, publish, use, compile, sell, or //distribute this software, either in source code form or as a compiled //binary, for any purpose, commercial or non-commercial, and by any //means. //In jurisdictions that recognize copyright laws, the author or authors //of this software dedicate any and all copyright interest in the //software to the public domain. We make this dedication for the benefit //of the public at large and to the detriment of our heirs and //successors. We intend this dedication to be an overt act of //relinquishment in perpetuity of all present and future rights to this //software under copyright law. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, //EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF //MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. //IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR //OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, //ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR //OTHER DEALINGS IN THE SOFTWARE. //For more information, please refer to  namespace SimplexNoise { ///  /// Implementation of the Perlin simplex noise, an improved Perlin noise algorithm. /// Based loosely on SimplexNoise1234 by Stefan Gustavson  /// ///  public class Noise { ///  /// 1D simplex noise ///  ///  ///  public static float Generate(float x) { int i0 = FastFloor(x); int i1 = i0 + 1; float x0 = x - i0; float x1 = x0 - 1.0f; float n0, n1; float t0 = 1.0f - x0*x0; t0 *= t0; n0 = t0 * t0 * grad(perm[i0 & 0xff], x0); float t1 = 1.0f - x1*x1; t1 *= t1; n1 = t1 * t1 * grad(perm[i1 & 0xff], x1); // The maximum value of this noise is 8*(3/4)^4 = 2.53125 // A factor of 0.395 scales to fit exactly within [-1,1] return 0.395f * (n0 + n1); } ///  /// 2D simplex noise ///  ///  ///  ///  public static float Generate(float x, float y) { const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 float n0, n1, n2; // Noise contributions from the three corners // Skew the input space to determine which simplex cell we're in float s = (x+y)*F2; // Hairy factor for 2D float xs = x + s; float ys = y + s; int i = FastFloor(xs); int j = FastFloor(ys); float t = (float)(i+j)*G2; float X0 = it; // Unskew the cell origin back to (x,y) space float Y0 = jt; float x0 = x-X0; // The x,y distances from the cell origin float y0 = y-Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords float y1 = y0 - j1 + G2; float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords float y2 = y0 - 1.0f + 2.0f * G2; // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = i % 256; int jj = j % 256; // Calculate the contribution from the three corners float t0 = 0.5f - x0*x0-y0*y0; if(t0 < 0.0f) n0 = 0.0f; else { t0 *= t0; n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0); } float t1 = 0.5f - x1*x1-y1*y1; if(t1 < 0.0f) n1 = 0.0f; else { t1 *= t1; n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1]], x1, y1); } float t2 = 0.5f - x2*x2-y2*y2; if(t2 < 0.0f) n2 = 0.0f; else { t2 *= t2; n2 = t2 * t2 * grad(perm[ii+1+perm[jj+1]], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! } public static float Generate(float x, float y, float z) { // Simple skewing factors for the 3D case const float F3 = 0.333333333f; const float G3 = 0.166666667f; float n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D float xs = x+s; float ys = y+s; float zs = z+s; int i = FastFloor(xs); int j = FastFloor(ys); int k = FastFloor(zs); float t = (float)(i+j+k)*G3; float X0 = it; // Unskew the cell origin back to (x,y,z) space float Y0 = jt; float Z0 = kt; float x0 = x-X0; // The x,y,z distances from the cell origin float y0 = y-Y0; float z0 = z-Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords /* This code would benefit from a backport from the GLSL version! */ if(x0>=y0) { if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // XYZ order else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // XZY order else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // ZXY order } else { // x0 0) ? ((int)x) : (((int)x) - 1); } private static float grad( int hash, float x ) { int h = hash & 15; float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 if ((h & 8) != 0) grad = -grad; // Set a random sign for the gradient return ( grad * x ); // Multiply the gradient with the distance } private static float grad( int hash, float x, float y ) { int h = hash & 7; // Convert low 3 bits of hash code float u = h<4 ? x : y; // into 8 simple gradient directions, float v = h<4 ? y : x; // and compute the dot product with (x,y). return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -2.0f*v : 2.0f*v); } private static float grad( int hash, float x, float y , float z ) { int h = hash & 15; // Convert low 4 bits of hash code into 12 simple float u = h<8 ? x : y; // gradient directions, and compute dot product. float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -v : v); } private static float grad( int hash, float x, float y, float z, float t ) { int h = hash & 31; // Convert low 5 bits of hash code into 32 simple float u = h<24 ? x : y; // gradient directions, and compute dot product. float v = h<16 ? y : z; float w = h<8 ? z : t; return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -v : v) + ((h&4) != 0 ? -w : w); } } }