C# 获取随机数的更快方法
我有个问题 我需要随机数,但除了我已经有的数 我的代码:C# 获取随机数的更快方法,c#,C#,我有个问题 我需要随机数,但除了我已经有的数 我的代码: List<int> current_numbers = repository.GetCurrentNumbers(); Random rnd = new Random(42); var new_values = Enumerable.Range(10000000,99999999) .Except(current_numbers) .OrderBy(o=> rnd.Next())
List<int> current_numbers = repository.GetCurrentNumbers();
Random rnd = new Random(42);
var new_values = Enumerable.Range(10000000,99999999)
.Except(current_numbers)
.OrderBy(o=> rnd.Next())
.Take(amount)
.ToList();
但是这个代码非常慢
当我尝试使用select代替OrderBy时,我得到了重复的结果。
就我而言,它必须没有副本
HashSet<int> current_numbers = new HashSet<int>(repository.GetCurrentNumbers());
HashSet<int> newValues = new HashSet<int>();
while (newValues.Count < amount)
{
var next = rnd.Next(10000000,99999999);
if (!current_numbers.Contains(next))
newValues.Add(next);
}
更新:
使用OrderBy-我的内存有问题:
范围必须是1M-99M
谢谢。使用哈希集而不是列表,然后使用Contains进行测试-如果您仔细看一下,您会注意到Except将把那些现有的数字构建到一个集合中
由于OrderBy尝试对整个集合进行排序,因此使用OrderBy(而不是使用常规循环并生成随机数)将失去延迟执行枚举的好处
var random = new Random(); // Default constructor or you'll get the same sequence because of a constant seed
var result = new HashSet<int>();
var currentNumbers = new HashSet<int>(repository.GetCurrentNumbers());
while(result.Count < amount)
{
var next = random.Next(1000000,99000000);
if(currentNumbers.Contains(next)) continue;
result.Add(next);
}
或者写你自己的发电机
public static IEnumerable<int> GenerateRandom()
{
var random = new Random();
while(true) { yield return random.Next(1000000,99000000); }
}
// Later...
var newValues = MyClass.GenerateRandom()
.Where(next => !currentNumbers.Contains(next))
.Distinct()
.Take(amount)
.ToList();
由于您需要的数字范围如此之大,因此可能需要使用哈希集来消除重复
HashSet<int> current_numbers = new HashSet<int>(repository.GetCurrentNumbers());
HashSet<int> newValues = new HashSet<int>();
while (newValues.Count < amount)
{
var next = rnd.Next(10000000,99999999);
if (!current_numbers.Contains(next))
newValues.Add(next);
}
将当前\u号码转换为哈希集将加快此过程,因为如果当前\u号码未存储为哈希集,则调用Contains将花费很长时间。为了避免创建如此庞大的号码列表,您可以跟踪您拥有的号码,并随机选择下一个号码的来源。首先,您需要一个已使用号码的有序列表。然后向其添加上下限。然后跟踪上下界的索引。然后迭代,直到有足够的数量,每次都在下界索引和上界索引之间随机选取一个索引。检查该索引处的数字与下一个索引处的数字之间的差值是否为1,如果是,则增加索引,直到它不为1或达到上限。如果达到上限,则只需向下走上限,然后重试。当您在已用编号中找到一个缺口时,随机选择缺口中的一个编号,并将其添加到您的退货列表和位于适当索引处的已用编号列表中。然后,如果需要,确保向上移动下限索引
var used = repository.GetCurrentNumbers().OrderBy(x => x).ToList();
used.InsertAt(0, 999999) // This is the lower bounds;
used.Add(100000000); // This is the upper bounds;
var random = new Random();
var values = new List<int>();
int lowerIndex = 0;
int upperIndex = used.Length - 1;
while(values.Count < amount) {
int ind = random.Next(lowerIndex, upperIndex);
while(ind < upperIndex && used[ind+1] - used[ind] == 1) ind++;
if(ind == upperIndex){
while(used[upperIndex] - used[upperIndex-1] == 1) upperIndex--;
continue;
}
int val = random.Next(used[ind]+1, used[ind+1]);
values.Add(val);
used.InsertAt(ind+1, val);
while(used[lowerIndx+1] - used[lowerIndex] == 1) lowerIndex++;
}
如果数量不是一个很大的数字,并且您的总体范围很大,并且初始使用的数字也很稀疏,则此方法效果最好。列表中需要多少数字?42?我不知道C,但典型的方法是将可用的数字放入一个列表中,然后要么洗牌列表并弹出第一个元素,要么从中随机抽取。如果您使用的是支持O1访问的结构,那么后者应该是O1访问,因此它应该是快速的。没有副本是什么意思?计算机可以存储有限的值,因此无法确保真正的唯一性。有一些近似值,比如GUID。我没有可用的数值。它的所有数字都在这个范围内。我需要数一数。在随机-只要把42。随机可以是空的constructior@hatchet更具体地说,这可能是一个例外。可枚举。范围本身不会使用太多内存,因为。但是,Except会创建一个底层哈希集,将所有元素添加到该哈希集中,因此那里的内存使用量将激增。当然,这只是因为OP需要1亿个无重复的整数。这将导致一组新结果和当前结果的组合,而不是新结果的单独集合