得到一个随机数,只要它不在列表中

时间:2014-01-02 20:37:58

标签: c# random

 var list = new List<int>(){1,17,18,21,30};
 Random rnd = new Random(DateTime.Now.Second);
 int r;
 do
 {
     r = rnd.Next(1, 30);
 }
 while (list.Contains(r));

但我认为这是一个愚蠢的解决方案,任何人都可以给我一个更优化的方法吗?

如果有办法防止Random实例返回已经返回的数字,那就更好了。

如果有人想知道我为什么需要这个,这是改组3字节数组并将它们组合成一个字节数组并生成3字节数组的第一步,它保存索引原始顺序,就像它在原始数组中一样。

2 个答案:

答案 0 :(得分:5)

是的,使得它更高效的一件事是使用HashSet<int>而不是List<int>查找HashSet比列表快得多(但构造函数的成本会略高一些)对于HashSet)。

此外,如果输入列表始终是相同的数字,则将其移出函数,以帮助减少第一次生成HashSet的成本开销。


由于订单现在很重要,根据我的个人经验(请根据您自己的情况测试和配置文件),在列表中的大约14项之后,将列表转换为HashSet并执行查找比执行查找更快列表本身。

var list = new List<int>(){1,17,18,21,30};
Random rnd = new Random(DateTime.Now.Second);
int r;

//In this example with 5 items in the list the HashSet will be slower do to the cost 
// of creating it, but if we knew that the 5 items where fixed I would make this 
// outside of the function so I would only have to pay the cost once per program 
// start-up and it would be considered faster again due to amortized start-up cost.
var checkHashSet = new HashSet<int>(list); 

do
{
    r = rnd.Next(1, 30);
}
while (checkHashSet.Contains(rnd.Next(1, 30))); //Shouldent this be "r" not "rnd.Next(1,30)"?

答案 1 :(得分:3)

你是对的,循环不是特别有效。如果考虑有效数字列表的约束,则可以使用some handy extensions来选择数字,而不是无效数字列表。

所以你有无效号码列表:

var list = new List<int>(){1,17,18,21,30};

这意味着有效号码列表的范围是1-30,除了这些。类似的东西:

var validList = Enumerable.Range(1, 30).Except(list);

因此我们可以使用链接答案中的这些扩展名:

public static T RandomElement(this IEnumerable<T> enumerable)
{
    return enumerable.RandomElementUsing(new Random());
}

public static T RandomElementUsing(this IEnumerable<T> enumerable, Random rand)
{
    int index = rand.Next(0, enumerable.Count());
    return enumerable.ElementAt(index);
}

从已知有效数字列表中选择一个随机元素:

var kindOfRandomNumber = Enumerable.Range(1, 30).Except(list).RandomElement();