我正在学习Horowitz的“ C中的数据结构基础”中的稀疏矩阵。 我的问题是关于稀疏矩阵乘法!我确实知道它的工作原理和算法,但是我不懂代码。
下面是有关“多人”的代码
这就是所谓的“边界条件”的一部分,这使我对此代码感到困惑。我不明白为什么需要这种情况。没有这些条款就不是很好吗?我需要一些帮助来了解这一部分...
这本书说:“ ...这些虚拟术语充当哨兵,使我们能够获得优美的算法。”
typedef struct {
int row;
int col;
int value;
} SM; // type SM is "Sparse Matrix"
void mmult(SM* A, SM* B, SM*C) {
int i, j;
int rowsA, colsB, totalA, totalB, totalC;
int rowbegin, A_Row, B_Col, sum;
SM* newB;
rowsA = A[0].row, colsB = B[0].col;
totalA = A[0].value, totalB = B[0].value;
totalC = 0;
if (A[0].col != B[0].row) {
fprintf(stderr, "can't multiply\n");
}
transpose(B, newB) // newB is a transposed matrix from B
/* set boundary condition */
A[totalA+1].row = rowsA;
newB[totalB+1].row = colsB;
newB[totalB+1].col = -1;
rowbegin = 1;
for (i = 1, A_Row = A[1].row, sum = 0; i <= totalA;) {
B_Col = newB[0].row;
for (j = 1; j <= totalB + 1) { // don't know why it should be iterated by totalB+1
/* current multiplying row != A[i].row */
if (A[i].row != A_Row) {
storesum(C, A_Row, B_Col, &totalC, &sum);
for(;newB[j].row == B_Col;j++);
i = rowbegin; // reset i to rowbegin, which is the first row term of current multiplying row;
}
/* current multiplying column != newB[j].col */
else if (newB[j].row != B_Col) {
storesum(C, A_Row, B_Col, &totalC, &sum);
B_Col = newB[j].row;
i = rowbegin;
}
/* Otherwise, during multiplication.. */
else {
switch(compare(A[i].col, newB[j].row)) {
case -1 :
i++;
break;
case 0 :
sum += (A[i].value * newB[j].value);
i++, j++;
break;
case 1 : j++;
}
}
}
for(;A[i].row == A_Row;) i++;
A_Row = row[i].row;
rowbegin = i;
}
}
void storesum(SM* C, int row, int col, int* totalC, int* sum) {
/* storesum is to store to C and set sum to 0 when multiplying current row or column is over */
if(*sum) {
(*totalC)++;
C[totalC].row = row;
C[totalC].col = col;
C[totalC].value = *sum;
*sum = 0;
}
}
答案 0 :(得分:0)
It is the part about so called "boundary condition" that makes me confused with this code. I don't understand why this condition is needed. Isn't it just fine without these terms??
The matrix multiplication could be computed without including the extra entry, yes, but the function given would not do it correctly, no.
The book says "...these dummy terms serve as sentinels that enable us to obtain an elegant algorithm.."
That's a little vague, I agree. Consider how the algorithm works: for each row of matrix A, it must scan each column of matrix B (== row of matrix newB) to compute one element of the product matrix. But the chosen sparse-matrix representation does not record how many elements there are in each row or column, so the only way to know when you've processed the last element for a given column is to look at the next one in linear element order, and see that it belongs to a new column.
The given code integrates the check for end of column and the storage of the resulting element into the processing for the next element, but that leaves a problem: what do you do about the last element in the matrix's element list? It has no following element with to trigger recording of an element of the result matrix -- at least, not a natural one. That could be solved with some special-case logic, but it is tidier to just add a synthetic extra element that definitely belongs to a different column, so that the end of the matrix no longer constitutes a special case.
I'm not sure I agree that the term "sentinel" is a good fit for this. It's just the opposite of a sentinel in many ways, as a matter of fact. The term normally means a special value that cannot be a part of ordinary data, and therefore can be recognized as an end-of-data marker. String terminators are an example. This "sentinel", on the other hand, works by mimicing real data. It is, nevertheless, an extra, artificial element at the end of the list, and in this sense it's not crazy to call it a sentinel.