2

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...

Krythic
  • 4,184
  • 5
  • 26
  • 67
  • 1
    You're making the assumption that it _does_ wrap around. How do you know that it will wrap around? – byxor Mar 11 '17 at 19:05
  • @byxor I've always heard that it does. Am I wrong about this? – Krythic Mar 11 '17 at 19:06
  • Look at what MSDN says about the Random class: https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx. _The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm._ – Psi Mar 11 '17 at 19:07
  • I have seen no indication that it wraps around, however, the reference documentation refers to a book that describes the algorithm in more detail. – Frank Hileman Mar 11 '17 at 19:08
  • 2
    It _will_ eventually wrap around, because it is a PRNG – Psi Mar 11 '17 at 19:08
  • @Psi This I am aware of, but my question is "How many iterations will it take before a wrap around occurs; can it generate an array of perfect 31's given a sufficient amount of time and iterations"? – Krythic Mar 11 '17 at 19:10
  • 1
    Possible duplicate of [How long does the stream of Random().Next() take until it repeats?](http://stackoverflow.com/questions/2050392/how-long-does-the-stream-of-random-next-take-until-it-repeats) – Mark Dickinson Mar 11 '17 at 19:11
  • @FrankHileman: The PRNG state has a fixed, finite size. That alone is enough to guarantee that there's a period, regardless of the actual algorithm. – Mark Dickinson Mar 11 '17 at 19:13
  • 1
    The only question is what you mean with "wrap around". If you mean, the internal state is the same after _n_ iterations, look at the link Mark Dickinson has posted. If you want to know, how long it is able to produce _unique_ numbers... well, if it's half way a good PRNG, it should be able to produce duplicates sometimes even in two subsequent calls. – Psi Mar 11 '17 at 19:15
  • @MarkDickinson The suggested "pre-existing answer" fails to answer the question that I have presented, please remove your comment and the suggestion to override this question. – Krythic Mar 11 '17 at 19:18
  • 2
    Apparently a bug in the implementation can reduce the periodicity, relative to the referenced algorithm: https://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug – Frank Hileman Mar 11 '17 at 19:18
  • I somehow doubt you'll get your 31's. The RNG essentially creates a finite sequence of numbers. You obviously won't get all possible finite subsequences with a finite sequence, simply because there are infinitely many finite subsequences of integers. Whether a string of eight 31's occurs or not depends on the implementation, but I think your test proves that it doesn't occur, which in turn proves nothing at all. – Arshia001 Mar 11 '17 at 19:21
  • @Arshia001 Actually, my test would prove quite a lot. It shows a possible limitation, which is invaluable information for the modern programmer. My test is currently at 1 hour and 18 minutes, and has iterated 35,012,875,899 times with zero successful 'perfect' arrays. It should also be noted that attempting to only locate 6 perfect 31's take approximately 20 minutes. Tested by me and verified. – Krythic Mar 11 '17 at 19:27
  • Where do you get "3 hours" from? From your previous results, after 49 minutes you've only had the opportunity to generate < 1/50th of all possible octuples of values (and many of those will be duplicates, thanks to the Birthday Paradox). So it looks as though you should be running this for several days before you can say anything (statistically) significant. And FTR, the chances are good that you *will* eventually produce this value, if you wait long enough. – Mark Dickinson Mar 11 '17 at 19:46
  • @MarkDickinson Actually, my math was way off. It will take 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 generating 383,500,572 arrays every minute. – Krythic Mar 11 '17 at 20:01

2 Answers2

4

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.

Arshia001
  • 1,854
  • 14
  • 19
  • I appreciate your answer, but my question is a very stubborn "How many iterations before a wrap around occurs; can it achieve the test that I have created?" – Krythic Mar 11 '17 at 19:54
  • The answer claims that it is at best 2^64. Links suggest it is at best 2^48. – TaW Mar 11 '17 at 20:31
  • @TaW I didn't claim it was, I just said it has to warp around. Other than the fact that it __does__ at some point in time, and that it's too far ahead into the future to matter, I don't claim anything. From a computer _engineering_ point of view, it doesn't matter. This kind of thing is exactly why I don't get along with computer scientists. Too much time spent on too many unimportant details. – Arshia001 Mar 12 '17 at 06:58
  • 1
    On re-reading you post the claim _'with enough iterations (n > 2^64) you will definitely get at least one repetitive value'._ may acutally have been about the values, which are not important; the issue of wrapping arond is all about the internal state. (Which also is stored in a finite number) - Also: The number only seem to be so hight to be unimportant if you think about some applications, like dealing cards. Once you enter other fields, like statistical simulations they will quickly gain quite a lot of importance. Tsk, tsk, engineers.. ;-) – TaW Mar 12 '17 at 08:52
  • 1
    @TaW I'll leave you to your statistical simulations and just go deal my cards. It's good enough (as in "it will work 99.9% of the time and most people won't even notice the difference"). – Arshia001 Mar 14 '17 at 06:14
0

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?

Krythic
  • 4,184
  • 5
  • 26
  • 67