How can I get true randomness in this class without Thread.Sleep (300)?

I created a class (code below) that handles the creation of the “corresponding” quiz element in the test, this is the output:

alt text

It works great.

However, in order to get completely random , I have to put the thread to sleep for at least 300 samples between the random shuffling of two columns, anything below 300 returns both columns sorted in the same order as if it used the same seed for randomness:

LeftDisplayIndexes.Shuffle(); Thread.Sleep(300); RightDisplayIndexes.Shuffle(); 

What do I need to do so that the shuffling of two columns is completely random without this time?

full code:

 using System.Collections.Generic; using System; using System.Threading; namespace TestSort727272 { class Program { static void Main(string[] args) { MatchingItems matchingItems = new MatchingItems(); matchingItems.Add("one", "111"); matchingItems.Add("two", "222"); matchingItems.Add("three", "333"); matchingItems.Add("four", "444"); matchingItems.Setup(); matchingItems.DisplayTest(); matchingItems.DisplayAnswers(); Console.ReadLine(); } } public class MatchingItems { public List<MatchingItem> Collection { get; set; } public List<int> LeftDisplayIndexes { get; set; } public List<int> RightDisplayIndexes { get; set; } private char[] _numbers = { '1', '2', '3', '4', '5', '6', '7', '8' }; private char[] _letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; public MatchingItems() { Collection = new List<MatchingItem>(); LeftDisplayIndexes = new List<int>(); RightDisplayIndexes = new List<int>(); } public void Add(string leftText, string rightText) { MatchingItem matchingItem = new MatchingItem(leftText, rightText); Collection.Add(matchingItem); LeftDisplayIndexes.Add(Collection.Count - 1); RightDisplayIndexes.Add(Collection.Count - 1); } public void DisplayTest() { Console.WriteLine(""); Console.WriteLine("--TEST:-------------------------"); for (int i = 0; i < Collection.Count; i++) { int leftIndex = LeftDisplayIndexes[i]; int rightIndex = RightDisplayIndexes[i]; Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText); } } public void DisplayAnswers() { Console.WriteLine(""); Console.WriteLine("--ANSWERS:-------------------------"); for (int i = 0; i < Collection.Count; i++) { string leftLabel = _numbers[i].ToString(); int leftIndex = LeftDisplayIndexes[i]; int rightIndex = RightDisplayIndexes.IndexOf(leftIndex); string answerLabel = _letters[rightIndex].ToString(); Console.WriteLine("{0}. {1}", leftLabel, answerLabel); } } public void Setup() { do { LeftDisplayIndexes.Shuffle(); Thread.Sleep(300); RightDisplayIndexes.Shuffle(); } while (SomeLinesAreMatched()); } private bool SomeLinesAreMatched() { for (int i = 0; i < LeftDisplayIndexes.Count; i++) { int leftIndex = LeftDisplayIndexes[i]; int rightIndex = RightDisplayIndexes[i]; if (leftIndex == rightIndex) return true; } return false; } public void DisplayAsAnswer(int numberedIndex) { Console.WriteLine(""); Console.WriteLine("--ANSWER TO {0}:-------------------------", _numbers[numberedIndex]); for (int i = 0; i < Collection.Count; i++) { int leftIndex = LeftDisplayIndexes[i]; int rightIndex = RightDisplayIndexes[i]; Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText); } } } public class MatchingItem { public string LeftText { get; set; } public string RightText { get; set; } public MatchingItem(string leftText, string rightText) { LeftText = leftText; RightText = rightText; } } public static class Helpers { public static void Shuffle<T>(this IList<T> list) { Random rng = new Random(); int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } } } 
+6
c # random
source share
6 answers

Make only one instance of the Random class. When you call it without a constructor, it grabs a random seed from a computer clock, so you can get the same thing twice.

 public static class Helpers { static Random rng = new Random(); public static void Shuffle<T>(this IList<T> list) { int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } } 
+6
source share

Move Random rng = new Random(); into a static variable.

MSDN says: "The initial default value is obtained from the system clock and has the final resolution." When you create many Random objects over a short period of time, they all get the same seed, and the first value will be equal to all random objects.

By reusing the same Random object, you go to the next random value from the given seed.

+10
source share

I need to put the thread to sleep at least 300 samples between random shuffling two columns, anything below 300 returns both columns sorted in the same order as if it were using the same seed for randomness

You answered your question here. It is “as if he were using the same seed” because he is using the same seed! Due to the relatively rough detailing of the Windows system clock, several instances of Random built almost at the same time will have the same initial value.

As Albin suggests , you should only have one Random object and use it. Thus, instead of a bunch of pseudo-random sequences that all start from the same semester and therefore the same, your Shuffle method will be based on one pseudo-random sequence.

Given that you have an extension method, you might want to reuse it. In this case, consider the presence of an overload that takes Random and the other:

 static void Shuffle<T>(this IList<T> list, Random random) { // Your code goes here. } static void Shuffle<T>(this IList<T> list) { list.Shuffle(new Random()); } 

This allows the caller to provide a static Random object if he / she calls Shuffle many times in a row; on the other hand, if it's just a one-time thing, Shuffle can take care of the implementation of Random .

The last thing I want to note is that since the solution involves using one common Random object, you should know that the Random class is not thread safe. If it is possible that you could call a Shuffle from multiple threads at the same time, you need to block your Next call (or: what I prefer to do is have a [ThreadStatic] Random object for each thread, each of which is placed in a random value, provided by the "core" Random , but a bit more active).

Otherwise, you can end up with Next just reconfiguring the infinite sequence of zeros.

+4
source share

The problem is that you are creating Random objects too close together in time. When you do this, their internal pseudo-random generators are seeded by the same system time, and the sequence of numbers they produce will be the same.

The simplest solution is to reuse a single Random object, passing it as an argument to your shuffle algorithm, or saving it as a member variable of the class in which the shuffle is implemented.

+2
source share

How random generators work, roughly speaking, is that they have a seed from which random values ​​are derived. When you create a new Random object, this seed is set as the current system time in seconds or milliseconds.

Let's say when you create the first random object, the seed is 10,000. After a three-time call, the seeds were 20,000, 40,000, 80,000, generating any numbers from the seeds (let them say 5, 6, 2). If you create a new random object very quickly, the same seed, 10000 will be used. Thus, if you name it three times, you will get the same seeds, 20,000, 40,000 and 80,000, and the same numbers from them.

However, if you reuse the same object, the last seed was 80,000, so instead you create three new samples, 160,000, 320,000, and 640,000, which will most likely give you new values.

This is why you should use one random generator without creating a new one every time.

0
source share

Try using Random () only once. You will get this idea.

0
source share

All Articles