簡體   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