表示下/上三角矩阵的有效方法

时间:2011-10-30 15:13:44

标签: c++ c algorithm data-structures multidimensional-array

我正在使用C / C ++程序处理我的数据,这是2维的。这里我的值是按成对计算的,此处foo[i][j]foo[j][i]的值相同。

因此,如果我使用一个简单的二维数组来实现它,我的一半空间就会被浪费掉。那么表示这个下/上三角矩阵的最佳数据结构是什么。

此致

8 个答案:

答案 0 :(得分:13)

如果你有N个项目,那么没有主对角线的下三角阵列将具有(N-1)* N / 2个元素,或者具有主对角线的(N + 1)* N / 2个元素。没有主对角线,(I,J)(I,J∈0..N-1,I> J)⇒(I *(I - 1)/ 2 + J)。对于主对角线,(I,J∈0..N-1,I≥J)⇒((I + 1)* I / 2 + J)。

(是的,当你在一台2.5千兆字节的机器上分配4千兆字节时,削减一半会产生很大的不同。)

答案 1 :(得分:12)

真的,你最好只使用常规的二维矩阵。 RAM非常便宜。如果你真的不想这样做,那么你可以构建一个具有正确数量元素的一维数组,然后找出如何访问每个元素。例如,如果数组的结构如下:

    j
    1234
i 1 A
  2 BC
  3 DEF
  4 GHIJ

并且您将其存储为一维数组,从左到右,您可以使用C访问元素(2, 2) array[3]。你可以设计一个从[i][j][n]的功能,但我不会破坏你的乐趣。但你不必这样做,除非有问题的三角形阵列真的很大或者你非常关心空间。

答案 2 :(得分:3)

使用锯齿状阵列:

int N;
// populate N with size

int **Array = new Array[N];
for(int i = 0; i < N; i++)
{
    Array[i] = new Array[N - i];
}

它将创建类似

的数组
   0 1 2 3 4 5
0 [           ]
1 [         ]
2 [       ]
3 [     ]
4 [   ]
5 [ ]

答案 3 :(得分:2)

需要在n×n对称矩阵中表示唯一元素的数量m

使用主对角线

m = (n*(n + 1))/2

没有对角线(对于OP描述的对称矩阵,需要主对角线,但只是为了好的测量......)

m = (n*(n - 1))/2

如果使用截断的整数运算,则在最后一次操作之前不要除以2。

你还需要做一些算术来在对角矩阵中对应于行x和列y的分配存储器中找到索引i。

分配内存中的索引,i,上行对角矩阵中的行x和列y:

用对角线

i = (y*(2*n - y + 1))/2 + (x - y - 1)

没有对角线

i = (y*(2*n - y - 1))/2 + (x - y -1)

对于下方对角矩阵,在方程中翻转x和y。对于对称矩阵,只需在内部选择x> = y或y&gt; = x,并根据需要翻转成员函数。

答案 4 :(得分:2)

Dan和Praxeolitic提出了具有对角线但具有修正过渡规则的下三角矩阵。

对于矩阵n乘n,您需要数组(n+1)*n/2长度,转换规则为Matrix[i][j] = Array[i*(i+1)/2+j]

#include<iostream>
#include<cstring>

struct lowerMatrix {
  double* matArray;
  int sizeArray;
  int matDim;

  lowerMatrix(int matDim) {
    this->matDim = matDim;
    sizeArray = (matDim + 1)*matDim/2;
    matArray = new double[sizeArray];
    memset(matArray, .0, sizeArray*sizeof(double));
  };

  double &operator()(int i, int j) {
    int position = i*(i+1)/2+j;
    return matArray[position];
  };
};

我是使用double完成的,但您可以将其设为template。这只是基本的骨架,所以不要忘记实现析构函数。

答案 5 :(得分:1)

在Adrian McCarthy的回答中,替换

p += side - row;

p += row + 1;

用于下三角矩阵而不是上三角矩阵。

答案 6 :(得分:0)

Riffing Dani的答案......

您可以分配一个数组来保存数据,而不是分配许多不同大小的数组(可能导致内存碎片或奇怪的缓存访问模式),而是分配一个小数组来保存指向第一个分配中的行的指针。

const int side = ...;
T *backing_data = new T[side * (side + 1) / 2];  // watch for overflow
T **table = new T*[side];
auto p = backing_data;
for (int row = 0; row < side; ++row) {
   table[row] = p;
   p += side - row;
}

现在你可以使用table,就好像它是一个锯齿状的数组,如Dani的回答所示:

table[row][col] = foo;

但所有数据都在一个块中,否则可能不会取决于您的分配器策略。

使用行指针表可能比使用Praxeolitic公式计算偏移更快或更快。

答案 7 :(得分:0)

string map {"_clk " " "} "$scan_ports "