选择第n个重复行之后的下一行

本文关键字:一行 之后 选择 | 更新日期: 2023-09-27 17:57:52

我正在做一个概率游戏,以证明翻转硬币没有内存。换言之,如果我连续翻转了两个头部,下一个翻转有同样的概率是尾巴或头部。我正在生成随机数。例如,1表示尾部,0表示头部。我正在将结果记录在列表中。因此,该列表包含1和0。例如,如何执行选择以仅获得3个连续头或尾之后的行。换句话说,如果我有清单:

0,1,0,1,1,0,0,1,0,1,1,1,1

我想得到1,1

因为有三个0重复,下一个数字是1。下一个1是因为连续3个1之后的数字是1


我知道我可以在循环中执行选择迭代,并在里面有一个计数器,每次有重复的计数器增量,但我想知道用linq查询是否可以做到这一点

选择第n个重复行之后的下一行

这个问题与另一个SO问题类似。我可以使用LINQ只检索吗;关于变化";价值观

Thomas Petricek的回答建议在GroupBy扩展方法上创建覆盖。该技术应该为您提供所需的内容,此外,它还可以在关键点中断时执行聚合函数。

Thomas一定非常关注这个问题,因为他写了一篇关于创建一个自定义GroupBy来对相邻键值进行分组的深入文章。这是那篇文章:http://tomasp.net/blog/custom-linq-grouping.aspx

如果您使用本文中建议的技术,那么您就有了一种很好的、干净的方法来提取重复的行。

int[] coinTossResults = {0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1};
var tails = (from x in coinTossResults.WithAdjacentGrouping()
             group x by x into g
             where g.Count() > 1 && g.Key == 1
             select g);

WithAdjacentGrouping创建具有GroupBy覆盖的IAdjacentGroup类型。

我不知道该怎么做。至少解决方案并不明显,例如,如果你这样做,

    int[] integers = new int[] {0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1};
    var values = integers.Select(x => 
        {
            return x == 1;
        });

到了编写select谓词的地步,您意识到一次只处理列表中的一项。似乎没有办法确定前三项是什么。如果这个问题能够得到解决,那么这可能是可能的。

然后你会得到这样的东西,

int[] integers = new int[] {0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1};
List<int> results = new List<int>();
for (int pos = 0; pos < integers.Length; pos++)
{
    if (pos > 2)
    {
        if (integers[pos - 1] == integers[pos - 2] && integers[pos - 2] == integers[pos - 3])
        {
            results.Add(integers[pos]);
        }
    }
}

这不一定是你想要的,而是需要思考的。

任何类型的运行计数算法都需要多个状态变量:

  • 电流值
  • 以前的值
  • 当前运行计数

最接近实现目标的是Enumerable.Aggregate,因为它既提供了当前值,也提供了您选择输出的一些自定义的每项值。在我们的情况下,它可能是以前的值。

对于简单的int[],您必须为current run count提供一些额外的状态。这必须放在Linq查询语句之外。虽然它可以工作,但它与使用for循环没有太大区别。

如果您改为将枚举修改为自定义结构,则可以将该结构修改为也包含运行计数,并且可以执行(稍微)更为linq友好的操作:

class Program
{
    class CoinToss
    {
        public int Value;
        public int RunCount;
    }
    static void Main(string[] args)
    {
        int[] values = new int[]
        {
            0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
            1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1
        };
        var coinTosses = values
            .Select(v => new CoinToss() { Value = v, RunCount = 1 })
            .ToList();
        coinTosses.Aggregate(
            (previous, current) =>
            {
                current.RunCount = current.Value == previous.Value
                    ? previous.RunCount + 1
                    : 1;
                return current;
            });
        foreach (var coinToss in coinTosses)
        {
            Console.WriteLine("Value: {0}, Run Count: {1}",
                coinToss.Value,
                coinToss.RunCount);
        }
    }
}

请注意,Linq操作有副作用有点奇怪,所以这完全取决于你想要有多纯净…

之后,您可以简单地选择:

coinTosses.Where(coinToss => coinToss.RunCount >= 3);

不幸的是,您无法链接Aggregate函数,因此必须构建整个列表才能正常工作。如果这是一个问题,您应该简单地使用循环和yield return,因为您的需求有点超出了"查询"。