簡體   English   中英

C# 將一個列表拆分為 n 個組的所有組合 - 從 Python 的代碼遷移

[英]C# split a list into all combinations of n groups - code migration from Python

我在這里追求的算法有一個很好的實現( @lazy dog )。 但是,我需要在 c# 中使用它,並且由於 C# 缺乏yield from以及可能是我自己的笨拙,因此轉換並不是微不足道的。

這是我目前擁有的:

public static IEnumerable<ArrayList> sorted_k_partitions(int[] seq, int k) {
  var n = seq.Length;
  var groups = new ArrayList(); //a list of lists, currently empty

  IEnumerable<ArrayList> generate_partitions(int i) {
    if (i >= n) {
      // this line was the bug, was not creating a 
      //   deep clone of the list of lists
      // yield return new ArrayList(groups); 
      yield return new ArrayList(groups.ToArray().Select(g => ((List<int>)g).ToList())); 
      // Ugly but that is because we are using ArrayList
      // Using proper List<List<int>> cleans this up significantly
    }
    else {
      if (n - i > k - groups.Count)
        foreach (List<int> group in new ArrayList(groups)) {
          group.Add(seq[i]);
          // yield from generate_partitions(i + 1);
          foreach (var d in generate_partitions(i + 1)) {
            yield return d;
          }
          group.RemoveAt(group.Count - 1);
        }

      if (groups.Count < k) {
        groups.Add(new List<int> {seq[i]});
        foreach (var d in generate_partitions(i + 1)) {
          // things start breaking down here, as this yield return
          //  appears to release flow control and we then get the 
          //  yield return above.  I have debuged this and the python
          //  version and the python version does not do this.  Very hard
          //  to explain considering I don't fully understand it myself
          yield return d;
        }
        groups.RemoveAt(groups.Count - 1);
      }
    }
  }
  return generate_partitions(0);
  // don't worry about the sorting methods in the python 
  //   version, not needed
}

任何人都可以看到任何明顯的錯誤,我確信我對 Python 的yield from和 coroutines 缺乏了解在這里傷害了我。

編輯:發現錯誤,在上面添加評論

好的,我想出的一個很好的工作解決方案在這里:

public static IEnumerable<List<List<int>>> CreatePartitions(int[] seq, int k) {
  var n = seq.Length;
  var groups = new List<List<int>>(); 

  IEnumerable<List<List<int>>> generate_partitions(int i) {
    if (i >= n) {
      yield return new List<List<int>>(groups.Select(g => g.ToList()));
    }
    else {
      if (n - i > k - groups.Count)
        foreach (var group in new List<List<int>>(groups)) {
          group.Add(seq[i]);
          foreach (var d in generate_partitions(i + 1)) {
            yield return d;
          }
          group.RemoveAt(group.Count - 1);
        }

      if (groups.Count < k) {
        groups.Add(new List<int> {seq[i]});
        foreach (var d in generate_partitions(i + 1)) {
          yield return d;
        }
        groups.RemoveAt(groups.Count - 1);
      }
    }
  }
  return generate_partitions(0);
}

正如您所期望的那樣,這比 python 快一點,但仍然不是很好。 我嘗試了並行化,但並沒有走得太遠。 我還嘗試刪除一些對象創建並使用 Array.Copy 來代替。 造成的混亂不值得微不足道的性能改進。 我想這只是很慢,因為隨着數字變大(比如 15-20 個項目的 seq),組合的數量非常龐大,沒有任何優化可以幫助將其變成一個更容易處理的問題。

你在這里有什么行為?

在我看來yield return generate_partitions(i + 1); 而不是 foreach 循環應該可以正常工作。 它只會使用新值i+1遞歸調用函數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM