运用雅可比和高斯赛德尔迭代公式求解方程组,并尝试将矩阵变为主对角占优矩阵

程序描述

首先要求用户输入矩阵的大小n(默认不超过10),然后再提示用户输入大小为n的方阵。因为输入的方阵可能含有较多的0元素,因此用了数据结构上的矩阵的压缩方法来存储稀疏矩阵。矩阵的每一个非零元用一个结构体对象来存储,里面存储着这个非零元的值,下一个非零元的地址,此非零元所在的列y。我定义的结构体中没有存储非零元所在的行号,这是因为我用一个指针数组来指向矩阵的每一行,因此,矩阵的行号信息存储在头指针中,之所以给每一行都定义一个头指针,是因为后面还要对矩阵进行行变换,这样会方便行互换操作的进行,到时候只需交换头指针就可以实现行互换的功能。同时,我还在程序中定义了一个二维数组用来存储输入的矩阵,定义这个二维数组的目的完全是为了输出矩阵的时候方便(因为要把变换之后的矩阵输出),真正的迭代过程是用稀疏矩阵来实现的。用户输入矩阵之后,程序自动建立矩阵的链表存储,判断矩阵主对角线上是否有非零元,若有,则尝试转换(其实此转换功能没有实际用处,因为后面还要进行主对角占优的转化,如果一个矩阵是主对角占优的,那么它的主对角线一定不存在零元。),再判断是否为主对角占优,若否,则尝试转换,任一转换过程失败,则程序结束执行。若前面的转换都成功了或不需要进行转换,则要求用户输入列向量b,初始迭代值,迭代精度(用范式来确定),迭代上限。之后分别计算雅可比迭代和高斯赛德尔迭代,迭代过程相对来说比较简单,关键点是用精度来作为程序执行结束的条件。

思路讲解

将主对角线调整为不含零元的形式。
比如说下面的这个四阶矩阵:
0 1 1 0
1 0 0 1
0 1 0 1
1 1 0 0
当我们输入这个矩阵的时候,用一个二维数组做一些标记,对于此矩阵来说,二维数组mov_pos的内容为
2 3 0 0
1 4 0 0
2 4 0 0
1 2 0 0
这个二维数组存储的内容实际上每行可以移动的位置,比如说矩阵的第一行“0 * * 0”,它只能移动到第二行和第三行,因为如果它移动到第一行或者第四行,对角线一定存在0元素。现在我们得到了四组数据,也就是二维数组mov_pos的四行数据(忽略0元素),从这四组数据中,每一组数据选一个数,如果能有这么一种选法,使得选出的这四个数互不相同(注意,不要选0,因为0没有实际意义),那么这个矩阵就可以调整成主对角线不含零元素的形式。至于应该怎样选,我用的是数据结构上老鼠走迷宫的思想,先从第一组数据里选它的第一个数,选出来的是2,再从第二组数据里选第一个数,这时比较选出的数中有没有重复的,因为此时选出的数为{2,1},所以无重复,那么选第三组数据的第一个数2,此时选出的数为{2,1,2},我们发现有重复了,那么我们重新选第三行的数据,既然第三行的第一个数据不行,那么我们选第二个数据4,此时选出的数据是{2,1,4},发现无重复,那么继续进行,选第四行的第一个数1,发现有重复,重新选择第四行的第二个数据2,发现还是有重复,这时第四行的所有数据都已经被我们选了一遍了,那么这时就需要退回第三行。(其实在选数的时候,还用到了一个一位数组,这个数组标记第i行数据选到第a个数了,因此next[i]=a。)到了第三行发现此行的所有元素也都选过了,因此就退回到第二行,发现第二行的第二个元素没有选过,那么此时选择的数据列为{2,4},然后再去第三行选择数据,依次类推。
调整为主对角占优的方法。
这个相对来说简单一点,在输入矩阵的时候,就标记下每行最大元所在的位置j,之后判断能不能把此行移动第j行,然后再判断主对角线元素的绝对值是否大于此行其它元素绝对值之和就可以了。

代码实现

如果代码中bug,欢迎各位批评指正



#include "pch.h"	//非VS2017编译器把此行注释掉
#include "stdio.h"
#include "malloc.h"
#include "math.h"

//比较一个数和其余的数是否相同,若相同返回flase
bool not_compare(int * array, int value, int n) {
	if (value == 0)
		return false;
	for (int i = 1; i < n; i++) 
		if (value == array[i])
			return false;
	
	return true;
}
//求范数
double paradigm(double * x1, double * x2, int n) {
	double result = 0;
	for (int i = 1; i <= n; i++) 
		result = result + fabs(x1[i] - x2[i]);
	
	return result;
}
struct matrix {		//压缩矩阵,用链表存储
	double value;	//非零元素的值
	int y;			//所在的列号
	matrix * next;
};

int main() {
		double initial_matrix[10][10] = { 0 };	//压缩之前的矩阵
		int move_pos[10][10] = { 0 };	//矩阵的当前行可以移动的位置
		int array[10] = { 0 };	//存放排序之后每行矩阵的位置
		int max_position[10];	//每行最大数所在的列号
		int max_num;	//中间变量,记录每行的最大值
		double x0[10];	//雅可比迭代
		double x1[10];	//雅可比迭代
		double x[10];	//高斯赛德尔迭代结果
		double b[10];	//行矩阵b
		int n;	//矩阵的大小
		int count;	
		double precision;	//迭代精度
		double a[10];	//存放主对角线的元素
		int N;	//迭代次数的上限
		int flag1 = 0;	//标记主对角线存在非零元
		int flag2 = 0;	//调整矩阵时用到
		int flag3 = 0;	//记录精度是否达到要求,达标则flag3为0
		int flag4 = 0;	//标记是否进行了对角占优化
		matrix  head[10];	//每行矩阵都有一个头结点
		matrix * hp;

		printf("请输入矩阵的大小n:");
		scanf("%d", &n);
		printf("请输入%d阶矩阵\n", n);
		for (int i = 1; i <= n; i++) {
			hp = & head[i];
			count = 1;
			max_num = 0;
			for (int j = 1; j <= n; j++) {
				scanf("%lf", &initial_matrix[i][j]);
				if (initial_matrix[i][j] != 0) {
					matrix * p = (matrix *)malloc(sizeof(matrix));
					hp->next = p;
					hp = p;
					p->next = NULL;
					p->y = j;
					p->value = initial_matrix[i][j];
					move_pos[i][count++] = j;	//记录当前行可以移动到第j行上去
					//判断当前输入是否为本行最大值
					if (fabs(initial_matrix[i][j]) > max_num) {
						max_num = fabs(initial_matrix[i][j]);
						max_position[i] = j;
					}
				}
			}
			if (initial_matrix[i][i] == 0)
				flag1 = 1;	//标记主对角线存在非零元
		}
		//进行矩阵的变换,通过行互换实现
		if (flag1 == 1) {
			printf("此矩阵主对角线存在非0元,正在尝试转换...\n");
			bool cannot_dia = false;	//标记是否可以变为主对角元全非零的形式
			int next[10] = { 0 };	//用next[j]记录第j行比较到哪一个元素
			
			for (int i = 1; (i <= n) || (flag2 == 1); i++) {
				if (flag2 == 1) {
					i = i - 2;
					if (i == 0) {
						cannot_dia = true;
						break;
					}	//if
					next[i]++;
					flag2 = 0;	
				}	//if
				for (int j = 1;; j++) {
					if (not_compare(array, move_pos[i][next[i] + 1], i)) {
						array[i] = move_pos[i][next[i] + 1];
						break;
					}
					else 
						next[i]++;
					if (move_pos[i][next[i] + 1] == 0) {
						flag2 = 1;	//此行所有元素都与前面所选的重复,因此只能退回到前一行,重新选择
						next[i] = 0;
						break;
					}

				}	//for
			}	//for
			if (!cannot_dia) {
				int temp_array[10];
				for (int i = 1; i <= n; i++)
					temp_array[i] = array[i];

				for (int i = 1; i <= n; i++) {
					array[temp_array[i]] = i;
				}
					

				printf("变换成功,变换之后的矩阵为:\n");
				for (int i = 1; i <= n; i++) {
					for (int j = 1; j <= n; j++)
						printf("%.0f ", initial_matrix[array[i]][j]);
					printf("\n");	//每输出n个元素换行
				}
			}	//if (!cannot_dia)
			else
			{
				printf("转换失败");
				return 0;
			} 
	
		}	//if (flag1 == 1)

		//判断是否为对角占优
		for (int i = 1; i <= n; i++)
			for (int j = i + 1; j <= n;j++)
			if (max_position[i] == max_position[j]) {	//移动位置发生冲突,第i行和第j行想要移动到相同的位置
				printf("无法使其变为对角占优矩阵\n");
				return 0;
			}

		for (int i = 1; i <= n; i++) {
			double result = 0;
			for (int j = 1; j <= n; j++)
				if (max_position[i] != j)
					result = result + fabs(initial_matrix[i][j]);
			if (result >= fabs(initial_matrix[i][max_position[i]])) {	//不满足绝对值最大的元素大于其他元素绝对值之和这一条件
				printf("无法使其变为对角占优矩阵\n");
				return 0;
			}
		}

			for (int i = 1; i <= n; i++) {
				if (max_position[i] != i) {		//绝对值最大的元素没有在主对角线上
					flag4 = 1;

					int temp_max[10];
					for (int i = 1; i <= n; i++)
						temp_max[i] = max_position[i];

					for (int i = 1; i <= n; i++) {
						max_position[temp_max[i]] = i;
					}
						

					printf("现在输出调整之后的对角占优矩阵\n");
					for (int i = 1; i <= n; i++) {
						for (int j = 1; j <= n; j++)
							printf("%.0lf ", initial_matrix[max_position[i]][j]);
						printf("\n");
					}
					break;
				}//if (max_position[i] != i)
			}
		
		printf("请输入b\n");
		for(int i = 1; i <= n;i++)
			scanf("%lf",&b[i]);
		printf("请输入初始的迭代值\n");
		for (int i = 1; i <= n; i++) {
			scanf("%lf", &x0[i]);
			x[i] = x0[i];
		}
		printf("请输入迭代精度:");
		scanf("%lf", &precision);
		printf("请输入迭代上限:");
		scanf("%d", &N);
		
		if (flag4 == 1) {
			//对压缩矩阵进行变换↓
			matrix * temp[10];
			for (int i = 1; i <= n; i++) {
				temp[i] = head[i].next;	//保存每个头结点所指向的结点位置
			}
			for (int i = 1; i <= n; i++) {
				head[i].next = temp[max_position[i]];
			}
			//对压缩矩阵进行变换↑

			//对数组b进行变换,因为前面对矩阵进行了调整↓
			double b_temp[10];
			for (int i = 1; i <= n; i++) {
				b_temp[i] = b[i];
			}
			for (int i = 1; i <= n; i++) {
				b[i] = b_temp[max_position[i]];
			}
			//对数组b行变换,因为前面对矩阵进行了调整↑
			
		}

		//寻找主对角线的元素,并存储到数组a
		for (int i = 1; i <= n; i++) {
			hp = head[i].next;
			while (hp) {
				if (hp->y == i) {
					a[i] = hp->value;
					break;
				}
				hp = hp->next;
			}
		}

		//雅可比迭代
		for (int j = 1; j <= N; j++) {
			for (int i = 1; i <= n; i++) {
				hp = head[i].next;
				double sum = 0;		//xj * aij的累加和 
				while (hp) {
					if (hp->y == i) {
						hp = hp->next;
						continue;
					}
					sum = sum + hp->value * x0[hp->y];
					hp = hp->next;
				}
				x1[i] = 1.0 / a[i] * (b[i] - sum);
			}
			if(paradigm(x1, x0, n) < precision) {
				printf("\n");
				printf("雅可比迭代次数为%d次\n",j);
				printf("迭代结果为:\n");
				for (int i = 1; i <= n; i++) {
					printf("%f ", x1[i]);
				}
				printf("\n");
				break;
			}
			else 
				for (int i = 1; i <= n; i++) {
					x0[i] = x1[i];
				}
			
			if (j == N)
				printf("雅可比迭代已达到迭代上限,迭代失败。\n");
		}
		//高斯赛德尔迭代
		for (int j = 1; j <= N; j++) {
			for (int i = 1; i <= n; i++) {
				hp = head[i].next;
				double sum = 0;
				while (hp) {
					if (hp->y == i) {
						hp = hp->next;
						continue;
					}
					sum = sum + hp->value * x[hp->y];
					hp = hp->next;
				}
				if (fabs(1.0 / a[i] * (b[i] - sum) - x[i]) > precision)
					flag3 = 1;
				x[i] = 1.0 / a[i] * (b[i] - sum);
			}
			
			if (flag3 == 0) {
				printf("\n");
				printf("高斯赛德尔迭代次数为%d次\n",j);
				printf("迭代结果为:\n");
				for (int i = 1; i <= n; i++) 
					printf("%f ", x[i]);
				printf("\n");
				break;
			}
			else {
				flag3 = 0;
			}
			if (j == N)
				printf("高斯赛德尔迭代已达到迭代上限,迭代失败。\n");
		}
	} 

你可能感兴趣的:(数值分析)