c# - 从数组中随机选择特定数量的索引?

标签 c# .net algorithm random

我有一个 bool 值数组,需要为 true 的值随机选择特定数量的索引。

生成索引数组的最有效方法是什么?

例如,

BitArray mask = GenerateSomeMask(length: 100000);
int[] randomIndices = RandomIndicesForTrue(mask, quantity: 10);

在这种情况下,randomIndices 的长度将为 10。

最佳答案

有一种更快的方法可以做到这一点,只需要对列表进行一次扫描。

当您不知道文件中有多少行并且文件太大而无法放入内存时,请考虑从文本文件中随机选择一行。显而易见的解决方案是读取文件一次以计算行数,在 0 到 Count-1 范围内选择一个随机数,然后再次读取文件直到选择的行数。这可行,但需要您读取文件两次。

更快的解决方案是读取第一行并将其保存为选定行。您以 1/2 的概率用下一行替换所选行。当您阅读第三行时,您以 1/3 的概率替换,等等。当您阅读整个文件时,您随机选择了一行,并且每一行被选中的概率均等。代码看起来像这样:

string selectedLine = null;
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
    ++numLines;
    double prob = 1.0/numLines;
    if (rnd.Next() >= prob)
        selectedLine = line;
}

现在,如果你想选择 2 行怎么办?你选择前两个。然后,当读取每一行时,它将替换两行之一的概率是 2/n,其中 n 是已读取的行数。如果确定需要替换一条线,则随机选择要替换的线。您可以按照相同的基本思路随机选择任意数量的行。例如:

string[] selectedLines = new int[M];
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
    ++numLines;
    if (numLines <= M)
    {
        selectedLines[numLines-1] = line;
    }
    else
    {
        double prob = (double)M/numLines;
        if (rnd.Next() >= prob)
        {
            int ix = rnd.Next(M);
            selectedLines[ix] = line;
        }
    }
}

您可以很容易地将其应用于您的 BitArray:

int[] selected = new int[quantity];
int num = 0;  // number of True items seen
Random rnd = new Random();
for (int i = 0; i < items.Length; ++i)
{
    if (items[i])
    {
        ++num;
        if (num <= quantity)
        {
            selected[num-1] = i;
        }
        else
        {
            double prob = (double)quantity/num;
            if (rnd.Next() > prob)
            {
                int ix = rnd.Next(quantity);
                selected[ix] = i;
            }
        }
    }
}

最后您需要一些特殊情况代码来处理数组中没有 quantity set 位的情况,但您需要使用任何解决方案。

这对 BitArray 进行了一次传递,它使用的唯一额外内存是用于选定索引的列表。如果它没有明显快于 LINQ 版本,我会感到惊讶。

请注意,我使用概率计算来说明数学。您可以将第一个示例中的内部循环代码更改为:

if (rnd.Next(numLines+1) == numLines)
{
    selectedLine = line;
}
++numLines;

您可以对其他示例进行类似的更改。这与概率计算的作用相同,并且应该执行得更快一些,因为它消除了每个项目的浮点除法。

关于c# - 从数组中随机选择特定数量的索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22546179/

相关文章:

javascript - MVC 将 json 数据传递给 Controller

c# - 当我尝试使用 visual c# 连接 SQL Server 时收到此错误消息

c# - 从对象获取属性信息而不将属性名称作为字符串提供

c# - 使用 Chello 在 Trello 上创建卡片

java - 为什么我的函数给出无限输出?

c# - 在 C# 中将 IEnumerable<T> 转换为 List<T> 的最快方法

c# - 访问 Linq 查询的基本列表

c# - 将 Dictionary<int, Enumerable> 转换为 Dictionary<int, Enumerable> 反转内容

algorithm - 如何通过使用一些索引来制作高效的合并排序算法

algorithm - 这个 Prime Factor 函数的运行时间?