有两种方法可以解决这个问题,检查数字是否已被使用,或者预先生成数字随机化它们存储的顺序,并以“随机顺序”将它们从列表中取出。
当您不知道总共需要多少个数字或者您有一个非常大的数字池并且您不太可能因多次找到相同的数字而发生冲突时,第一种方法会更好。这样做的缺点是,使用的可用总数的百分比越多,下一个数字生成运行的速度就越慢。
//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;
}