资源描述
高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求 出可逆方阵的逆矩阵。高斯消元法的原理是:若用初等行变换将增广矩阵 化为,则AX = B与CX = D是同解方程组。所以我们可以用初等行变换把增广矩阵转换为行阶梯阵,然后回代求出方程的解。以上是线性代数课的回顾,下面来说说高斯消元法在编程中的应用。首先,先介绍程序中高斯消元法的步骤:(我们设方程组中方程的个数为equ,变元的个数为var,注意:一般情况下是n个方程,n个变 元,但是有些题目就故意让方程数与变元数不同)1. 把方程组转换成增广矩阵。2. 利用初等行变换来把增广矩阵转换成行阶梯阵。枚举k从0到equ - 1,当前处理的列为col(初始为0),每次找第k行以下(包括第k行),col列中 元素绝对值最大的列与第k行交换。如果col列中的元素全为0,那么则处理col + 1列,k不变。3. 转换为行阶梯阵,判断解的情况。 无解当方程中出现(0, 0,,0, a)的形式,且a != 0时,说明是无解的。 唯一解条件是k = equ,即行阶梯阵形成了严格的上三角阵。利用回代逐一求出解集。 无穷解。条件是k k)。显然如果akk特别小的话,不仅有可能损失精度,还有可能 造成出。为了避免这种情况,我们需要选取主元:在系数矩阵右下角选取n-k+1阶子矩阵中绝对值最大的元素a”,交换第行与第i行,交换第 k列与殉列。注意到列的交换会使得解的顺序混乱,因此我们需要记录列的交换过程,以便 最后还原。当然由于交换后akk的绝对值是最大的,所以如果akk=我们就可以直接终止 计算了,因为这时无法找到唯一的一组解。(选取主元的过程并不影响时间复杂度,整个过 程的时间复杂度仍然是O(n3)高斯约当消元法高斯约当消元法是一种不需要回代的消元法,可以算是高斯消元法的一种改进,它与高斯消 元法唯一的不同是:高斯约当消元法每次不仅要把主对角线以下的元素消为0,还要把主对角 线以上的元素消为0。自然消完后系数矩阵只有主对角线上有非0元素,所以就不需要回代了。其他解法除了高斯消元法与高斯约当消元法外,我们还可以使用迭代法或者共轭梯度法来解线性方程 组。练习题1. zoj2296 Spread Sheet2. hdu2449 Gauss Elimination3. NOIP2004 虫食算参考资料1 徐士良数值分析与算法机械工业出版社2 何江舟用高斯消元法解线性方程组下面讲解几道OJ上的高斯消元法求解线性方程组的题目:POJ 1222 EXTENDED LIGHTS OUTPOJ 1681 Painters ProblemPOJ 1753 Flip GamePOJ 1830开关问题POJ 3185 The Water Bowls开关窗户,开关灯问题,很典型的求解线性方程组的问题。方程数和变量数均为行数*列数,直 接套模板求解即可。但是,当出现无穷解时,需要枚举解的情况,因为无法判断哪种解是题目要 求最优的。POJ 2947 Widget Factory求解同余方程组问题。与一般求解线性方程组的问题类似,只要在求解过程中加入取余即可。 注意:当方程组唯一解时,求解过程中要保证解在3, 9之间。POJ 1166 The Clocks经典的BFS问题,有各种解法,也可以用逆矩阵进行矩阵相乘。但是这道题用高斯消元法解决好像有些问题(困扰了我N天持续困扰中),由于周期4不是素 数,故在求解过程中不能进行取余(因为取余可能导致解集变大),但最后求解集时,还是需要进 行取余操作,那么就不能保证最后求出的解是正确的在discuss里提问了好几天也没人回答. 希望哪位路过的大牛指点下POJ 2065 SETI同样是求解同余方程组问题,由于题目中的p是素数,可以直接在求解时取余,套用模板求解即 可。(虽然AC的人很少,但它还是比较水的一道题,)POJ 1487 Single-Player Games很麻烦的一道题目题目中的叙述貌似用到了编译原理中的词法定义(看了就给人不想做的感 觉.)解方程组的思想还是很好看出来了 (前提是通读题目不下5遍.),但如果把树的字符串表达式转换 成方程组是个难点,我是用栈+递归的做法分解的。首先用栈的思想求出该结点的孩子数,然 后递归分别求解各个孩子。这题解方程组也与众不同首先是求解浮点数方程组,要注意精度问题,然后又询问不确定的变 元,按前面说的方法求解。一顿折腾后,这题居然写了6000+B.而且冏的是巨人C+ WA,G+ AC,可能还是精度的问题 吧看这题目,看这代码,就没有改的欲望hdu OJ 2449哈尔滨现场赛的一道纯高斯题,当时鹤牛敲了 1个多小时主要就是写一个分数类,套个高精模 板(偷懒点就Java.)搞定注意下0和负数时的输出即可。fze OJ 1704福大月赛的一道题目,还是经典的开关问题,但是方程数和变元数不同(考验模板的时候到了),最后要求增广阵的阶,要用到高精度这里提供下自己写的还算满意的求解整数线性方程组的模板(浮点数类似,就不提供了)/*用于求整数解得方程组.*/#include #include #include using namespace std;const int maxn = 105;int equ, var; /有equ个方程,var个变元。增广阵行数为equ,分别为0到equ - 1,列数为var + 1, 分别为0到var.int amaxnmaxn;int xmaxn; / 解集.bool free_xmaxn; /判断是否是不确定的变元.int free_num;void Debug(void)(int i, j;for (i = 0; i equ; i+)(for (j = 0; j var + 1; j+)(cout aij ;cout endl;cout endl;inline int gcd(int a, int b)(int t;while (b != 0)(t = b;b = a % b;a = t;return a;inline int lcm(int a, int b)(return a * b / gcd(a, b);/高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)int Gauss(void)(int i, j, k;int max_r; /当前这列绝对值最大的行.int col; /当前处理的列.int ta, tb;int LCM;int temp;int free_x_num;int free_index;/转换为阶梯阵.col = 0; /当前处理的列.for (k = 0; k equ & col var; k+, col+)( /枚举当前处理的行./找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) max_r = k;for (i = k + 1; i abs(amax_rcol) max_r = i;if (max_r != k)( /与第k行交换.for (j = k; j var + 1; j+) swap(akj, amax_rj);if (akcol = 0)( /说明该col列第k行以下全是0了,则处理当前行的下一列.k-; continue;for (i = k + 1; i equ; i+)( /枚举要删去的行.if (aicol != 0)LCM = lcm(abs(aicol), abs(akcol);ta = LCM / abs(aicol), tb = LCM / abs(akcol);if (aicol * akcol 0) tb = -tb; / 异号的情况是两个数相加.for (j = col; j var + 1; j+)(aij = aij * ta - akj * tb;Debug();/ 1.无解的情况:化简的增广阵中存在(0,0, ., a)这样的行(a != 0).for (i = k; i equ; i+)( /对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则 要记录交换.if (aicol != 0) return -1;/ 2.无穷解的情况:在var * (var + 1)的增广阵中出现(0,0, ., 0)这样的行,即说明没有形成 严格的上三角阵./且出现的行数即为自由变元的个数.if (k = 0; i-)(/第i行一定不会是(0, 0, ., 0)的情况,因为这样的行是在第k行到第equ行./同样,第i行一定不会是(0, 0, ., a), a != 0的情况,这样的无解的.free_x_num = 0; /用于判断该行中的不确定的变元的个数,如果超过1个,则无法 求解,它们仍然为不确定的变元.for (j = 0; j 1) continue; /无法求解出确定的变元./说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是 确定的.temp = aivar;for (j = 0; j = 0; i-)(temp = aivar;for (j = i + 1; j var; j+)(if (aij != 0) temp -= aij * xj;if (temp % aii != 0) return -2; /说明有浮点数解,但无整数解. xi = temp / aii;return 0;int main(void)(freopen(Input.txt”, r, stdin);int i, j;while (scanf(%d %d, &equ, &var) != EOF)(memset(a, 0, sizeof(a);memset(x, 0, sizeof(x);memset(free_x, 1, sizeof(free_x); / 一开始全是不确定的变元.for (i = 0; i equ; i+)(for (j = 0; j 0)(printf(无穷多解!自由变元个数为%dn, free_num);for (i = 0; i var; i+)(if (free_xi) printf(x%d 是不确定的n, i + 1);else printf(x%d: %dn, i + 1, xi);else(for (i = 0; i var; i+)(printf(x%d: %dn, i + 1, xi);printf(n);return 0;
展开阅读全文