2011-05-29 82 views
1

我正在進行一個概率遊戲以便證明分揀硬幣沒有記憶。換句話說,如果我連續翻轉了兩個頭,那麼下一個翻轉具有相同的成爲尾部或頭部的概率。我正在生成隨機數字。例如,1代表尾部,0代表頭部。我將結果記錄在列表中。因此該列表包含1和0。例如,如何執行選擇以獲得連續3個頭部或尾部後的行。換句話說,如果我有名單:在第n個重複行之後選擇下一行

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

我想1, 1

因爲有三個0,即重複和下一個數字是1,而接下來的1是因爲連續3次1的號碼是1


我知道我可以執行選擇迭代通過一個循環,並有一個櫃檯裏面,每當有重複計數器遞增,但我想知道是否是可行的做到這一點一個linq查詢

回答

3

這個問題是類似於此的其他SO質疑Can I use LINQ to retrieve only "on change" values?

由托馬斯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創建具有GroupBy覆蓋的IAdjacentGroup類型。

+0

+1。優於我的回答,因爲它以通用的方式解決了這個問題,並且不涉及任何奇怪的狀態攻擊(關閉狀態或帶有副作用的linq)。 – 2011-05-31 09:12:41

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]); 
     } 
    } 
} 

這不一定是你所追求的,但一些思考。

+0

你應該設置'int pos = 3'開始,然後你不必有'if'。 – 2011-05-30 06:24:08

1

任何類型的運行計數算法需要多個狀態變量:

  • 電流值
  • 前值
  • 當前的運行計數

最接近的事情讓你有Enumerable.Aggregate,因爲它爲您提供當前值和您選擇輸出的一些自定義每項物品值。在我們的案例中,它可能是以前的價值。

對於簡單的int[],您必須爲current run count增加一些額外的狀態。這將不得不放在您的Linq查詢語句之外。雖然它會起作用,但它與使用for-loop沒有太大區別。

如果不是修改你的枚舉是一個自定義的結構,你可以修改結構也包含運行次數,你可以做一個(有點)更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,因爲您的需求比「查詢」稍微多一點。