【问题标题】:Iterate every combination of two elements in HashSet迭代HashSet中两个元素的每个组合
【发布时间】:2014-06-08 02:37:08
【问题描述】:

如何遍历 HashSet 中两个元素的每个组合一次?

foreach (var elt1 in hashSet) {
  foreach (var elt2 in hashSet) {
    ...
  }
}

这将迭代两个组合,但将迭代每个组合两次。我想做一次。

我认为用 Python 很容易做到。有没有办法在 C# 中做到这一点?

示例:

输入哈希集:{ 1, 2, 3, 4 }

遍历:(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)

【问题讨论】:

  • ToArray() hashSet 然后在数组上使用嵌套循环?
  • 这个可以,是的。有没有更简洁的方法?
  • 如果 hashSet 包含 n 项,则应有 n * n 个组合;生成它们的最简单方法是嵌套循环,INHO
  • 能否请您详细说明一下...表示您的输入是什么样的,输出是什么以及您想要执行的操作,以便我们更好地了解整个事情
  • @yper 在我的头上,我不这么认为。只需ToArray()For i 0 → sizeFor j i → size

标签: c# .net linq hashset


【解决方案1】:

我最初误读了这个问题。这是一个新答案

这就是你想要的(如果基于索引的工作是一个选项)。说明如下

string[] myArray = GetArray();

for (int i = 0; i < myArray.Length - 1; i++)
{
    var element1 = myArray[i];

    for(int j = i + 1; j < myArray.Length; j++)
    {
        var element2 = myArray[j];
        Console.WriteLine("{0} {1}", element1, element2);
    }
}

说明:假设如下数组:

Apple, Banana, Coconut, Zucchini

i = 0(Apple)时,j 将是 1(香蕉),然后是 2(椰子),然后是 3(西葫芦)

i = 1(香蕉)时,j 将是 2(椰子),然后是 3(西葫芦)。

等等……

基本上,您要确保元素 j 总是在元素 i 之前。这意味着您已经有效地消除了一半的可能性(其中 j 将在 i 之前),这正是您想要的。

注意:如果要使用相等元素的集合(Apple + Apple),则第二个 for 循环需要更改为:

    for(int j = i; j < myArray.Length; j++) //notice j = i instead of i + 1

【讨论】:

  • 每个组合不仅意味着相邻元素。
  • 我改变了答案。我完全误读了它。这个是正确的:)
【解决方案2】:

要返回所有排列(即(1,2)(2,1)),您可以使用SelectMany 交叉加入集合:

 var hashSet = new HashSet<int>{1,2,3,4,5,6,7,8};
 foreach (var elt in hashSet.SelectMany(
                     x => hashSet.Select(y => new Tuple<int, int>(x, y))))
 {
    Debug.WriteLine("{0}-{1}", elt.Item1, elt.Item2);
 }

编辑:如果您只想要唯一的组合(即(1,2) 而不是(2,1)),那么只需在交叉连接期间添加一个过滤器,只需要更大的值:

 var hashSet = new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
 foreach (var elt in hashSet.SelectMany(
                     x => hashSet.Where(y => y >= x)
                                 .Select(y => new Tuple<int, int>(x, y))))
 {
    Debug.WriteLine("{0}-{1}", elt.Item1, elt.Item2);
 }

【讨论】:

  • 这并不能防止相同的组合发生两次,例如1+4 和 4+1。如果我理解正确的话,这就是 OP 想要的。
  • OP 的原始问题从元素 0 开始循环两次,从而给出所有排列。
  • 引用自 OP:...but would iterate each combination TWICE. I'd like to do it once. 这意味着元素的顺序是不相关的,1+4 等于 4+1 并且不应该为每个排列运行,只为组合运行。
  • 我认为 OP 正在寻找 C(n, 2) 组合。
  • @Fung:我想是的。不知道这些符号了:)
【解决方案3】:

在 C# 中没有内置的方法可以做到这一点。因为HashSet&lt;T&gt; 没有被索引 *,所以你也不能用两个循环来做。

如果这是一次性交易,最简单的解决方案是对ToList()ToArray() 的结果进行两个嵌套循环,如下所示:

var items = hashSet.ToList();
for (var i = 0 ; i != items.Count ; i++) {
    var a = items[i];
    for (var j = i+1 ; j != items.Count ; j++) {
        var b = items[i];
    }
}

如果您正在寻找可重用的东西,请在 IEnumerable&lt;T&gt; 上创建一个生成所有对的扩展方法:

static IEnumerable<Tuple<T,T>> MakeAllPairs<T>(this IEnumerable<T> data) {
    var items = data.ToList();
    for (var i = 0 ; i != items.Count ; i++) {
        var a = items[i];
        for (var j = i+1 ; j != items.Count ; j++) {
            var b = items[i];
            yield return Tuple.Create(a, b);
        }
    }
}

现在您可以在一个循环中迭代您的配对:

foreach (var pair in hashSet.MakeAllPairs()) {
    Console.WriteLine("{0} {1}", pair.Item1, pair.Item2);
}

* 从技术上讲,您可以使用来自 EnumerableElementAt&lt;T&gt;(int) 扩展名,但在大型集上会非常慢。

【讨论】:

    【解决方案4】:

    您可以直接在 HashSet 上使用索引。 试试这个:

    int int1, int2;
    HashSet<int> hs = new HashSet<int>();
    hs.Add(1);
    hs.Add(2);
    hs.Add(3);
    for (int i = 0; i < hs.Count-1; i++) {
        int1 = hs.ElementAt<int>(i);
        for (int j = i + 1; j < hs.Count; j++)
        {
            int2 = hs.ElementAt<int>(j);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-10-01
      • 1970-01-01
      • 2015-12-25
      • 1970-01-01
      • 2021-12-16
      • 2015-01-17
      • 1970-01-01
      • 2013-12-27
      相关资源
      最近更新 更多