#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
typedef struct _triplet TRIPLET;
struct _triplet
{
int i;
int j;
double value;
};
typedef struct _s S;
struct _s
{
int nRow;
int nCol;
int nz;
TRIPLET* ts;
};
S* sCreate(int nRow, int nCol, int nz)
{
S* s = (S*)malloc(sizeof(S));
s->nRow = nRow;
s->nCol = nCol;
s->nz = nz;
s->ts = (TRIPLET*)malloc(nz * sizeof(TRIPLET));
for(int i = 0; i < nz; i++)
{
s->ts[i].i = 0;
s->ts[i].j = 0;
s->ts[i].value = 0;
}
return s;
}
void sFree(S* s)
{
free(s->ts);
free(s);
}
int jacobi_sparse(S* A, double* b, double* x, int max_iter, double tolerance)
{
int n = A->nRow;
double* x_new = (double*)malloc(n * sizeof(double));
double* diag = (double*)malloc(n * sizeof(double));
for(int i = 0; i < n; i++) {
diag[i] = 1.0;
}
for(int k = 0; k < A->nz; k++) {
if(A->ts[k].i == A->ts[k].j) {
diag[A->ts[k].i] = A->ts[k].value;
}
}
int iter;
for(iter = 0; iter < max_iter; iter++) {
for(int i = 0; i < n; i++) {
x_new[i] = b[i];
}
for(int k = 0; k < A->nz; k++) {
int i = A->ts[k].i;
int j = A->ts[k].j;
if(i != j) {
x_new[i] -= A->ts[k].value * x[j];
}
}
for(int i = 0; i < n; i++) {
x_new[i] /= diag[i];
}
double max_error = 0.0;
for(int i = 0; i < n; i++) {
double error = fabs(x_new[i] - x[i]);
if(error > max_error) {
max_error = error;
}
}
for(int i = 0; i < n; i++) {
x[i] = x_new[i];
}
if(max_error < tolerance) {
break;
}
}
free(x_new);
free(diag);
return iter;
}
S* generate_diagonally_dominant_sparse_matrix(int n, double density)
{
int nz = (int)(n * n * density);
if(nz < 3 * n) nz = 3 * n;
S* A = sCreate(n, n, nz);
int count = 0;
srand(time(NULL));
for(int i = 0; i < n; i++) {
A->ts[count].i = i;
A->ts[count].j = i;
A->ts[count].value = (n + 1) + (rand() % 100) * 0.1;
count++;
}
while(count < nz) {
int i = rand() % n;
int j = rand() % n;
if(i != j) {
int exists = 0;
for(int k = 0; k < count; k++) {
if(A->ts[k].i == i && A->ts[k].j == j) {
exists = 1;
break;
}
}
if(!exists) {
A->ts[count].i = i;
A->ts[count].j = j;
A->ts[count].value = (rand() % 100 - 50) * 0.01;
count++;
}
}
}
return A;
}
double* generate_test_vector(int n)
{
double* b = (double*)malloc(n * sizeof(double));
srand(time(NULL) + 1);
for(int i = 0; i < n; i++) {
b[i] = (rand() % 100) * 0.1;
}
return b;
}
{
int n = A->nRow;
double* Ax = (double*)calloc(n, sizeof(double));
int i = A->ts[k].i;
int j = A->ts[k].j;
Ax[i] += A->ts[k].value * x[j];
}
double residual = 0.0;
for(int i = 0; i < n; i++) {
residual += (Ax[i] - b[i]) * (Ax[i] - b[i]);
}
free(Ax);
return sqrt(residual);
}
void gaussian_elimination_dense(double** A, double* b, double* x, int n)
{
double** Ab = (double**)malloc(n * sizeof(double*));
for(int i = 0; i < n; i++) {
Ab[i] = (double*)malloc((n + 1) * sizeof(double));
for(int j = 0; j < n; j++) {
Ab[i][j] = A[i][j];
}
Ab[i][n] = b[i];
}
for(int k = 0; k < n; k++) {
int max_row = k;
for(int i = k + 1; i < n; i++) {
if(fabs(Ab[i][k]) > fabs(Ab[max_row][k])) {
max_row = i;
}
}
if(max_row != k) {
double* temp = Ab[k];
Ab[k] = Ab[max_row];
Ab[max_row] = temp;
}
for(int i = k + 1; i < n; i++) {
double factor = Ab[i][k] / Ab[k][k];
for(int j = k; j <= n; j++) {
Ab[i][j] -= factor * Ab[k][j];
}
}
}
for(int i = n - 1; i >= 0; i--) {
x[i] = Ab[i][n];
for(int j = i + 1; j < n; j++) {
x[i] -= Ab[i][j] * x[j];
}
x[i] /= Ab[i][i];
}
for(int i = 0; i < n; i++) {
free(Ab[i]);
}
free(Ab);
}
double** sparse_to_dense(S* A)
{
int n = A->nRow;
double** dense = (double**)malloc(n * sizeof(double*));
for(int i = 0; i < n; i++) {
dense[i] = (double*)calloc(n, sizeof(double));
}
for(int k = 0; k < A->nz; k++) {
int i = A->ts[k].i;
int j = A->ts[k].j;
dense[i][j] = A->ts[k].value;
}
return dense;
}
void free_dense_matrix(double** A, int n)
{
for(int i = 0; i < n; i++) {
free(A[i]);
}
free(A);
}
int main()
{
const int n = 1000;
const double density = 0.01;
const int max_iter = 1000;
const double tolerance = 1e-6;
printf("=== 1000阶稀疏线性方程组求解对比 ===\n");
printf("生成严格对角占优的稀疏矩阵...\n");
S* A = generate_diagonally_dominant_sparse_matrix(n, density);
double* b = generate_test_vector(n);
double* x_jacobi = (double*)calloc(n, sizeof(double));
double* x_gauss = (double*)calloc(n, sizeof(double));
printf("矩阵规模: %d x %d\n", n, n);
printf("非零元素: %d (密度: %.3f%%)\n", A->nz, (double)A->nz/(n*n)*100);
printf("\n--- 雅克比迭代法 ---\n");
clock_t start = clock();
int iterations = jacobi_sparse(A, b, x_jacobi, max_iter, tolerance);
clock_t end = clock();
double time_jacobi = ((double)(end - start)) / CLOCKS_PER_SEC;
double residual_jacobi = compute_residual(A, x_jacobi, b);
printf("迭代次数: %d\n", iterations);
printf("计算时间: %.4f 秒\n", time_jacobi);
printf("残差范数: %.6e\n", residual_jacobi);
printf("\n--- 高斯消去法 ---\n");
start = clock();
double** A_dense = sparse_to_dense(A);
gaussian_elimination_dense(A_dense, b, x_gauss, n);
end = clock();
double time_gauss = ((double)(end - start)) / CLOCKS_PER_SEC;
double residual_gauss = compute_residual(A, x_gauss, b);
printf("计算时间: %.4f 秒\n", time_gauss);
printf("残差范数: %.6e\n", residual_gauss);
printf("\n=== 对比分析 ===\n");
printf("时间比 (高斯/雅克比): %.2f\n", time_gauss / time_jacobi);
printf("残差比 (雅克比/高斯): %.2e\n", residual_jacobi / residual_gauss);
double max_diff = 0.0;
for(int i = 0; i < n; i++) {
double diff = fabs(x_jacobi[i] - x_gauss[i]);
if(diff > max_diff) {
max_diff = diff;
}
}
printf("解的最大差异: %.6e\n", max_diff);
size_t sparse_memory = sizeof(S) + A->nz * sizeof(TRIPLET);
size_t dense_memory = n * n * sizeof(double);
printf("\n内存使用对比:\n");
printf("稀疏矩阵存储: %.2f MB\n", sparse_memory / (1024.0 * 1024.0));
printf("稠密矩阵存储: %.2f MB\n", dense_memory / (1024.0 * 1024.0));
printf("存储比 (稠密/稀疏): %.1f\n", (double)dense_memory / sparse_memory);
sFree(A);
free(b);
free(x_jacobi);
free(x_gauss);
free_dense_matrix(A_dense, n);
return 0;
}
精度
- 高斯消去法:由于是直接法,通常能达到机器精度(~1e-15)
- 雅克比迭代法:达到预设容差1e-6,精度较低但满足大多数工程需求
效率
- 对于严格对角占优矩阵,雅克比迭代收敛较快
- 高斯消去法的时间复杂度为O(n³),对于n=1000需要较长时间
- 雅克比迭代每次迭代时间复杂度为O(nnz),总体效率更高