c#中的快速随机生成器

时间:2014-11-27 16:51:50

标签: c#

我需要游戏中程序性地形等的随机数字。游戏基于种子。 我被告知不要依赖.NET框架Random类,因为实现可能会更改,种子可能会导致其他版本中的其他值。 (这已经发生了...... .NET 1.1有不同的实现)

最随机的替代方案是密码学,其中焦点依赖于非常好的数字而非性能。

所以我所寻找的是一个非常快速的基于种子的伪随机数生成器,它独立于.NET版本,具有平均质量随机性。

2 个答案:

答案 0 :(得分:3)

不要重新发明轮子。借用/窃取有用的东西:

只需使用现有的反编译实现之一并将其折叠到您自己的库中,以确保它不会更改:

using System;
using System.Runtime;
using System.Runtime.InteropServices;
namespace System
{
    /// <summary>Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet certain statistical requirements for randomness.</summary>
    /// <filterpriority>1</filterpriority>
    [ComVisible(true)]
    [Serializable]
    public class Random
    {
        private const int MBIG = 2147483647;
        private const int MSEED = 161803398;
        private const int MZ = 0;
        private int inext;
        private int inextp;
        private int[] SeedArray = new int[56];
        /// <summary>Initializes a new instance of the <see cref="T:System.Random" /> class, using a time-dependent default seed value.</summary>
        public Random() : this(Environment.TickCount)
        {
        }
        /// <summary>Initializes a new instance of the <see cref="T:System.Random" /> class, using the specified seed value.</summary>
        /// <param name="Seed">A number used to calculate a starting value for the pseudo-random number sequence. If a negative number is specified, the absolute value of the number is used. </param>
        public Random(int Seed)
        {
            int num = (Seed == -2147483648) ? 2147483647 : Math.Abs(Seed);
            int num2 = 161803398 - num;
            this.SeedArray[55] = num2;
            int num3 = 1;
            for (int i = 1; i < 55; i++)
            {
                int num4 = 21 * i % 55;
                this.SeedArray[num4] = num3;
                num3 = num2 - num3;
                if (num3 < 0)
                {
                    num3 += 2147483647;
                }
                num2 = this.SeedArray[num4];
            }
            for (int j = 1; j < 5; j++)
            {
                for (int k = 1; k < 56; k++)
                {
                    this.SeedArray[k] -= this.SeedArray[1 + (k + 30) % 55];
                    if (this.SeedArray[k] < 0)
                    {
                        this.SeedArray[k] += 2147483647;
                    }
                }
            }
            this.inext = 0;
            this.inextp = 21;
            Seed = 1;
        }
        /// <summary>Returns a random number between 0.0 and 1.0.</summary>
        /// <returns>A double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        protected virtual double Sample()
        {
            return (double)this.InternalSample() * 4.6566128752457969E-10;
        }
        private int InternalSample()
        {
            int num = this.inext;
            int num2 = this.inextp;
            if (++num >= 56)
            {
                num = 1;
            }
            if (++num2 >= 56)
            {
                num2 = 1;
            }
            int num3 = this.SeedArray[num] - this.SeedArray[num2];
            if (num3 == 2147483647)
            {
                num3--;
            }
            if (num3 < 0)
            {
                num3 += 2147483647;
            }
            this.SeedArray[num] = num3;
            this.inext = num;
            this.inextp = num2;
            return num3;
        }
        /// <summary>Returns a nonnegative random number.</summary>
        /// <returns>A 32-bit signed integer greater than or equal to zero and less than <see cref="F:System.Int32.MaxValue" />.</>/returns>
        /// <filterpriority>1</filterpriority>
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public virtual int Next()
        {
            return this.InternalSample();
        }
        private double GetSampleForLargeRange()
        {
            int num = this.InternalSample();
            bool flag = this.InternalSample() % 2 == 0;
            if (flag)
            {
                num = -num;
            }
            double num2 = (double)num;
            num2 += 2147483646.0;
            return num2 / 4294967293.0;
        }
        /// <summary>Returns a random number within a specified range.</summary>
        /// <returns>A 32-bit signed integer greater than or equal to <paramref name="minValue" /> and less than <paramref name="maxValue" />; that is, the range of return values includes <paramref name="minValue" /> but not <paramref name="maxValue" />. If <paramref name="minValue" /> equals <paramref name="maxValue" />, <paramref name="minValue" /> is returned.</returns>
        /// <param name="minValue">The inclusive lower bound of the random number returned. </param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. <paramref name="maxValue" /> must be greater than or equal to <paramref name="minValue" />. </param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///   <paramref name="minValue" /> is greater than <paramref name="maxValue" />. </exception>
        /// <filterpriority>1</filterpriority>
        public virtual int Next(int minValue, int maxValue)
        {
            if (minValue > maxValue)
            {
                throw new ArgumentOutOfRangeException("minValue", Environment.GetResourceString("Argument_MinMaxValue", new object[]
                {
                    "minValue", 
                    "maxValue"
                }));
            }
            long num = (long)maxValue - (long)minValue;
            if (num <= 2147483647L)
            {
                return (int)(this.Sample() * (double)num) + minValue;
            }
            return (int)((long)(this.GetSampleForLargeRange() * (double)num) + (long)minValue);
        }
        /// <summary>Returns a nonnegative random number less than the specified maximum.</summary>
        /// <returns>A 32-bit signed integer greater than or equal to zero, and less than <paramref name="maxValue" />; that is, the range of return values ordinarily includes zero but not <paramref name="maxValue" />. However, if <paramref name="maxValue" /> equals zero, <paramref name="maxValue" /> is returned.</returns>
        /// <param name="maxValue">The exclusive upper bound of the random number to be generated. <paramref name="maxValue" /> must be greater than or equal to zero. </param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        ///   <paramref name="maxValue" /> is less than zero. </exception>
        /// <filterpriority>1</filterpriority>
        public virtual int Next(int maxValue)
        {
            if (maxValue < 0)
            {
                throw new ArgumentOutOfRangeException("maxValue", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", new object[]
                {
                    "maxValue"
                }));
            }
            return (int)(this.Sample() * (double)maxValue);
        }
        /// <summary>Returns a random number between 0.0 and 1.0.</summary>
        /// <returns>A double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
        /// <filterpriority>1</filterpriority>
        [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
        public virtual double NextDouble()
        {
            return this.Sample();
        }
        /// <summary>Fills the elements of a specified array of bytes with random numbers.</summary>
        /// <param name="buffer">An array of bytes to contain random numbers. </param>
        /// <exception cref="T:System.ArgumentNullException">
        ///   <paramref name="buffer" /> is null. </exception>
        /// <filterpriority>1</filterpriority>
        public virtual void NextBytes(byte[] buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            for (int i = 0; i < buffer.Length; i++)
            {
                buffer[i] = (byte)(this.InternalSample() % 256);
            }
        }
    }
}

答案 1 :(得分:2)

标准Random(或你自己的副本)对于地形和许多东西可能没问题,但你需要小心一些你使用产生足够随机性的生成器的游戏。

拿一副卡,如果你使用32位发生器,它只能产生40亿个第一个包(见下文)的排列(2 ^ 32)。但实际上有52个! (因子是)可能的,即8x10 ^ 67。这需要一个226位的数字。

因此,有时可能需要使用加密生成器。

为什么这只适用于第一包?

我假设下一个数字的种子只是最后返回的32位值。我现在看了一下代码,内部种子的位数要高得多。您提供的32位外部种子值仅用作生成较大种子的起始点的方法。它维护并修改此内部种子,同时生成随机值。这一切意味着是的,只有40亿个序列,但这些序列是唯一的,它们与我假设的方式不重叠。这意味着最初只能有40亿个包,但可以追随更多包。