繁体   English   中英

n维数组

[英]n-dimensional Array

我想创建一个n维的双精度数组。 在编译时,维数n的数量是未知的。

我最后将数组定义为字典,键是一个对应于不同轴的int数组(所以在三维数组中,我提供[5,2,3]得到双精度(5 ,2,3)在数组中。

但是,我还需要使用从(0,0,... 0)到(m1,m2,... mn)的双精度填充字典,其中m1到mn是每个轴的长度。

我最初的想法是创建嵌套的for循环,但由于我仍然不知道我需要多少(每个维度1个),所以我无法在编译时执行此操作。

我希望我以一种可以理解的方式提出问题,但请随时让我详细说明部分内容。

要创建n维数组,可以使用Array.CreateInstance方法:

Array array = Array.CreateInstance(typeof(double), 5, 3, 2, 8, 7, 32));

array.SetValue(0.5d, 0, 0, 0, 0, 0, 0);
double val1 = (double)array.GetValue(0, 0, 0, 0, 0, 0);

array.SetValue(1.5d, 1, 2, 1, 6, 0, 30);
double val2 = (double)array.GetValue(1, 2, 1, 6, 0, 30);

要填充数组,可以使用Rank属性和GetLength方法返回当前维度的长度,使用几个嵌套for循环来执行O(n ^ m)算法(警告 - 未经测试):

private bool Increment(Array array, int[] idxs, int dim) {
    if (dim >= array.Rank) return false;

    if (++idxs[idxs.Length-dim-1] == array.GetLength(dim)) {
        idxs[idxs.Length-dim-1] = 0;
        return Increment(array, idxs, dim+1);
    }
    return true;
}

Array array = Array.CreateInstance(typeof(double), ...);
int[] idxs = new int[array.Rank];
while (Increment(array, idxs, 0)) {
    array.SetValue(1d, idxs);
}

快速跟进此事:

我们成功使用了Array.CreateInstance方法,但正如有人预测的那样,效率相当低,并且还会产生可读性问题。

相反,我们开发了一种方法,其中n维数组被转换为1维(正常)数组。

public static int NDToOneD(int[] indices, int[] lengths)
{
  int ID = 0;
  for (int i = 0; i < indices.Length; i++)
  {
    int offset = 1;
    for (int j = 0; j < i; j++)
{
      offset *= lengths[j];
}
    ID += indices[i] * offset;
  }
  return ID;
}

1DtoND(int[] indices, int[] arrayLengths)
{
  int[] indices = new int[lengths.Length];
  for (int i = lengths.Length - 1; i >= 0; i--)
  {
    int offset = 1;
    for (int j = 0; j < i; j++)
    {
      offset *= lengths[j];
    }
    int remainder = ID % offset;
    indices[i] = (ID - remainder) / offset;
    ID = remainder;
  }
  return indices;
}

这实质上是将笛卡尔坐标转换为单个整数并再次转换的概括。

我们的测试没有正式化,所以我们获得的任何加速都完全是轶事,但对于我的机器,它提供了大约30-50%的加速,具体取决于样本大小,并且代码的可读性大大提高了。

希望这可以帮助任何偶然发现这个问题的人。

你为什么不使用多维数组: double[,,] array = new double[a,b,c] 所有数组元素都会自动初始化为0.0。

或者,您可以使用锯齿状数组double[][][] ,但每个子数组都需要在for循环中初始化:

int a, b, c;
double[][][] array = new double[a][][];

for (int i=0; i<a; i++) {
    double[i] = new double[b][];

    for (int j=0; j<b; j++) {
        double[i][j] = new double[c];
    }
}

编辑:没有意识到维度的数量是运行时。 上面添加了另一个答案

使用此方法,您可以创建任何类型的n维锯齿状数组。

    public static Array CreateJaggedArray<T>(params int[] lengths)
    {
        if(lengths.Length < 1)
            throw new ArgumentOutOfRangeException(nameof(lengths));

        void Populate(Array array,  int index)
        {
            for (int i = 0; i < array.Length; i++)
            {
                Array element = (Array)Activator.CreateInstance(array.GetType().GetElementType(), lengths[index]);
                array.SetValue(element, i);
                if (index + 1 < lengths.Length)
                    Populate(element, index + 1);

            }
        }

        Type retType = typeof(T);
        for (var i = 0; i < lengths.Length; i++)
            retType = retType.MakeArrayType();

        Array ret = (Array)Activator.CreateInstance(retType, lengths[0]);
        if (lengths.Length > 1)
            Populate(ret, 1);
        return ret;
    }

暂无
暂无

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

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