【问题标题】:looping over multidimensional array in a single loop在一个循环中循环多维数组
【发布时间】:2018-07-14 10:07:01
【问题描述】:

想象有一个包含 2 列的多维数组,每列具有完全相同的元素数:

int[,] virtualArray = new int[800,2];

如果我要在单个循环中迭代这样的数组 - 我会做类似的事情:

int columnCount = 2;
int eachColumnElementCount = 800;
int totalCombinationCount = Convert.ToInt32(Math.Pow(eachColumnElementCount, columnCount)); // 640_000

for(int i = 0; i < totalCombinationCount ; i++) {
        int columnOneIndex= index / totalCombinationCount ;
        int columnTwoIndex= index - totalCombinationCount * eachColumnElementCount;
}

结果将是:

0,0 (i = 0)

0,1

..

0,799 (i = 799)

1,0 (i = 800)

1,1

..

1,799

..

799,799 (i = 640_000 - 1)

现在,我想扩展我当前的实现,在一个单维循环中迭代——在一个具有 3 列的多维数组上! IE。假设我们在每列中有相同的 800 个元素计数,预期结果应如下:

int[,] virtualArray = new int[800,3];

int columnCount = 3;
int eachColumnElementCount = 800;
int totalCombinationCount = Convert.ToInt32(Math.Pow(eachColumnElementCount, columnCount)); // 512_000_000

for(int i = 0; i < totalCombinationCount ; i++) {
        // int index1 = 0; // ??
        // int index2 = 0; // ??
        // int index3 = 0; // ??
}

0,0,0 (i = 0)

0,0,1

...

0,0,799

...

10,80,156

...

799,799,799 (i = 512_000_000 - 1)

对于有 3 列的情况,我无法想出一个公式。有没有办法在一个循环中循环这样的数组?

【问题讨论】:

  • 我不明白您为什么要多次迭代相同的元素。只有 2*800 = 1,600 个元素,但您正在迭代 640,000 次。因此,您会多次访问每个元素。
  • 您需要第二个循环...因为您需要遍历列
  • @MatthewWatson 这是个好问题。我的最终目标是遍历所有可能的值组合。仅仅从数组中获取必要的元素是不够的。就像你说的那样——总共只有 1600 个元素,但是这 1600 个元素可以创建 640000 个独特的组合。
  • 啊,所以你想要的是每列中一个元素的每个组合。

标签: c# arrays multidimensional-array


【解决方案1】:

当然可以,试试这个 Java 代码:

int columnCount = 3;
        int eachColumnElementCount = 800;
        int totalCombinationCount = (int)Math.pow(eachColumnElementCount, columnCount); // 800*800*800

        for(int i = 0; i < totalCombinationCount ; i++) {
                int column1Index= i % eachColumnElementCount;
                int column2Index= i / eachColumnElementCount % eachColumnElementCount ;
                int column3Index= i / eachColumnElementCount / eachColumnElementCount ;
                System.out.println(column3Index+","+column2Index+","+column1Index);
        }

【讨论】:

  • 这就是我喜欢Java 的原因。语法与C# 非常相似!我正计划使用 CUDA 执行循环,所以我将稍微修改 C/C++ 的代码。您的解决方案效果很好!谢谢!
【解决方案2】:

我知道您问过如何对三列执行此操作,但如果您想将其概括为与 N 列一起使用,那就有点棘手了。

您计算的是所谓的Cartesian Product

埃里克·利珀特posted a Linq solution to this in his blog.

看起来像这样:

static IEnumerable<IEnumerable<T>> CartesianProduct<T>
    (this IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };

    return sequences.Aggregate(
        emptyProduct,
        (accumulator, sequence) =>
            from accseq in accumulator
            from item in sequence
            select accseq.Concat(new[]{item}));
}

(请阅读我在上面链接的 Eric Lippert 的博客文章,了解其工作原理的完整详细信息。)

为了在二维数组中使用这种方法,我们需要一种按列而不是按行迭代数组内容的方法:

public static IEnumerable<T> Column<T>(T[,] array, int column)
{
    for (int row = array.GetLowerBound(0); row <= array.GetUpperBound(0); ++row)
        yield return array[row, column];
}

public static IEnumerable<IEnumerable<T>> ByColumn<T>(T[,] array)
{
    for (int column = array.GetLowerBound(1); column <= array.GetUpperBound(1); ++column)
        yield return Column(array, column);
}

然后我们可以像这样求解一个 5x4 数组:

char[,] array =
{
    { 'A', 'F', 'K', 'P' },
    { 'B', 'G', 'L', 'Q' },
    { 'C', 'H', 'M', 'R' },
    { 'D', 'I', 'N', 'S' },
    { 'E', 'J', 'O', 'T' },
};

foreach (var combination in CartesianProduct(ByColumn(array)))
    Console.WriteLine(string.Concat(combination));

请注意,您不需要为不同数量的列编写不同的代码 - 这适用于任何数量大于一的列。

将所有内容放在一个简单的控制台应用程序中:

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            char[,] array =
            {
                { 'A', 'F', 'K', 'P' },
                { 'B', 'G', 'L', 'Q' },
                { 'C', 'H', 'M', 'R' },
                { 'D', 'I', 'N', 'S' },
                { 'E', 'J', 'O', 'T' },
            };

            foreach (var combination in CartesianProduct(ByColumn(array)))
                Console.WriteLine(string.Concat(combination));
        }

        public static IEnumerable<T> Column<T>(T[,] array, int column)
        {
            for (int row = array.GetLowerBound(0); row <= array.GetUpperBound(0); ++row)
                yield return array[row, column];
        }

        public static IEnumerable<IEnumerable<T>> ByColumn<T>(T[,] array)
        {
            for (int column = array.GetLowerBound(1); column <= array.GetUpperBound(1); ++column)
                yield return Column(array, column);
        }

        static IEnumerable<IEnumerable<T>> CartesianProduct<T>
            (this IEnumerable<IEnumerable<T>> sequences)
        {
            IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };

            return sequences.Aggregate(
                emptyProduct,
                (accumulator, sequence) =>
                    from accseq in accumulator
                    from item in sequence
                    select accseq.Concat(new[]{item}));
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 2018-07-30
    • 2013-06-25
    相关资源
    最近更新 更多