繁体   English   中英

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

[英]Pick a varying number of item combinations from a List

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

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

例如,X = 1将返回:

1

3

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

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

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

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

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

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

这对我有用:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM