我正在制作一个概率游戏,以证明掠夺硬币没有记忆。换句话说,如果我连续翻转了2个头,则下一个翻盖具有相同的成为tailes或head的概率。我正在生成随机数。例如,1代表尾部,0代表头部。我将结果记录在列表中。因此该列表包含1和0。例如,如何执行选择以仅在连续3个头或尾之后获取行。换句话说,如果我有列表:
0,1,0,1,1,0,0,0,1,0,1,1,1,1
我想得到1,1
因为有三个0重复而下一个数字是1.而下一个1是因为连续3个1之后的数字是1
我知道我可以执行选择迭代循环并在内部有一个计数器,每次有重复时计数器递增但我想知道用linq查询是否可行吗
答案 0 :(得分:3)
此问题类似于其他问题Can I use LINQ to retrieve only "on change" values?
Thomas Petricek的回答建议在GroupBy扩展方法上创建覆盖。该技术应该为您提供您想要的功能,并且它可以创建在您的键断点上执行聚合功能的功能。
托马斯一定非常着迷于这个问题,因为他撰写了一篇关于创建自定义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创建IAdjacentGroup类型,该类型具有GroupBy的覆盖。
答案 1 :(得分:1)
我不知道怎么做。至少解决方案并不明显,例如,如果你这样做,
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;
});
您到了编写选择谓词的位置,并且您意识到您一次只处理列表中的一个项目。似乎没有办法确定前三个项目是什么。如果可以解决这个问题,那么就有可能。
然后你最终得到这样的东西,
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]);
}
}
}
这不一定是你想要的,但需要考虑的事情。
答案 2 :(得分:1)
任何类型的运行计数算法都需要多个状态变量:
最接近你的是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
,因为您的要求超出了“查询”。