如果没有Thread.Sleep(300),如何在这个类中获得真正的随机性?

时间:2010-09-07 15:52:25

标签: c# random

我已经创建了一个类(下面的代码)来处理在测试中创建“匹配”测验项目,这是输出:

alt text

工作正常。

然而,为了让它完全随机,我必须让线程进入睡眠状态,在两列的随机改组之间至少有300个计数,任何事情低于300会返回按相同顺序排序的两列,就像它使用相同的种子进行随机性一样:

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

如果没有等待这段时间,我需要做什么才能使两列完全随机混乱?

完整代码:

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 个答案:

答案 0 :(得分:10)

Random rng = new Random();移至静态变量。

MSDN说“默认种子值是从系统时钟导出的,具有有限的分辨率”。当您在较小的时间范围内创建许多Random个对象时,它们都会获得相同的种子,第一个值将等于所有随机对象。

通过重复使用相同的Random对象,您将前进到给定种子的下一个随机值。

答案 1 :(得分:6)

只创建一个Random类的一个实例。当你在没有构造函数的情况下调用它时,它会从计算机时钟中获取一个随机种子,因此你可以获得两次相同的种子。

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;
        }
    }
}

答案 2 :(得分:4)

  

我必须让线程入睡   随机之间至少有300个计数   两列的洗牌,什么的   低于300会返回两列   以相同的顺序排序,就好像它一样   使用相同的种子进行随机性

你在这里回答了自己的问题。它“就好像它使用相同的种子”,因为 使用相同的种子!由于Windows系统时钟的粒度相对较粗,多个{{1几乎在同一时间构造的实例将具有相同的种子值。

作为Albin suggests,您应该只有一个Random对象并使用它。这种方式而不是一堆伪随机序列,它们都从同一个种子开始,因此是相同的,你的Random方法将基于单个伪随机序列。

考虑到您将其作为扩展方法,您可能希望它可以重复使用。在这种情况下,请考虑使用接受Shuffle的重载和不接受的重载:

Random

这允许调用者提供静态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()); } 对象,如果他/她将连续多次调用Random;另一方面,如果它只是一次性事物,Shuffle可以处理Shuffle实例化本身。

我要指出的一个最后事情是,由于解决方案涉及使用单个共享Random对象,您应该知道Random类不是线程-安全。如果您有可能同时从多个线程调用Random,则需要锁定Shuffle调用(或者:我更喜欢为每个线程设置一个Next对象线程,每个人都在一个由“核心”[ThreadStatic] Random提供的随机值上播种 - 但这更复杂一些。)

否则你最终可能会Random突然重新调整无数的零序列。

答案 3 :(得分:2)

问题是您正在创建Random个对象,它们之间的距离太近了。当你这样做时,它们的内部伪随机生成器以相同的系统时间播种,它们产生的数字序列将是相同的。

最简单的解决方案是重用单个Random对象,方法是将其作为参数传递给shuffle算法,或者将其存储为实现shuffle的类的成员变量。

答案 4 :(得分:0)

随机生成器的工作方式大致是,它们有一个种子,从中可以得到随机值。创建新的Random对象时,此种子将设置为当前系统时间(以秒或毫秒为单位)。

让我们说当你创建第一个随机对象时,种子是10000.在调用它三次之后,种子是20000,40000,80000,从而生成种子中的任何数字(比方说5,6,2)。如果你很快创建一个新的Random对象,将使用相同的种子,10000。所以,如果你调用它三次,你将获得相同的种子,20000,40000和80000,以及它们相同的数字。

但是,如果重新使用同一个对象,则最新的种子为80000,因此您将生成三个新种子,160000,320000和640000,它们很可能会为您提供新值。

这就是为什么你必须使用一个随机生成器,而不是每次都创建一个新生成器。

答案 5 :(得分:0)

尝试使用Random()一次。你会明白的。