特定范围内的整数的每种组合以及字符串列表

时间:2020-07-24 12:18:05

标签: c# list algorithm

我有以下问题:

我有三个元素a,b和c。以及从0到100的整数。如何获得所有可能的组合,如下所示:

a 0 b 0 c 0
a 1 b 0 c 0
a 0 b 1 c 0
a 0 b 0 c 1
a 1 b 1 c 0
...
a 100 b 100 c 100

等等?我正在使用C#,但我想尽自己的努力独立于编程语言来找到正确的算法。不幸的是,我不太了解笛卡尔积等。

2 个答案:

答案 0 :(得分:1)

您说要

独立于编程语言来找到正确的算法

因此,我将尝试使用最少的编程语言功能来回答这个问题。我将给出的示例假定编程语言具有可扩展的列表,数组,数组数组以及浅表克隆数组的能力。这些是常见的编程功能,因此希望可以。

要解决此问题,您需要生成3组N个整数的所有组合,其中每组包括0..N-1个整数。 (一组集合的组合集合-这就是所谓的这些集合的笛卡尔积。)

下面的解决方案使用递归,但是我们不必担心堆栈溢出,因为堆栈深度不超过要组合的集合数-在这种情况下为3。(通常使用递归,您可以尝试使用堆栈类来管理它,但这会使代码更加复杂。)

工作方式:

combine()递归地遍历每个集合的所有元素,并在每个递归级别上开始处理下一个集合的元素。

因此,递归的外部级别开始在set [0]的所有元素上进行迭代,并且对于每个元素,它都使用该元素填充当前组合的下一项。

然后:如果是最后一组,则完成组合并输出。否则,将进行递归调用以开始填充下一组元素。

一旦有了所有组合,我们就可以遍历它们并穿插它们 根据您的要求使用abc

将其放在一起:

using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var sets = createSets(3, 10);
            var combinations = Combinations(sets);

            foreach (var combination in combinations)
            {
                Console.WriteLine($"a {combination[0]} b {combination[1]} c {combination[2]}");
            }
        }

        static int[][] createSets(int numSets, int intsPerSet)
        {
            int[][] sets = new int[numSets][];

            // All the sets are the same, so we can just use copies of it rather than create multiples.
            int[] oneSet = new int[intsPerSet]; 

            for (int i = 0; i < intsPerSet; ++i)
                oneSet[i] = i;

            for (int i = 0; i < numSets; ++i)
                sets[i] = oneSet;

            return sets;
        }

        public static List<int[]> Combinations(int[][] sets)
        {
            var result = new List<int[]>();
            combine(sets, 0, new int[sets.Length], result);
            return result;
        }

        static void combine(int[][] sets, int set, int[] combination, List<int[]> output)
        {
            for (int i = 0; i < sets[set].Length; ++i)
            {
                combination[set] = sets[set][i];

                if (set < (sets.Length - 1))
                    combine(sets, set + 1, combination, output);
                else
                    output.Add((int[])combination.Clone());
            }
        }
    }
}

注释

  1. 这是一种低效的实现,因为它会在一个巨大的列表中返回所有组合。为了简化起见,我采用了这种方式(并减少了其实现所需的程序语言功能的数量)。在C#中,更好的解决方案是传递一个Action<int[]>并与每个组合一起调用-这样就不必通过庞大的列表返回结果。
  2. 这不会产生与示例输出相同顺序的结果。我以为这没关系!
  3. 笛卡尔乘积的一个很好的Linq实现是presented by Eric Lippert here。我强烈建议您阅读它!

答案 1 :(得分:0)

如果输出的顺序无关紧要,那应该足够了:

for(int i = 0; i <= 100; i++){
    for(int j = 0; j <= 100; j++){
        for(int k = 0; k <= 100; k++){
            Console.WriteLine($"a {i} b {j} c {k} ");
        }
    }
}

输出

a 0 b 0 c 0 
a 0 b 0 c 1 
a 0 b 0 c 2 
a 0 b 0 c 3 
...
a 100 b 100 c 100