1. 程式人生 > >解線性方程組——高斯消元の板子

解線性方程組——高斯消元の板子

ATP記得它在很久以前看過一點點高斯消元的東西然後做過一點點題目。。但是當時實在是太zz了所以本來就沒有很懂這個東西現在更是忘得差不多了。。
所以現在就當重新學一遍了QwQ

一點口胡的解釋

高斯消元。。聽起來這個名字很高大上實際上它也確實很高大上但是它的操作過程實際上和數學課學的加減消元法是一個道理的啦。

它可以用來求解線性方程組Ax=b,其中A是一個nn的矩陣,xbn1的矩陣。實際上也就是求解由n個未知量和n個方程組成的n元一次方程組,其中A是係數矩陣,A的第i行代表第i個方程,第j列代表第j個未知數對應的係數。而b是常數項,第i行代表第i個方程等號後面的常數項。

高斯消元還可以求解可逆矩陣的逆矩陣。還可以求解矩陣的秩。(秩是啥?我不會= =)

高斯消元的操作過程實際上非常暴力。。總的來說分兩個步驟,首先要通過矩陣行變換也就是加減消元消來消去把這些方程變成某個等價形式。這個等價形式的特徵體現在係數矩陣A裡就是A變成了一個“上三角矩陣”,也就是隻有左上-右下對角線及其上方有數字,這條對角線的下面都是0,大概就是這個樣的東西:

A1,1000A1,2A2,20A1,nA2,nAn,n
這樣的話可以看出來第i個方程有n-i+1個係數,那麼最後一個方程只有一個係數,也就是經過這樣消元以後最後一個方程變成了An,nxn=b
n
的形式,那麼就可以直接得到xn的值了。得到xn以後就可以往回代,因為倒數第2個方程有2個係數,其中一個係數是關於xn的,把得到的xn的值帶回去以後就可以把它消成只有一個未知量的方程,那麼就可以得到xn1。同理把已經求出的未知量都往回代,就可以求出所有的未知數。

貼一個板子。出自BZOJ1013球形空間產生器,是高斯消元的一個板子題:


void Gauss_Eli(){
    int num;
    for (int i=1;i<n;i++){
        num=i;
        for (int j=i+1;j<=n;j++)
          if (fabs
(A[j][i])>fabs(A[num][i])) num=j; for (int j=1;j<=n;j++) swap(A[i][j],A[num][j]); swap(b[num],b[i]); for (int j=i+1;j<=n;j++){ if (fabs(A[j][i])<=eps) continue; double t=A[j][i]/A[i][i]; for (int k=1;k<=n;k++) A[j][k]-=A[i][k]*t; b[j]-=b[i]*t; } } for (int i=n;i>=1;i--){ ans[i]=b[i]/A[i][i]; for (int j=i;j>=1;j--) b[j]-=A[j][i]*ans[i]; } }

可以看出高斯消元具體的實現複雜度是O(n3)的。這裡以及下面我們都把將要留在上三角矩陣的第i行的方程稱為未知數i的關鍵方程,因為如果未知數xi的值是唯一的,上三角矩陣的第i行必須有未知數xi的係數。

上面的程式碼大體來說就是先列舉每一個變數i找到它的關鍵方程,然後把它的關鍵方程下面所有方程xi的係數都消成0讓它符合上三角矩陣的形式,最後再進行回代。

首先我們要從上到下列舉每一行。設當前列舉到的是第i行,那麼前i-1行已經消成了上三角矩陣的一部分,不能再動了。我們要找第i個未知量的關鍵方程只能從後面找。上面程式碼的第4..9行幹了一個看起來比較奇怪的事情:從後面的方程中找了一個未知數i的係數最大的方程然後換到第i個位置。據說找一個最大的能減少浮點誤差?然而比較實質性的作用還是保證選中的這個“準”關鍵方程裡xi的係數不是0吧。。如果它題目裡給的方程排列比較湊巧,第i行上xi的係數正好是0,那麼它肯定不能作為xi的關鍵方程,肯定要找一個係數不是零的搞過來用。所以這一步還是比較重要的。

找到了“準”關鍵方程以後我們要把它變成關鍵方程。要乾的事情就是讓它下面的所有方程xi的係數都變成0。那麼就要用到加減消元法了。設第i行下面的某一行為j,如果第j行中xi的係數不為0,那麼我們就要把它變成0。聯想一下數學課上解二元一次方程組的方法,我們要把某一個方程中某一個未知量消掉,方法就是把這個方程擴大一個特定的倍數,讓要消掉的這個未知量的係數和另一個方程的對應係數相等,再把這兩個方程相減就能消掉目標未知數。那麼這裡目標未知數xi的係數是Ai,i,我們要把它擴大某個倍數讓Ai,i變成Aj,i,然後用第j個方程和第i個方程作差來消掉xi。顯然只需要把第i個方程擴大Aj,iAi,i倍就能解決問題。那麼求出這個係數,將第i個方程包括後面帶著的常數項bi都乘以這個係數然後作差就可以了。

最後一步是回代,回代的時候要從後往前列舉上三角矩陣中的方程,每次求出一個未知數xi以後都檢查前面所有xi的係數不為0的方程然後把它代進去,相當於進行一次移項,因為xi求出來以後原本帶未知數的項變成了常數項,那麼把它移動到方程右邊和b合併就可以了。最後當這個方程後面的所有方程都處理過了以後,它自己也只帶一個未知數了,並且這個未知數的值也可以直接求出來了。

比較簡單的題目

1.異或方程組(BZOJ1923、POJ1222、POJ1681、POJ1830、POJ3185、BZOJ1770)

異或方程組是用高斯消元可以解決的一大類問題,實際上就是普通解方程組的變形。在異或運算裡面因為當前變數的出現情況只和係數的奇偶性有關,那麼可以直接把係數和常數項變成0或1。而加減消元也變成了直接異或,因為不需要計算擴大的倍數,只要異或一下就能把目標未知數消去。

“開關問題”是異或方程組的經典問題,大概就是指定一些燈,每個燈的初始狀態或開或關。每個燈有一個開關,每個開關還有一些相關的開關。如果按下一個開關,那麼與之相關的開關都會被按下,相連的燈的開關狀態都會改變,求讓燈達到某個目標狀態的方案。

可以發現燈的開關狀態只和按下開關次數的奇偶性有關,那麼可以構造異或方程組的模型。把每個開關按或沒按設成未知量xi,如果按了就賦值為1,沒按就賦值為0,那麼可以發現某個燈狀態改沒改變取決於它自己的開關和所有與它相連的開關。按照相連狀態建立矩陣,對於每個燈建立一個方程,把所有會影響到它的開關對應的係數賦值為1,不會影響到它的就賦值為0。常數項b對應著這個燈的目標狀態,然後解異或方程組就可以了。

在解異或方程組的時候還有一個有趣的優化。可以發現在消元的時候實際上就是係數矩陣的每一行按位異或,那麼如果用bitset儲存係數矩陣A,每次消元的時候就可以一次異或一整行,省去了列舉,可以把時間複雜度從O(n3)優化到O(n364)。有些題不用bitset是過不了的。。

這裡貼出使用bitset的板子。常數項bi存在了Ai,n+1中。

bitset<250>A[250];
void Gauss_Eli(){
    int num;
    for (int i=1;i<=n;i++){
        if (A[i][i]==0){
            num=i;
            for (int j=i+1;j<=n;j++)
              if (A[j][i]!=0){num=j;break;}
            swap(A[num],A[i]);
        }
        for (int j=i+1;j<=n;j++)
          if (A[j][i]!=0) A[j]^=A[i];
    }
    for (int i=n;i>=1;i--){
        ans[i]=A[i][n+1];
        for (int j=i;j>=1;j--)
          if (A[j][i]!=0)
            A[j][n+1]=A[j][n+1]^ans[i];
    }
}

在異或方程組中常常考察的另一個問題就是自由元的問題。在消元的過程中,可能某個未知數xi根本就找不到“關鍵方程”,那麼

相關推薦

線性方程組——板子

ATP記得它在很久以前看過一點點高斯消元的東西然後做過一點點題目。。但是當時實在是太zz了所以本來就沒有很懂這個東西現在更是忘得差不多了。。 所以現在就當重新學一遍了QwQ 一點口胡的解釋 高斯消元。。聽起來這個名字很高大上實際上它也確實很

[SDOI2006]線性方程組——模板

題目大意: 求解線性方程組。 判斷惟一解,無窮解,無解的三種情況。 高斯消元: 洛谷的模板題好像怎麼打都可以過,也沒有具體區分無窮解和無解的情況,看來這個題才是高斯消元的真正模板。 惟一解: 這個大概是最好判斷的了,在每次消元的時候都沒有出現係數全部都為0的情況

【bzoj4004】【JLOI2015】裝備購買 (線性基+

complete truct algo turn insert input 否則 沒有 main Description 臉哥最近在玩一款神奇的遊戲,這個遊戲裏有 n 件裝備,每件裝備有 m 個屬性,用向量zi(aj ,.....,am) 表示 (1 <= i <

題解 外星千足蟲(線性基+)

置疑 pac 想要 %d ++ ldb printf ins n+1 題解 luogu外星千足蟲(線性基+高斯消元) 題目 luogu題目傳送門 題解想法 首先需要知道這是個異或方程對吧 然後既然看到位運算,又有這麽多,就可以考慮線性基(做題技巧),那我們就丟進去 接下

板子

#include<stdio.h>///poj 2947當作板子可能更好一下 #include<algorithm> #include<iostream> #include<string.h> #include<math.h> usin

bzoj1013球形空間產生器 板子

第一次寫高斯消元啦啦啦~~ #include <cstdio> #include <cmath> #include <algorithm> #define db double #define rep(i,j,k) for (i=j;i<=k;i++)

BZOJ 3105 線性

思路: 按照從大到小排個序 維護兩個陣列 一個是消元后的 另一個是 按照消元的位置排的 不斷 維護從大到小 (呃具體見程式碼) //By SiriusRen #include <c

HDU 5833 Zhu and 772002(異或方程組)

#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib&g

hdu3949 XOR (線性基())

hdu3949 XOR 題意: T組資料, 每組資料給n個數,m個詢問,對於每個詢問給出一個k,詢問給的n個數,選取任意非空子集,能異或出的數中第k小的,重複的數不計算。 資料範圍 T&

Python計算——線性方程組

#!/usr/bin/env python # coding=gb2312 # 以上的資訊隨自己的需要改動吧 def print_matrix( info, m ): # 輸出矩陣 i = 0; j = 0; l = len(m) print info

POJ.2065.SETI(線性方程組)

oid 求解 一個 fine earth std htm unique line 題目鏈接 http://blog.csdn.net/Clove_unique/article/details/54381675 http://blog.csdn.net/u013081425/

HDU.3571.N-dimensional Sphere(線性方程組)

con 需要 etc oid 如果 git inline cpp 由於 題目鏈接 高斯消元詳解 /* $Description$ 在n維空間中給定n+1個點,求一個點使得這個點到所有點的距離都為R(R不給出)。點的任一坐標|xi|<=1e17. $Solution$

法求解線性方程組(分數精確表示)

原理可以參考 https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%B6%88%E5%8E%BB%E6%B3%95 這裡給出使用分數表示計算過程的高斯消元法寫法 github地址:https://github.com/YIWANFEN

求解線性方程組_BZOJ4004_裝備購買

點此開啟題目頁面 思路分析:     考慮求解所有n個裝備的屬性對應的n個長度為m的向量的基底, 使用高斯消元時, 每次選擇當前對應列非零, 且價格最低的行(裝備)進行消元, 具體實現如下AC程式碼所示: //BZOJ4004_裝備購買 #include <

01異或方程組(附poj 1222題解)

const int maxn=50; //有equ個方程,var個變元。增廣矩陣行數為equ,列數為var+1 int equ,var; int a[maxn][maxn]; //增廣矩陣 int x[maxn]; //解集 int free_x[maxn]; int f

異或方程組-開關問題

#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<iomanip> #include<vect

法——求解線性方程組

學習了《挑程》中的高斯消元法,它是求解最基礎的線性方程組(未知數個數和方程個數相等,並且有唯一解)的演算法。 首先舉一個例子:求解如下方程組: ⎧⎩⎨x−2y+3z=6..........①4x−5y+6z=12.......②7x−8y+10z=21...

法求解線性方程組

con sta eat sed 高斯 sso 一個 ids row 線性方程組問題可以利用矩陣變換求解。利用高斯消元法,將矩陣轉換成一個行階梯矩陣,最後得到一個簡化行階梯矩陣,就是方程的解。參考資料(高斯消元法) Java代碼 public class Function

hdu5833 Zhu and 772002 【異或方程組

題意:給你n個數,每個數的素數因子最大不超過2000,從n個數取出1~n個,問有多少種方案使得騰門乘積為完全平方數。 分析:我們知道完全平方數分解後的所有素數的都是偶數次方的,所以我們可以將所有數都素因素分解,可以得到選出來的數都是2^(x1+x2...)*3^(x1+x

poj 2065 取模方程組

題意: a0*1^0 + a1*1^1+a2*1^2+........+an-1*1^(n-1) = f(1) a0*2^0 + a1*2^1+a2*2^2+........+an-1*2^(n-1) = f(2) ...... a0*n^0 + a1*n^1+a