如何创建一个包含所有数字组合的n维数组?

3
我希望创建一个函数AllCombnations(d, maxValue),它将创建一个d维数组,包含从0到maxValue的所有数字组合。
例如,在3D空间中创建所有数字组合的硬编码版本,从0到maxValue可能是这样的:
for (int i = 0; i < maxValue; i++)
    for (int j = 0; j < maxValue; j++)
        for (int k = 0; k < maxValue; k++)
        {
            // code here
        }

我面临的问题是我无法嵌套多个for循环,也不确定该怎么做。我考虑过递归,但没有成功。非常感谢您提供任何帮助。

1
请提供一个小例子,展示您希望数据看起来的样子。 - 500 - Internal Server Error
什么是数字组合? - preciousbetine
这样的表格有什么意义呢?使用它,你可以(在这种情况下)查找任何 i, j, karray[i, j, k],但这只会给你你用来查找它的东西。 - 500 - Internal Server Error
https://dev59.com/Wm855IYBdhLWcg3wsWr3?rq=1 - Ctrl S
难道你不需要任何数学知识吗?将其放入多维数组中有什么意义呢?(不是反问,也许有一些我还不知道/不理解的点。) - Andreas
显示剩余5条评论
2个回答

3

实际上,您可以循环遍历维度。请查看Array类。

演示:

                    // [6, 6, 6] array
int rank = 3;       // 3D array - 3 dimensions
int maxValue = 6;   // Each dimension is of size 6

int[] lengths = Enumerable // {6, 6, 6} - lengths of the dimensions:
  .Repeat(maxValue, rank)  // rank times maxValue 
  .ToArray();              // materialized as array

//TODO: put the right type of arrays' items 
// In demo, let array be of type string: "string[6, 6, 6] array"
var array = Array.CreateInstance(typeof(string), lengths);

// we can't use hardcoded set (i, j, k) of variables 
// we have to address array's item via array of rank length
int[] address = new int[array.Rank];

// Single loop over all array's items (and dimensions)
do {
  //TODO: put the right value here by given address:
  //      (i == address[0], j == address[1], k == address[2] etc.)
  array.SetValue(
    string.Concat(address.Select(i => (char) (i + 'A'))), // value: "AAA", "AAB" etc. 
    address);                                             // address: [0,0,0], [0,0,1], 

  // here we compute next address
  for (int i = 0; i < address.Length; ++i)
    if (address[i] >= array.GetLength(i) - 1)
      address[i] = 0;
    else {
      address[i] += 1;
      break;
    }

  // if we get {0, 0, ..., 0} address, we've exhausted all the items
}
while (!address.All(index => index == 0));

让我们来看看数组(20个顶部项目):

  Console.WriteLine(string.Join(Environment.NewLine, array.OfType<string>().Take(20)));

结果:

AAA
AAB
AAC
AAD
AAE
AAF
ABA
ABB
ABC
ABD
ABE
ABF
ACA
ACB
ACC
ACD
ACE
ACF
ADA
ADB

1
谢谢!我以前没见过这个!我只是刚学了C#,相比之前的学习,这个更加高级,所以我会进一步研究。感谢您提供的信息! - TimeTravelPenguin
我知道这是一个旧帖子,但我发布了一个解决方案,并想知道您是否认为这是解决问题的可行方法。它比您的解决方案简单得多,但我认为让更好、更有经验的人确认我的方法是正确的会很好。谢谢。 - TimeTravelPenguin

0

我知道这是一个旧帖子了,但我确实为这个问题创建了一个解决方案。

让我通过一个示例脚本来说明这个问题。

class Program
{
    static void Main()
    {
        // Print all combinations from a to b, for n dimensions
        // e.g. 0000 to 2222 <- each dimension goes from 0 to 2, with 4 dimensions
        // Note that each dimension can have a unique start/end point
        // e.g. 1234 to 5678, so the 2nd dimensions is bound 2 <= x <= 6

        int dimensions = 4;
        int[] startValues = { 0, 0, 0, 0 };
        int[] endValues = { 2, 2, 2, 2 };

        PrintCombinations(startValues, endValues, dimensions);

        Console.ReadKey();
    }

    /// <summary>
    /// Prints all combinations of numbers given inputs
    /// </summary>
    /// <param name="start">Inclusive stating integers</param>
    /// <param name="end">Inclusive ending integers</param>
    /// <param name="dimensions">The number of dimensions to iterate</param>
    private static void PrintCombinations(int[] startValues, int[] endValues, int dimensions)
    {
        // Create new array to loop through without disturbing the original array
        int[] loopArray = (int[])startValues.Clone();

        // Loop through each value
        while (!Enumerable.SequenceEqual(loopArray, endValues))
        {
            // Write array to console
            Console.WriteLine($"{string.Join(", ", loopArray)}");

            // Increment array
            loopArray[0]++;

            // Check if a dimension is larger than it's maximum, then set to min, and add +1 to next dimension
            // Do not do this for last dimension, as loop will break once the final combination is met
            for (int i = 0; i < dimensions - 1; i++)
                if (loopArray[i] > endValues[i])
                {
                    loopArray[i] = startValues[i];
                    loopArray[i + 1]++;
                }
        }

        // Write final array combination  to console
        Console.WriteLine($"{string.Join(", ", loopArray)}");
    }
}

这是一个足够简单的示例,展示了我想要扩展“多维度”表示为数组的想法。

如果您查看PrintCombinations的底部,您将看到以下代码:

for (int i = 0; i < dimensions - 1; i++)
    if (loopArray[i] > endValues[i])
    {
        loopArray[i] = startValues[i];
        loopArray[i + 1]++;
    }

这是我编写的循环多维数组的代码,当您有用户提交的维度和其他信息时,可以避免硬编码循环(如上例所示)。

基本上,这段代码将每个维度的值存储在一个数组中。 让我们举一个三维的例子,(x,y,z)。 我们可以说点(x,y,z)= int [] {x,y,z} 如果我们说x,y和z是数组的上限,我们可以通过减去数组的第一个维度来循环遍历该数组,直到达到零,然后从以下维度中删除一个,直到达到零等等,同时重置维度为上限,或者像这个例子一样,从零加到上限,然后重置为零,并增加以下维度。

通过使用上下限的进一步数组,您可以在两个特定范围之间实现嵌套循环。在上面的例子中,我使用了上限为{2,2,2,2}

希望我已经解释清楚了。谢谢


是的,你的解决方案相当不错;一些改进是:dimensions 是一个冗余参数;你可以计算它 - int dimension = startValues.Length;,要克隆数组 - int[] loopArray = startValues.ToArray(); 而不是使用 Clone()。我建议从该方法中提取 Console.WriteLine(混合业务逻辑和UI),最后实现通用方法(如果我想要字符组合怎么办?)作为 private static IEnumerable<T[]> PrintCombinations<T>(T[] startValues, T[] endValues) {..} - Dmitry Bychenko
非常感谢您的回复。这段代码并没有很好地编写,因为它只是一个快速脚本的代码,但是您提出的其他建议非常有道理,我将来一定会考虑它们!非常感谢您的反馈! - TimeTravelPenguin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接