【问题标题】:C# flattening/expanding a 3D matrix into a jagged arrayC# 将 3D 矩阵展平/扩展为锯齿状数组
【发布时间】:2019-10-21 19:08:24
【问题描述】:

我必须展平一个 3d 数组才能进行序列化。 让我们从这个开始:

int[,,] array3D = new int[,,] { 
            { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
            { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
            { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
        };

它是这样的(类似于 1,2,3,4,...,24):

所以现在我有这个 s/r

public static T[] Flatten<T>(T[,,] arr)
{
    int rows0 = arr.GetLength(0);
    int rows1 = arr.GetLength(1);
    int rows2 = arr.GetLength(2);
    T[] arrFlattened = new T[rows0 * rows1* rows2];

    int i, j, k;
    for (k = 0; k < rows2; k++)
    {
        for (j = 0; j < rows1; j++)
        {
            for (i = 0; i < rows0; i++)
            {
                var test = arr[i, j, k];
                int index = i + j * rows0 + k * rows1;
                arrFlattened[index] = test;
            }
        }
    }
    return arrFlattened;
}

将 3d 矩阵展平为 1d 数组

我不够聪明,无法理解程序是否正确,但让我们更进一步。 然后我用以下 s/r 展开

public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
{
    int length = arr.GetLength(0);
    int rows2 = length / rows0 / rows1;

    T[,,] arrExpanded = new T[rows0, rows1, rows2];
    for (int k = 0; k < rows2; k++)
    {
        for (int j = 0; j < rows1; j++)
        {
            for (int i = 0; i < rows0; i++)
            {
                T test = arr[i + j * rows0 + k * rows1];
                arrExpanded[i, j, k] = test;
            }
        }
    }
    return arrExpanded;
}

但结果如下:

所以没有什么比 1,2,3,4,5....24 我知道这个错误可能是一个微不足道的错误,但我可能找不到它。 提前致谢。

帕特里克


感谢您帮助所有 3 个解决方案都很棒且有效,但我选择了一个更容易理解和调试的解决方案

【问题讨论】:

  • 你的一维数组不是锯齿状的。交错数组是数组的数组。

标签: c# arrays multidimensional-array expand flatten


【解决方案1】:

我假设您想知道代码中的错误是什么,而不是想知道获得答案的最快方法。你的指数计算是错误的。你是这样计算的:

int index = i + j * rows0 + k * rows1;

但实际上,您不仅需要将最后一项乘以 rows1,还需要乘以 rows0:

int index = i + j * rows0 + k * rows1 * rows0;

此外,交换在 for 循环中迭代的维度顺序以按顺序获得结果是有意义的。最终的代码是:

    public static T[] Flatten<T>(T[,,] arr)
    {
        int rows0 = arr.GetLength(0);
        int rows1 = arr.GetLength(1);
        int rows2 = arr.GetLength(2);
        T[] arrFlattened = new T[rows0 * rows1* rows2];

        int i, j, k;
        for (k = 0; k < rows0; k++)
        {
            for (j = 0; j < rows1; j++)
            {
                for (i = 0; i < rows2; i++)
                {
                    var test = arr[k, j, i];
                    int index = i + j * rows2 + k * rows1 * rows2;
                    arrFlattened[index] = test;
                }
            }
        }
        return arrFlattened;
    }

    public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
    {
        int length = arr.GetLength(0);
        int rows2 = length / rows0 / rows1;

        T[,,] arrExpanded = new T[rows0, rows1, rows2];
        int i, j, k;
        for (k = 0; k < rows0; k++)
        {
            for (j = 0; j < rows1; j++)
            {
                for (i = 0; i < rows2; i++)
                {
                    T test = arr[i + j * rows2 + k * rows1 * rows2];
                    arrExpanded[k, j, i] = test;
                }
            }
        }
        return arrExpanded;
    }

【讨论】:

    【解决方案2】:

    你可以试一试:

    void Main()
    {
        int[,,] array3D = new int[,,]
        {
            { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
            { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
            { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
        };
    
        var flattened = array3D.Cast<int>().ToArray();
    
        var restored = Expand(flattened, 3, 4);
    }
    
    public static T[,,] Expand<T>(T[] arr, int rows0, int rows1)
    {
        int length = arr.GetLength(0);
        int rows2 = length / rows0 / rows1;
        int x = 0;
    
        T[,,] arrExpanded = new T[rows0, rows1, rows2];
        for (int i = 0; i < rows0; i++)
        {
            for (int j = 0; j < rows1; j++)
            {
                for (int k = 0; k < rows2; k++)
                {
                    T test = arr[x++];
                    arrExpanded[i, j, k] = test;
                }
            }
        }
        return arrExpanded;
    }
    

    对我来说效果很好。

    【讨论】:

      【解决方案3】:

      Linq OfType&lt;T&gt;() 的帮助下,很容易从任何多维数组中获得 int[]

      var result = source.OfType<int>().ToArray();
      

      演示:

      using System.Linq;
      
      ...
      
      int[,,] array3D = new int[,,] {
        { {  1,  2}, {  3,  4}, {  5,  6}, { 7,  8 } },
        { {  9, 10}, { 11, 12}, { 13, 14}, {15, 16 } },
        { { 17, 18}, { 19, 20}, { 21, 22}, {23, 24 } },
      };
      
      var result = array3D.OfType<int>().ToArray();
      
      Console.Write(string.Join(", ", result)); 
      

      结果:

      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
      

      我们可以使用模运算Expand数组返回:

      private static T[,,] Expand<T>(T[] value, int length1, int length2, int length3) {
        T[,,] result = new T[length1, length2, length3];
      
        for (int i = 0; i < value.Length; ++i) {
          int r = i / (length3 * length2);
          int c = i /  length3 % length2;
          int h = i %  length3;
      
          result[r, c, h] = value[i];
        }
      
        return result;
      }
      

      例如

      int[,,] back = Expand(result, 3, 4, 2);
      

      【讨论】:

      • 您的解决方案似乎很有希望。也就是说,我必须展平为一维数组才能对其进行序列化。您的展平是 int[][][] 而不是 int[]。您能否更改它并添加 Expand 方法?谢谢
      • @Patrick: 所以你既不想要 multidimension 也不想要 jagged 数组,而是好旧的int[]?然后给int[] 你想把Expand 重新变成3d 吗?如果是,让我提出一个问题:扩展时长度从何而来?假设您有int[16],是否应该将其扩展为int[4,2,2]int[2,4,2],甚至扩展为int[16,1,1]
      • 是的,很抱歉没有说清楚。是的,我想展平为 1d (例如 3x4x2 为 1D ),然后通过给出已知的原始尺寸(例如 3x4x2)来扩展。谢谢
      【解决方案4】:

      展平一个多维数组,只需使用Cast&lt;T&gt;().ToArray()

      var d3 = new int[,,]
      {
          { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
          { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
          { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
      };
      var d1 = d3.Cast<int>().ToArray();
      Console.WriteLine(string.Join(" ", d1));
      

      给予:

      1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
      

      扩展使用这个:

      static int[,,] Expand(int[] array, int size2, int size3)
      {
          var size = new[] { array.Length / size2 / size3, size2, size3 };
          var res = Array.CreateInstance(typeof(int), size);
          for (var i = 0; i < array.Length; i++)
              res.SetValue(array[i], GetMultidimensionalIndex(i, size));
      
          return (int[,,])res;
      }
      
      static int[] GetMultidimensionalIndex(int index, int[] size)
      {
          var factors = size.Select((item, i) => size.Skip(i).Aggregate((a, b) => a * b)).ToArray();
          var factorsHelper = factors.Zip(factors.Skip(1).Append(1), (Current, Next) => new { Current, Next }).ToArray();
          return factorsHelper.Select(item => index % item.Current / item.Next).ToArray();
      }
      

      用法:

      var d3 = new int[,,]
      {
          { { 1, 2 }, { 3, 4 }, {5,6 }, {7,8 } },
          { { 9, 10}, { 11, 12},{ 13,14} , {15,16 }},
          { { 17, 18}, { 19, 20},{ 21,22}, {23,24 } }
      };
      Console.WriteLine("Original:");
      Print3DArray(d3);
      
      var flat = d3.Cast<int>().ToArray();
      Console.WriteLine("Flat:");
      Console.WriteLine(string.Join(" ", flat));
      
      var expanded = Expand(flat, d3.GetLength(1), d3.GetLength(2));
      Console.WriteLine("Expanded:");
      Print3DArray(expanded);
      

      使用辅助方法:

      static void Print3DArray(int[,,] array)
      {
          Console.WriteLine("{");
          for (int i = 0; i < array.GetLength(0); i++)
          {
              Console.Write("    {");
              for (int j = 0; j < array.GetLength(1); j++)
              {
                  Console.Write(" {");
                  for (int k = 0; k < array.GetLength(2); k++)
                  {
                      Console.Write($" {array[i, j, k]}");
                  }
                  Console.Write(" }");
              }
              Console.WriteLine(" }");
          }
          Console.WriteLine("}");
      }
      

      给予:

      Original:
      {
          { { 1 2 } { 3 4 } { 5 6 } { 7 8 } }
          { { 9 10 } { 11 12 } { 13 14 } { 15 16 } }
          { { 17 18 } { 19 20 } { 21 22 } { 23 24 } }
      }
      Flat:
      1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
      Expanded:
      {
          { { 1 2 } { 3 4 } { 5 6 } { 7 8 } }
          { { 9 10 } { 11 12 } { 13 14 } { 15 16 } }
          { { 17 18 } { 19 20 } { 21 22 } { 23 24 } }
      }
      

      【讨论】:

      • 哇非常感谢!!和扩展方法?显然,您需要传递 2 个整数才能恢复 X、Y、Z(行、列、平面)的原始大小
      • 您的解决方案越来越好。但是第一个:我不需要 3 个尺寸而是 2 个,因为我可以从 1d 的长度除以其他 2 个尺寸得到第 3 个。第二是否有可能像 Expand(int[] flattend, int size2, int size3) 这样的扩展方法?提前致谢
      • @Patrick,我检查了除法的工作速度确实比GetLength 快。这很有趣。
      猜你喜欢
      • 1970-01-01
      • 2015-12-11
      • 2016-12-19
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      • 1970-01-01
      • 2016-05-08
      • 2011-09-13
      相关资源
      最近更新 更多