从列表中选择不同数量的项目组合

时间:2016-03-21 03:02:11

标签: c# .net for-loop

假设我有一个任意长度的整数列表,例如我有1,3,5和7的列表。

我想要一个算法从列表中选择X元素的组合。

例如,X = 1将返回:

1

3

5

7

x = 2会返回:

1 + 1

1 + 3

1 + 5

1 + 7

3 + 3

3 + 5

3 + 7

5 + 5

5 + 7

7 + 7

var listOfInts = new List<int> { 1, 3, 5, 7 };
var combinedInts = new List<int>();

// x = 1 solution
// This is only picking one item from the list. 
for (int i = 0; i < listOfInts.Count(); i++)
{
    combinedInts.Add(listOfInts[i]);
}

// x = 2 solution
// This is how to pick two. I wrap it around another for loop.
for (int i = 0; i < listOfInts.Count(); i++)
{
    for (int j = i; j < listOfInts.Count(); j++)
    {
        combinedInts.Add(listOfInts[i] + listOfInts[j]);
    }
}

// x = 3 solution
// If I go up another level I have to wrap it around another for loop. This solution won't scale.
for (int i = 0; i < listOfInts.Count(); i++)
{
    for (int j = i; j < listOfInts.Count(); j++)
    {
        for (int k = j; k < listOfInts.Count(); k++)
        {
            combinedInts.Add(listOfInts[i] + listOfInts[j] + listOfInts[k]);
        }
    }
}

这个解决方案没有扩展,因为我必须不断地为另一个元素I拣选另一个for循环。例如,X = 7需要7个嵌套for循环。是否有更好的方法来编写这种不涉及嵌套的方法?

3 个答案:

答案 0 :(得分:3)

您可以使用以下内容获取序列的组合

public static class LinqHelper
{
    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int? k = null)
    {
        if (!k.HasValue)
            k = elements.Count();

        return k == 0 ? new[] { new T[0] } :
           elements.SelectMany((e, i) => elements.Skip(i).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
    }
}

var list = new List<int> { 1, 3, 5, 7 };

int x = 2; //Change to 3, 4, 5, etc

var result = list.Combinations(x);

收率:

  

1 1
  1 3
  1 5
  1 7
  3 3
  3 5
  3 7
  5 7
  7 7

要获得每个的总和,您需要汇总结果:

var result = list.Combinations(x).Select(g => g.Aggregate((left, right) => left + right));

产生:

  

2
  4
  6
  8
  6
  8
  10个
  10个
  12个
  14

答案 1 :(得分:2)

还有一种纯粹的迭代方式来做到这一点。它需要更多的思考和复杂性,但可以非常有效。基本思想是模拟相同的嵌套循环,但跟踪每个嵌套循环的迭代作为循环计数器的数组,它们以与原始嵌套循环代码相同的方式向前迭代。这是一个完整的例子:

var listOfInts = new List<int> { 1, 3, 5, 7 };
var combinedInts = new List<int>();

var numInts = listOfInts.Count;
var numElements = 5; // number of "nested loops", or ints selected in each combination
var loopCounters = new int[numElements]; // make one loop counter for each "nested loop"
var lastCounter = numElements - 1; // iterate the right-most counter by default

// maintain current sum in a variable for efficiency, since most of the time
// it is changing only by the value of one loop counter change.
var tempSum = listOfInts[0] * numElements;

// we are finished when the left/outer-most counter has looped past number of ints
while (loopCounters[0] < numInts) {
    // you can use this to verify the output is iterating correctly:
    // Console.WriteLine(string.Join(",", loopCounters.Select(x => listOfInts[x])) + ": " + loopCounters.Select(x => listOfInts[x]).Sum() + "; " + tempSum);

    combinedInts.Add(tempSum);

    tempSum -= listOfInts[loopCounters[lastCounter]];
    loopCounters[lastCounter]++;
    if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];

    // if last element reached in inner-most counter, increment previous counter(s).
    while (lastCounter > 0 && loopCounters[lastCounter] == numInts) {
        lastCounter--;
        tempSum -= listOfInts[loopCounters[lastCounter]];
        loopCounters[lastCounter]++;
        if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];
    }

    // if a previous counter was advanced, reset all future counters to same
    // starting number to start iteration forward again.
    while (lastCounter < numElements - 1) {
        lastCounter++;
        if (loopCounters[lastCounter] < numInts) tempSum -= listOfInts[loopCounters[lastCounter]];
        loopCounters[lastCounter] = loopCounters[lastCounter - 1];
        if (loopCounters[lastCounter] < numInts) tempSum += listOfInts[loopCounters[lastCounter]];
    }
}

在迭代结束时,combinedInts应包含所有总和组合的列表,类似于原始代码或其他递归解决方案。如果您正在使用小集合和小组合,那么这种效率水平是不必要的,您应该更喜欢递归解决方案,这更容易推断正确性。我将此作为思考问题的另一种方式。干杯!

答案 2 :(得分:1)

这对我有用:

Func<IEnumerable<int>, int, IEnumerable<IEnumerable<int>>> generate = null;
generate = (xs, n) =>
    (xs == null || !xs.Any())
        ? Enumerable.Empty<IEnumerable<int>>()
        : n == 1
            ? xs.Select(x => new [] { x })
            : xs.SelectMany(x => generate(xs, n - 1).Select(ys => ys.Concat(new [] { x })));

int[] array = { 1, 3, 5, 7, };

var results =
    generate(array, 3)
        .Select(xs => String.Join("+", xs));

通过这个电话,我得到了:

  

1 + 1 + 1,3 + 1 + 1,5 + 1 + 1,7 + 1 + 1,1 + 3 + 1,3 + 3 + 1,5 + 3 + 1,7 + 3 + 1 ,1 + 5 + 1,3 + 5 + 1,5 + 5 + 1,7 + 5 + 1,1 + 7 + 1,3 + 7 + 1,5 + 7 + 1,7 + 7 + 1,1 + 1 + 3,3 + 1 + 3,5 + 1 + 3,7 + 1 + 3,1 + 3 + 3,3 + 3 + 3,5 + 3 + 3,7 + 3 + 3,1 + 5 + 3,3 + 5 + 3,5 + 5 + 3,7 + 5 + 3,1 + 7 + 3,3 + 7 + 3,5 + 7 + 3,7 + 7 + 3,1 + 1 + 5 ,3 + 1 + 5,5 + 1 + 5,7 + 1 + 5,1 + 3 + 5,3 + 3 + 5,5 + 3 + 5,7 + 3 + 5,1 + 5 + 5,3 + 5 + 5,5 + 5 + 5,7 + 5 + 5,1 + 7 + 5,3 + 7 + 5,5 + 7 + 5,7 + 7 + 5,1 + 1 + 7,3 + 1 + 7,5 + 1 + 7,7 + 1 + 7,1 + 3 + 7,3 + 3 + 7,5 + 3 + 7,7 + 3 + 7,1 + 5 + 7,3 + 5 + 7 ,5 + 5 + 7,7 + 5 + 7,1 + 7 + 7,3 + 7 + 7,5 + 7 + 7,7 + 7 + 7