Random.Next()发生回绕前的迭代次数?

时间:2017-03-11 18:58:25

标签: c# random

我很确定这永远不会成为问题。但是,我仍然很好奇,确切地说,任何给定的种子在其范围完全耗尽之前会产生一个随机数,并且它会再次生成相同的数字,从而产生多少次迭代?

举个例子:

假设你有一个由8个整数索引组成的数组;在给定的迭代期间,random.Next将为每个indice填充0-31的值。测试试图了解生成31个完美阵列需要多长时间。

在数学上,每次迭代的概率大约是1,099,511,627,776中的1,以产生所有31个的完美阵列。然而,这假设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 #####

我估计需要时间才能找到一个完美的31个阵列大约47小时和56分钟,以接近找到一个完美的31集的范围。这是我的电脑每分钟填充我的阵列383,500,572。看起来这个测试的时间比我原先预测的要长得多。

2小时更新

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

我有点希望自己能做到这一点...可能会把时间缩短一半......

2 个答案:

答案 0 :(得分:4)

已经有足够的评论了。这是最终答案。

首先:RNG最多只能运行64位值。存在有限多个64位值,因此,根据鸽子原理,通过足够的迭代(n> 2 ^ 64),您肯定会得到至少一个重复值。

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

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

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

答案 1 :(得分:0)

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

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指数...但只有你在第一个十亿左右的时候才能在Random.Next()中调用。我知道这似乎是一个主观的说法,但它是我通过这些测试所经历的。我从来没有得过8-Perfect 31,但我确实得到了7-Perfect 31&#39; 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 ##

我当时没有将其打印出来进行打印,但是打印输出意味着对Random.NextInt()进行了50,253,021,875次单独调用。这意味着该决议一直持续到 50亿次电话

其他7-Perfect仅在程序运行约30秒后才开始。这意味着有好的种子&#34;很快就能获得这种稀有性。我还对7-Perfect指数进行了30分钟的测试,并没有获得单一指数。它基于运气,但与此同时,我感觉好像有一个无形的门槛;如果你不赶快打它,它根本就不会发生。上面的一张海报说,随机类的分辨率是&#34; 281,474,976,710,656&#34;。但我的测试似乎得出结论,分辨率实际上可能远小于此。自己尝试一下,从4-6个索引开始(在几秒钟内发生)并向上移动到7和8.不仅概率增加,还有一个阈值。或许我错了。谁知道?