根据百分比从 IEnumerable 中选择行
本文关键字:选择 IEnumerable 百分比 | 更新日期: 2023-09-27 18:33:37
我目前有一个每5分钟运行一次的Windows服务。该代码从数据库中选择要处理的行。有一个上限(允许选择的最大行数),因此选择的行数可以在 0-100 之间。
我希望根据随机百分比选择对这些行进行一些处理。
- 任务1 25%
- 任务 2 50%
- 任务 3 100%
为简单起见,假设服务选择 100 行,然后随机选择的 25 行将运行任务 1,50 个随机选择的行将运行任务 2,所有行将运行任务 3。
我目前拥有的代码看起来像这样:
var rows = repository.GetRows(100);
foreach(var row in rows)
{
task1.Run(row);
task2.Run(row);
task3.Run(row);
}
这将在所有行上运行所有三个任务。我将如何只为每个任务选择分配的百分比?
可能有点质朴...
var rows = repository.GetRows(100);
rows.OrderBy(Guid.NewGuid()).Take(25).ToList().ForEach(m => task1.Run(m));
rows.OrderBy(Guid.NewGuid()).Take(50).ToList().ForEach(m => task2.Run(m));
rows.ToList().ForEach(m => task3.Run(m));
您可以定义一个执行 Fisher-Yates-Durstenfeld shuffle 的 Shuffle() 扩展方法(该方法以线性时间执行,而不是 OrderBy 的 NlogN 时间):
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> input)
{
var buffer = input.ToArray();
//Math.Random is OK for "everyday" randomness;
//you should use RNGCryptoServiceProvider if you need
//cryptographically-strong randomness
var rng = new Math.Random();
//as the loop proceeds, the element to output will be randomly chosen
//from the elements at index i or above, which will then be swapped with i;
//the yield return gives us each shuffled value as it is chosen, and
//allows the shuffling to happen "lazily".
for (int i = 0; i < buffer.Length; i++)
{
int j = rng.Next(i, buffer.Length);
yield return buffer[j];
//if we cared about the elements in the buffer this would be a swap,
//but we don't, so...
buffer[j] = buffer[i];
}
}
//simple extension method to provide List.ForEach()-like functionality
//on any collection or IEnumerable.
public static void ForEach(this IEnumerable<T> collection, Action<T> action)
{
foreach(var element in collection) action(element);
}
//Usage - pretty much the same as Raphael's,
//but now you don't have to convert to a List to use ForEach:
rows.Shuffle().Take(25).ForEach(m => task1.Run(m));
rows.Shuffle().Take(50).ForEach(m => task2.Run(m));
rows.ForEach(m => task3.Run(m));
<</div>
div class="answers"> 你可以得到一个随机的子集合,如下所示:
task1.Run(rows);
task2.Run(rows.OrderBy(x => Guid.NewGuid()).Take(25));
task2.Run(rows.OrderBy(x => Guid.NewGuid()).Take(50))
在这种情况下,您可以使用 Knuth 的随机抽样方法(从 n 中选择 m 个项目):
var rows = repository.GetRows(100);
int[] maxTake = new[] {25,50,100};
int remaining = rows.Length;
Random rand = new Random();
for (int i = 0; i < rows.Length; i++)
{
var num = rand.Next() % remaining;
if (num < maxTake[0])
{
task1.Run(rows[i]);
maxTake[0]--;
}
if (num < maxTake[1])
{
task2.Run(rows[i]);
maxTake[1]--;
}
if (num < maxTake[2])
{
task3.Run(rows[i]);
maxTake[2]--;
}
remaining--;
}
获得 25 个随机唯一数
Random rand=new Random()
int[] task1nums = new int[25];
for (int i=0;i<25;i++);
{
int r=rand.Next(100);
while (task1nums.Contains(r))
{
r=rand.Next(100);
}
task1nums[i]=r;
}
获得 50 个随机唯一数字
int[] task2nums = new int[50];
for (int i=0;i<50;i++);
{
int r=rand.Next(100);
while (task2nums.Contains(r))
{
r=rand.Next(100);
}
task2nums[i]=r;
}
所以现在你有 25 个随机数和 50 个随机数
var rows = repository.GetRows(100);
int counter=0
foreach(var row in rows)
{
if (task1nums.Contains(counter))
task1.Run(row);
if (task2nums.Contains(counter))
task2.Run(row);
task3.Run(row);
counter++;
}
您可以使用
Random
实例为每行生成一个介于 0.0 和 1.0 之间的随机值。
小于 0.25;大约 50% 的行的生成值小于 0.5。
var rows = repository.GetRows(100);
Random random = new Random();
task1.Run(rows.Where(_ => random.NextDouble() <= 0.25));
task2.Run(rows.Where(_ => random.NextDouble() <= 0.5));
task3.Run(row);
如果要保证将获得恰好 25% 和 50% 的行集合(向下舍入),请使用:
Random random = new Random();
var rows = repository.GetRows(100);
var rowsRandomized = rows.OrderBy(_ => random.NextDouble());
task2.Run(rowsRandomized.Take((int)(0.25 * rows.Count())));
task2.Run(rowsRandomized.Take((int)(0.5 * rows.Count())));
task3.Run(rowsRandomized);