【问题标题】:Random.Next() How many iterations before a wrap around occurs?Random.Next() 在发生回绕之前有多少次迭代?
【发布时间】:2017-03-11 18:58:25
【问题描述】:

我很确定这永远不会成为问题。但是,我仍然很好奇,任何给定的种子到底可以迭代多少次才能在其范围完全耗尽之前生成一个随机数,然后它会重新返回以再次生成相同的数字?

举个例子:

假设您有一个由八个整数索引组成的数组;在给定的迭代期间,random.Next 将使用 0-31 的值填充每个索引。并且该测试试图查看生成所有 31 的完美数组需要多长时间。

从数学上讲,每次迭代产生所有 31 个完美数组的几率大约为 1,099,511,627,776 分之一。但是,这是假设 C# 随机数生成器甚至可以使其达到 1 万亿次迭代的预计范围,而不会自行回绕。

那么,总结一下我的实际问题,Random 类可以实现我提出的测试吗?或者它会达到一个中途标记并且不管它经历多少次迭代都注定要失败?在达到随机数生成器结束之前的迭代次数到底是多少?我还想提一下,成功生成 6 个完美 31 的数组只需要大约 20 分钟。这是经过我测试和验证的。

我还应该提到,我目前正在运行一个试图实现这一目标的测试机制。到目前为止,这是当前报告,每分钟显示一次:

##### Report #####
Elapsed Simulation Time: 00:49:00.1759559
Total Iterations: 20784834152
Perfect Eight Success: 0
Failure: 20784834152
##### End Report #####

我估计找到 1 个完美的 31 数组所需的时间大约是 47 小时 56 分钟,以接近找到甚至 1 个完美的 31 数组的范围。那是我的计算机每分钟填充我的数组 383,500,572。看起来这个测试需要的时间比我最初预计的要长。

2 小时更新

##### Report #####
Elapsed Simulation Time: 02:00:00.4483950
Total Iterations: 55655726300
Success: 0
Failure: 55655726300
##### End Report #####

我有点希望我能把这个串起来……可能会把时间缩短一半……

【问题讨论】:

  • 您正在假设它确实环绕。你怎么知道它会环绕?
  • 最终环绕,因为它是一个 PRNG
  • 唯一的问题是“环绕”是什么意思。如果您的意思是,内部状态在 n 次迭代之后是相同的,请查看 Mark Dickinson 发布的链接。如果您想知道,它能够产生唯一数字多长时间......好吧,如果它是一个好的PRNG,它应该能够在随后的两个调用中有时产生重复。
  • 显然实现中的一个错误可以减少周期性,相对于引用的算法:connect.microsoft.com/VisualStudio/feedback/details/634761/…

标签: c# random


【解决方案1】:

已经有足够的 cmets。这是确定的答案。

首先:RNG 最多只能对 64 位值进行操作。 64 位值的数量是有限的,所以根据鸽巢原理,通过足够的迭代次数(n > 2^64),你肯定会得到至少一个重复值。

底层算法使用一些有限的、任意数量的参数来决定下一个随机值。如果我们假设有 N 个状态变量,每个都有 64 位,那么最多可以有 (2^64)^N 个不同的内部状态。像以前一样,通过足够的迭代,您的 RNG 将具有相同的内部状态。这将导致一个循环,并且它肯定会在某个时间点通过。至于循环返回需要多少次迭代,只要说日常随机数生成所需的次数就足够了。我还没有遇到任何这样的循环(它已经在我的 i7 CPU 上连续生成了 20 分钟,如果你的代码生成了这么多数字,那么你可能做错了什么)。

第二次:我不知道连续 8 个 31,但这只是一个特例。基本上,您要问的是:给定一些任意序列 S_QUERY 的数字,RNG 会生成 S_QUERY 吗?

要回答这个问题,我们必须首先注意 RNG 生成一个有限的数字序列 S_RNG。所以真正的问题是:S_QUERY 是 S_RNG 的子序列吗?由于 S_RNG 是有限的,它只能有有限多个子序列。但是,有无数种可能的 S_QUERY 可供选择,因此对于每个 RNG,您都可以找到一些该 RNG 无法生成的 S_QUERY。至于8个31的特例,我不知道,也无法知道。保持该代码运行并找出答案。

【讨论】:

  • 感谢您的回答,但我的问题是一个非常顽固的问题“在发生回绕之前进行了多少次迭代;它可以实现我创建的测试吗?”
  • 答案声称最多为 2^64。链接表明它最多为 2^48。
  • @TaW 我没有声称它是,我只是说它必须扭曲。除了它在某个时间点确实,而且它对未来太遥远而无所谓,我不主张任何东西。从计算机工程的角度来看,这并不重要。这种事情正是我与计算机科学家相处不来的原因。太多时间花在太多不重要的细节上。
  • 在重新阅读您发布的声明 '如果有足够的迭代次数 (n > 2^64),您肯定会得到至少一个重复值'。 可能实际上是关于不重要的价值观;环绕的问题完全与内部状态有关。 (它也存储在一个有限的数字中) - 另外:如果你考虑一些应用程序,比如发牌,这个数字似乎是如此之高以至于不重要。一旦你进入其他领域,比如统计模拟,它们很快就会变得非常重要。啧啧,工程师……;-)
  • @TaW 我会留给您进行统计模拟,然后去处理我的牌。这已经足够好了(就像“它会在 99.9% 的时间里工作,而且大多数人甚至不会注意到其中的区别”)。
【解决方案2】:

我只是想发布我的测试代码并解释一些事情。首先,这是我的代码:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public static class Program
    {

        public static long Success;
        public static long Failure;
        public static long TotalIterations;
        public static long TotalCallsToRandom;
        public static readonly int CurrentSeed = Environment.TickCount;
        public static Random Random = new Random(CurrentSeed);
        public static Stopwatch TotalSimulationTime = new Stopwatch();
        public static Stopwatch ReportWatchTime = new Stopwatch();
        public static bool IsRunning = true;

        //
        public const int TotalTestingIndices = 7;
        public const int MaximumTestingValue = 31;
        public const int TimeBetweenReports = 30000; // Report every 30 Seconds.
        //

        public static void Main(string[] args)
        {
            int[] array = new int[TotalTestingIndices];
            TotalSimulationTime.Start();
            ReportWatchTime.Start();
            while (IsRunning)
            {
                if (ReportWatchTime.ElapsedMilliseconds >= TimeBetweenReports)
                {
                    Report();
                    ReportWatchTime.Restart();
                }
                Fill(array);
                if (IsPerfect(array))
                {
                    Success++;
                    Console.WriteLine("A Perfect Array was found!");
                    PrintArray(array);
                    Report();
                    IsRunning = false;
                }
                else
                {
                    Failure++;
                }
                TotalIterations++;
            }
            Console.Read();
        }

        public static void Report()
        {
            Console.WriteLine();
            Console.WriteLine("## Report ##");
            Console.WriteLine("Current Seed: " + CurrentSeed);
            Console.WriteLine("Desired Perfect Number: " + MaximumTestingValue);
            Console.WriteLine("Total Testing Indices: " + TotalTestingIndices);
            Console.WriteLine("Total Simulation Time: " + TotalSimulationTime.Elapsed);
            Console.WriteLine("Total Iterations: " + TotalIterations);
            Console.WriteLine("Total Random.NextInt() Calls: " + TotalCallsToRandom);
            Console.WriteLine("Success: " + Success);
            Console.WriteLine("Failure: " + Failure);
            Console.WriteLine("## End of Report ##");
            Console.WriteLine();
        }

        public static void PrintArray(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                Console.Write(array[i]);
                if (i != array.Length - 1)
                {
                    Console.Write(",");
                }
            }
        }

        /// <summary>
        /// Optimized to terminate quickly.
        /// </summary>
        /// <param name="array"></param>
        /// <returns></returns>
        public static bool IsPerfect(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] != MaximumTestingValue)
                {
                    return false;
                }
            }
            return true;
        }

        public static void Fill(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = Random.Next(MaximumTestingValue + 1);
                TotalCallsToRandom++;
            }
        }
    }
}

经过大约三个小时的测试,我有了一些认识。我相信有可能获得 8 个完美的 31 指数……但前提是你在第 10 亿个左右的 Random.Next() 调用中很幸运。我知道这似乎是一个主观的说法,但这是我通过这些测试所经历的。我从来没有得到 8-Perfect 31's,但我确实得到了 7-Perfect 31's。第一次是在 13 分钟后。这是打印出来的:

A Perfect Array was found!
31,31,31,31,31,31,31
## Report ##
Total Simulation Time: 00:13:32.4293323
Total Iterations: 7179003125
Success: 1
Failure: 7179003125
## End of Report ##

我当时没有将它编码打印出来,但打印出来意味着有 50,253,021,875 次单独调用 Random.NextInt();这意味着该决议一直坚持到 500 亿次调用

而另一个 7-Perfect 仅在程序运行大约 30 秒后才运行。这意味着有“好种子”可以相当快地获得这种稀有性。我还对 7-Perfect 指数进行了 30 分钟的测试,但没有得到一个。是靠运气,但同时我也深深地感觉到好像有一道无形的门槛;如果你不尽快击中它,它根本不会发生。上面的一个海报说Random类的分辨率是“281,474,976,710,656”。但我的测试似乎得出结论,分辨率实际上可能远小于此。自己尝试一下,从 4-6 个指数开始(几秒钟内发生),然后向上移动到 7 和 8。这不仅仅是概率增加,而是有一个阈值......或者也许我错了。谁知道?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 2013-02-05
    • 1970-01-01
    • 2011-10-22
    • 2013-04-15
    相关资源
    最近更新 更多