【问题标题】:Will System.Random always generate predictable numbers for a given seed across platforms?System.Random 是否总是为跨平台的给定种子生成可预测的数字?
【发布时间】:2017-08-06 23:42:32
【问题描述】:

我知道 Object.GetHashCode 可以根据平台为同一对象返回不同的值(例如,相同的字符串)。因此,对于跨平台的简单哈希,我不能依赖它。

但是我可以依赖 System.Random 吗?无论我是否使用 Microsoft .NET / Mono / x86 / x64 等,它是否会对给定的种子产生相同的结果?

【问题讨论】:

  • 我建议依靠一个名为“Random”的类来提供可预测的数字是错误的:)
  • 我不认为有任何保证他们不会在某个时候改变算法......
  • 你有没有新的随机循环?
  • @DavidG 在给定特定种子的情况下,依靠随机数生成器产生可预测的结果、确定性地重现问题等是很常见的。
  • @DavidG 在这种情况下,鉴于它们涉及许多 RNG 计算,您如何建议确定性地重现游戏场景等?

标签: c# .net random


【解决方案1】:

正如 Jeremy 提到的 in his answer 文档指出,不能保证数字生成器在 .NET 版本之间保持一致。

但是,文档也告诉你how to implement your algorithm too

您可以通过从 Random 类继承并提供您的随机数生成算法来实现自己的随机数生成器。要提供您自己的算法,您必须重写实现随机数生成算法的Sample 方法。您还应该重写 Next()Next(Int32, Int32)NextBytes 方法,以确保它们调用您重写的 Sample 方法。您不必重写 Next(Int32)NextDouble 方法。

使用它,我们可以制作自己的随机类,该类使用您可以使用的已知固定算法。例如,我们可以去to the .Net source 并实现当前的随机算法,这将允许我们使用ConsistantRandom 类并且确定算法不会改变我们。

using System;

namespace ConsoleApplication1
{
    public class ConsistantRandom: Random
    {
        private const int MBIG = Int32.MaxValue;
        private const int MSEED = 161803398;
        private const int MZ = 0;

        private int inext;
        private int inextp;
        private int[] SeedArray = new int[56];

        public ConsistantRandom()
            : this(Environment.TickCount)
        {
        }

        public ConsistantRandom(int seed)
        {
            int ii;
            int mj, mk;

            int subtraction = (seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(seed);
            mj = MSEED - subtraction;
            SeedArray[55] = mj;
            mk = 1;
            for (int i = 1; i < 55; i++)
            {
                ii = (21 * i) % 55;
                SeedArray[ii] = mk;
                mk = mj - mk;
                if (mk < 0) mk += MBIG;
                mj = SeedArray[ii];
            }
            for (int k = 1; k < 5; k++)
            {
                for (int i = 1; i < 56; i++)
                {
                    SeedArray[i] -= SeedArray[1 + (i + 30) % 55];
                    if (SeedArray[i] < 0) SeedArray[i] += MBIG;
                }
            }
            inext = 0;
            inextp = 21;
        }
        protected override double Sample()
        {
            return (InternalSample() * (1.0 / MBIG));
        }

        private int InternalSample()
        {
            int retVal;
            int locINext = inext;
            int locINextp = inextp;

            if (++locINext >= 56) locINext = 1;
            if (++locINextp >= 56) locINextp = 1;

            retVal = SeedArray[locINext] - SeedArray[locINextp];

            if (retVal == MBIG) retVal--;
            if (retVal < 0) retVal += MBIG;

            SeedArray[locINext] = retVal;

            inext = locINext;
            inextp = locINextp;

            return retVal;
        }

        public override int Next()
        {
            return InternalSample();
        }

        private double GetSampleForLargeRange()
        {
            int result = InternalSample();
            bool negative = (InternalSample() % 2 == 0) ? true : false;
            if (negative)
            {
                result = -result;
            }
            double d = result;
            d += (Int32.MaxValue - 1);
            d /= 2 * (uint)Int32.MaxValue - 1;
            return d;
        }


        public override int Next(int minValue, int maxValue)
        {
            if (minValue > maxValue)
            {
                throw new ArgumentOutOfRangeException("minValue");
            }

            long range = (long)maxValue - minValue;
            if (range <= (long)Int32.MaxValue)
            {
                return ((int)(Sample() * range) + minValue);
            }
            else
            {
                return (int)((long)(GetSampleForLargeRange() * range) + minValue);
            }
        }
        public override void NextBytes(byte[] buffer)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");
            for (int i = 0; i < buffer.Length; i++)
            {
                buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
            }
        }
    }

}

因为ConsistantRandom 派生自Random,您可以使用该类作为替代替换任何您之前有Random 类型的地方,您只需将Random rnd = new Random(yourSeed); 替换为Random rnd = new ConsistantRandom(yourSeed);

【讨论】:

    【解决方案2】:

    不,您不能依赖跨平台或版本生成的一致值。来自the .NET Framework docs for System.Random(不太详细的.NET Core docs不要解决这个问题):

    Random 类中的随机数生成器的实现不能保证在 .NET Framework 的主要版本中保持不变。因此,您不应假设相同的种子会在不同版本的 .NET Framework 中产生相同的伪随机序列。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-09
    • 2011-01-12
    • 2020-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-27
    相关资源
    最近更新 更多