问题
I'm pretty sure that this will never be an issue. However, I'm still curious, exactly how many iterations can any given seed generate a random number before its scope is fully exhausted and it wraps back around to generating the same numbers again?
As an example:
Suppose you have an array consisting of eight integer indices; during a given iteration the random.Next would fill each indice with a value of 0-31. And the test is attempting to see how long it would take to generate a perfect array of all 31's.
Mathematically, the odds are roughly 1 in 1,099,511,627,776 per iteration to yield a perfect array of all 31's. However, this is assuming that the C# Random number generator could even make it to the projected range of 1 trillion iterations without wrapping back around on itself.
So, to sum up my actual question, could the Random class achieve the test that I have presented? Or would it reach a half-way mark and just doom itself to failure regardless of how many iterations it goes through? What exactly is the number of iterations before the end of the random number generator will be reached? I wanted to also mention that it only takes about 20 minutes to successfully generate an array of 6 perfect 31's. This was tested by me and verified.
I should also mention that I am currently running a testing mechanism that is trying to achieve this. So far, this is the current report, which is displayed every minute:
##### Report #####
Elapsed Simulation Time: 00:49:00.1759559
Total Iterations: 20784834152
Perfect Eight Success: 0
Failure: 20784834152
##### End Report #####
I have estimated the required time to find 1 perfect array of 31's to be roughly 47 Hours and 56 Minutes to get close to the range of finding even 1 perfect set of 31's. That's with my computer filling my array 383,500,572 every minute. Looks like this test will take far longer than I originally projected.
2 Hour Update
##### Report #####
Elapsed Simulation Time: 02:00:00.4483950
Total Iterations: 55655726300
Success: 0
Failure: 55655726300
##### End Report #####
I kind of wish I would have threaded this...probably could have cut the time in half...
回答1:
Enough comments already. Here's the definitive answer.
First: The RNG can, at best, operate on 64-bit values. There are finitely many 64-bit values, so, according to the pigeonhole principle, with enough iterations (n > 2^64) you will definitely get at least one repetitive value.
The underlying algorithm uses some finite, arbitrary number of parameters to decide the next random value. If we assume there are N state variables, each with 64 bits, there can be at most (2^64)^N different internal states. Like before, with enough iterations, your RNG will have the same internal state. This will cause a loop, and it will certainly come to pass at some point in time. As for how many iterations it takes to loop back, suffice it to say there will be more than you'll ever need for day-to-day random number generation. I haven't run into any such loop yet (it's been generating for 20 minutes straight on my i7 CPU, and if your code generates that many numbers, you're probably doing something very wrong).
Second: I don't know about eight 31's in a row, but that's just a special case. What you're asking, basically, is this: Given some arbitrary sequence S_QUERY of numbers, will the RNG generate S_QUERY?
To answer, we must first note that the RNG generates a finite sequence S_RNG of numbers. So the real question is this: is S_QUERY a subsequence of S_RNG? Since S_RNG is finite, it can only have finitely many subsequences. However, there are infinitely many possible S_QUERY's to choose from, so for every RNG you can find some S_QUERY's which cannot be generated by that RNG. As for the special case of eight 31's, I don't know and I can't know. Keep that code running and find out.
回答2:
I just wanted to post my testing code and explain a few things. First, here is my code:
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++;
}
}
}
}
After about three hours of testing I have come to a few realizations. I believe it may be possible to get eight perfect indices of 31...but only if you get lucky within the first billion or so calls to Random.Next(). I know this may seem like a subjective thing to say, but it's what I have experienced through these tests. I never once got 8-Perfect 31's, but I did get 7-Perfect 31's. The first time it was after 13 minutes. Here is the print out:
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 ##
I didnt have it coded in at the time to print it out, but that print out would mean there were 50,253,021,875 individual calls to Random.NextInt(); This means that the resolution held up all the way to 50 Billion calls.
And the other 7-Perfect was only after about 30 seconds of the program running. That means there are "Good Seeds" for getting this kind of rarity fairly quickly. I also ran the test for 7-Perfect indices for thirty minutes and didn't get a single one. It's based on luck, but at the same time I heavily feel as though there is an invisible threshold; if you don't hit it soon it won't happen at all. A poster above said that the resolution of the Random class is "281,474,976,710,656". But my tests seem to conclude that the resolution may actually be far smaller than that. Try it yourself, start from 4-6 indices(Happens within a matter of seconds) and move up to 7 and 8. It's not just that the probability increases, it's that there is a threshold...or maybe I am just wrong. Who knows?
来源:https://stackoverflow.com/questions/42739296/random-next-how-many-iterations-before-a-wrap-around-occurs