[英]efficient way to represent a lower/upper triangular matrix
我正在一個二維的 C/C++ 程序中處理我的數據。 這里我的值是成對計算的,這里的值對於foo[i][j]
和foo[j][i]
。
因此,如果我使用一個簡單的二維數組來實現它,我的一半空間將被浪費。 那么表示這個下/上三角矩陣的最佳數據結構是什么。
問候,
如果您有 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 GB 的機器上分配 4 GB 時,將其減半確實會產生巨大的不同。)
真的,最好只使用常規的二維矩陣。 RAM相當便宜。 如果您真的不想這樣做,那么您可以使用正確數量的元素構建一個一維數組,然后弄清楚如何訪問每個元素。 例如,如果數組的結構如下:
j
1234
i 1 A
2 BC
3 DEF
4 GHIJ
並且您將它存儲為一維數組,從左到右,您可以使用array[3]
訪問元素C
(2, 2)
。 您可以計算出從[i][j]
到[n]
的函數,但我不會破壞您的樂趣。 但是您不必這樣做,除非所討論的三角形陣列非常大或者您非常關心空間。
正如 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
。 這只是基本的骨架,所以不要忘記實現析構函數。
使用鋸齒狀數組:
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 [ ]
需要在 n × n 對稱矩陣中表示的唯一元素數 m:
隨着主對角線
m = (n*(n + 1))/2
沒有對角線(對於 OP 描述的對稱矩陣,需要主對角線,但只是為了更好的衡量......)
m = (n*(n - 1))/2
。
如果使用截斷的整數運算,直到最后一個操作才除以 2 很重要。
您還需要進行一些算術運算,以在對角矩陣中對應於行 x 和列 y 的已分配內存中找到索引 i。
上對角矩陣中第 x 行和第 y 列的已分配內存 i 中的索引:
隨着對角線
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>=x 並根據需要翻轉成員函數。
我正在一個二維的 C/C++ 程序中處理我的數據。 這里我的值是成對計算的,這里的值對於foo[i][j]
和foo[j][i]
。
因此,如果我使用一個簡單的二維數組來實現它,我的一半空間將被浪費。 因此,最好的數據結構來表示這個較低/較高的三角矩陣。
問候,
對 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 公式計算偏移更快。
#include <stdio.h>
// Large math problems running on massively parallel systems sometimes use a lot
// of upper triangular matrices. Implemented naively, these waste 50% of the memory
// in the machine, which is not recoverable by virtual memory techniques because it
// is interspersed with data on each row. By mapping the array elements into an
// array that is half the size and not actually storing the zeroes, we can do twice
// the computation in the same machine or use half as many machines in total.
// To implement a safety feature of making the zero-part of an upper triangular matrix
// read-only, we place all the zeroes in write-protected memory and cause a memory violation
// if the programmer attempts to write to them. System dependent but relatively portable.
// Requires that you compile with the -Wno-discarded-qualifiers option.
// for the awkward case (an even-sized array bound):
// +--------/
// row 0, 40 items -> |0 /
// row 1, 39 items -> | /
// row 19, 21 items -> | /
// row 20, 20 items -> |----/ <------ cut and rotate here to form a rectangle.
// row 21, 19 items -> | /
// | /
// row 39, 1 item -> | /
// row 40, 0 items -> |/
// /
// x y x y
// 0,0 39,0
// +----/ | +--------/
// | / | row 0, 40 items -> |0 /| <-- row 40, 0 items
// | / - 20,18 | row 1, 39 items -> | /0| <-- row 39, 1 item
// | /\ | row 19, 21 items -> | / | <-- row 21, 19 items
// |/ 19,19 | row 20, 20 items -> | /???| <-- row 20, 20 items half of row 20 is wasted...
//0,39 v | ~~~~~~~~~~
// | |
// for odd-sized array bounds, there is no need for the wasted half-row marked '???' above...
// And once the mapping above is done, mirror the indexes in x to get a proper Upper Triangular Matrix which
// looks like this...
// ____
// \ |
// \ |
// \|
//
// Rather than store the underlying data in a 2D array, if we use a 1-D array,
// and map the indexes ourselves, it is possible to recover that final half-row...
// The implementation allows for the matrix elements to be any scalar type.
#define DECLARE_TRIANGULAR_MATRIX(type, name, bound, zero) \
type _##name[bound * (bound+1) / 2 + 1]; /* +1 is for a debugging tombstone */ \
type *__##name(int x, int y) { \
static const type Zero = zero; /* writing to the lower half of the matrix will segfault */ \
x = (bound-1)-x; /* mirror */ \
if (x+y >= bound) return &Zero; /* requires cc -Wno-discarded-qualifiers */ \
if (y > bound/2) {x = (bound-1)-x; y = bound-y;} \
return &_##name[y*bound+x]; /* standard mapping of x,y -> X */ \
}
#define TRIANGULAR_MATRIX(name, x, y) *__##name(x,y)
// ----------------------------------------------------------------------------------------
// Simulate 'int fred[11][11];' as an upper triangular matrix:
#define ARRAYSIZE 11
DECLARE_TRIANGULAR_MATRIX(int, fred, ARRAYSIZE, 0)
#define fred(x, y) TRIANGULAR_MATRIX(fred, x, y)
/* unfortunately we can't #define fred[x][y] here ... In the Imp language which used () both
for array indexes and procedure parameters, we could write a mapping function fred(x,y)
which made the indirected function call indistinguishable from a normal array access.
We attempt to do something similar here using macros, but C is not as cooperative. */
int main(int argc, char **argv) {
int x,y, element;
// treat as fully populated 2D array...
for (y = 0; y < ARRAYSIZE; y++) {
for (x = 0; x < ARRAYSIZE; x++) {
if (ARRAYSIZE-x+y <= ARRAYSIZE) fred(x,y) = (x+1) * 100 + (y+1); // upper triangle test
}
}
fprintf(stdout, "Upper Triangular matrix:\n\n");
fprintf(stdout, " ");
for (x = 0; x < ARRAYSIZE; x++) fprintf(stdout, "%5d", x);
fprintf(stdout, "\n ");
for (x = 0; x < ARRAYSIZE; x++) fprintf(stdout, "_____");
fprintf(stdout, "\n");
for (y = 0; y < ARRAYSIZE; y++) {
fprintf(stdout, "%2d |", y);
for (x = 0; x < ARRAYSIZE; x++) {
element = fred(x,y);
fprintf(stdout, "%5d", element);
if (ARRAYSIZE-x+y <= ARRAYSIZE) { // upper triangle test
if (element != (x+1) * 100 + (y+1)) {
fflush(stdout); fprintf(stderr, "Mismatch! at %d,%d (%d != %d)\n", x, y, element, x * 100 + y);
}
} else if (element != 0) {
fflush(stdout); fprintf(stderr, "Mismatch! at %d,%d (%d != 0)\n", x, y, element);
}
}
fprintf(stdout, "\n");
}
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.