先前未选择的随机数

时间:2013-09-13 20:49:15

标签: c#

以下代码部分按我希望的那样工作

for (int i = 1; i <= practicehistory.TotalNumQuestions; i++)
{
    query = from a in db.Questions
            where a.CategoryId == practicehistory.CategoryId
            orderby a.QuestionId
            select a;
    randomNumber = random.Next(1, count + 1);
    int qNum = query.Skip(randomNumber - 1).First().QuestionId;
    asked.QuestionId = qNum;
    asked.OrderAsked = i;
    db.AskedHistories.Add(asked);
    db.SaveChanges();
}

但是,我偶尔会遇到随机数与之前for循环运行中的随机数相同的情况。我想知道是否有人有一个优雅的解决方案,我怎么能确保只生成一个比以前没有生成过的随机数?我正在考虑填充一个数组并检查它,但这似乎相当多余!

2 个答案:

答案 0 :(得分:7)

这样做的一种方法是生成可能值的数组(例如,整数1到N的数组),然后shuffle它们,然后根据需要迭代尽可能多的结果混洗值。如果您对“通过示例”进行编码感兴趣,快速Google搜索会产生Fisher-Yates shuffle的几个C#实现。

答案 1 :(得分:0)

有两种方法可以解决这个问题,检查数字是否已被使用,或预先生成数字随机化它们存储的顺序,并以“混洗顺序”将它们从列表中取出。

第一种方法更好,因为当您不知道总共需要多少数字或者您拥有非常大的数字池时,您不可能多次查找相同数字的碰撞。这样做的缺点是可用总数的百分比越多,下一个数字生成运行的速度越慢。

//This function will run slower and slower until all numbers have been used and then it will throw a InvalidOperationExecption. 
//You could get a InvalidOperationExecption early if you reuse the ISet for multiple ranges.
public static int NextUnused(this Rand rand, int minValue, int maxValue, ISet<int> usedNumbers)
{
    if(usedNumbers.Count >= maxValue - minValue)
        throw new InvalidOperationExecption("All possible numbers have been used");

    int number;
    do
    {
       number = rand.Next(minValue, maxValue);

    } while(!usedNumbers.Add(number)) //if we have seen the number before it will return false and try again.

    return number;
}

当您确切知道需要多少个对象,或者可以选择一小部分可能的选择时,第二种方法会更好。

public class RandomRange
{

    public RandomRange(int start, int count) : this(start, count, new Rand())
    {
    }

    public RandomRange(int start, int count, Rand randSource)
    {
        var numberList = new List<int>(Enumerable.Range(start, count);

        Shuffle(numberList);

        _numbers = new Queue<int>(numberList);
    }

    //Will throw a InvalidOperationExecption when you run out of numbers.
    public int GetNextNumber()
    {
        return _numbers.Dequeue();
    }

    private static void Shuffle(List<int> list)
    {
         throw new NotImplementedException("An exercise for the reader");
    }

    private Queue<int> _numbers;
}