【C++】超級詳細,多元一次方程的求解方法

浸 心發表於2020-12-19

在這個問題中,假設我們是要解一個N元一次的方程;

解題使用方法:

使用多元一次方程的 消去法
假設我有三個方程
2a-b+c=3 , 3a+b-c=2 , a-3b+2c=1
對第一個式子我讓第一個元素為準(我們這裡就叫他標準行吧,先記住這個名字!!!!!!),即a,每個方程同時除以a的係數,就變成了
a-(1/2)b+(1/2)c=3/2 , a+(1/3)b-(1/3)c=2/3 , a-3b+2c=1
然後,用第一個式子的a去替換掉其他式子的a(自身不換),也可以理解為其他式子減去用來替換a的式子,方程變為
a+2b+2c=1 , (5/6)b-(5/6)c=-(5/6) , -(5/2)b+(3/2)c=1/3
這裡第二個式子和第三個式子都只有bc,按照這種方式,我再以第二個方程為準(第二個方程作為標準行),替換掉第三個方程的b(這裡為什麼不替換第一個方程後面解釋),這樣第三個方程只剩下c,最後以第三個方程為標準行,因為它這個方程只有一個元素,可以解出c
然後將c往第二個方程中帶入第二個方程(只有b,c兩個元素),解出b,再帶入第一個,解出a
所以,在第一次替換後,只有第一個式子有a,如果我還去替換它,那肯定算不出a的呀

儲存辦法:

一個多元一次的方程要用什麼來儲存呢?我們可以將方程儲存在double m[N][N+1]陣列(N個需要求解的元素,還有第N+1個位置儲存方程結果)中;
在這裡插入圖片描述
計算的資料結果我們可以在double s[N]陣列中。

同時,還需要一個用來記錄方程中用來替換其他的元素的陣列int loc[N],並且初始化它為-1,元素位置不可能為-1(比如說我第二個方程第二個元素,即b元素就是用來替換其他方程的第二個元素的,則loc[1]=1,這裡從0開始)

辦法:解決這個問題,我們需要一些函式

1,

一個能將第h行的方程同時除以第n個元素的係數的函式

void make1(int h, int n)//使h行同時除以第n個元素
{
	double t = m[h][n];//過程中第n個元素的係數會改變,所以先儲存其係數
	if (m[h][n] == 0)
		return;
	for (int i = 0; i < N + 1; i++)
		m[h][i] /= t;
}

為了方便記,就記make1,make係數變為1

在輪到第n個元素做消去的時候,對所以能夠除去第n個元素的係數的方程進行make1函式

void Allmake1(int n)
{
	for (int i = 0; i < N; i++)
	{
		if (loc[i] == -1 && m[i][n] != 0)
			make1(i, n);
			//loc[i]==-1,說明這一行沒有做過標準行
			//m[i][n] != 0,說明這個方程的第n個元素係數不是0
			//如果是0,除以係數是會出現分母為0的情況,答案可能會得到-inf
	}
}

Allmake1,讓所有係數變為一(不是真的所有)

2,在進行了除以係數的函式後,我們就要開始替換啦

min函式的作用是標準行s,對目標行h,進行替換掉第n個元素的操作
因為之前進行過所有方程的第n個元素的係數直接變成一的操作了,所以直接減法就可以讓h的第n個元素消去
不應該減去的方程是:作標準行的和這個方程沒有第n個元素的(n號元素在方程中係數為0)

void min(int s,int h,int n)
{
	if(loc[h]==-1&&m[h][n]!=0)
		for (int i = 0; i < N+1; i++)
			m[h][i] -= m[s][i];
}


void Allmin(int s,int n)//對所有應該替換的程都替換掉第n個元素
{
	for (int i = 0; i < N; i++)
		min(s, i, n);
}

記住min消去你的第n個元素
Allmin(消去所有方程第n個元素,不是真的所有哦)

3,標記第h個方程是作為第n個元素標準行的函式

隨便找一個第n個元素的係數不為0的方程(在第h行),記錄loc並且用這個方程去替換除了自己以外所有這個元素的係數不為0並且沒有作為標準行過的方程(這個方程在loc陣列中對應值為-1)

void setloc(int h, int n)
{
	loc[h] = n;
}

setloc,標記標準行

4,找到第n個元素的標準行,並對其他行進行替換的函式

void Onereplace(int n)
{
	int l;
	for (int i = 0; i < N; i++)
	{
		if (loc[i] == -1 && m[i][n] != 0)
		{
			l = i;
			setloc(i, n);//標記標準行
			make1(i, n);//使自身方程進行除法,都除以第n個元素的係數
			break;
		}	
	}
	Allmake1(n);//對所有方程進行make1
	Allmin(l,n);//消去第n個元素
}
void Allreplace()//從第1個元素到第n個,所有元素能換掉就換掉
{
	for (int i = 0; i < N; i++)
		Onereplace(i);
}

One replace,換掉所有的第n個元素
All replace,換到所有元素(不是真的所有)

這就是主要的函式啦,但是你不覺得一個一個函式打出來很麻煩嗎? 我乾脆把他們都放在一個函式裡算啦。

void pc()
{
	fill(loc, loc + N, -1);
	Allreplace();
	Allcacular();
}

還要有一個輸出元素結果的函式

void Cout()
{
	for (int i = 0; i < N; i++)
		cout << s[i] << " ";
	cout << endl;
}

什麼!你有強迫症,不想一個一個函式單獨看? 行行,整合了發。
唉,隨便水水字數了,好累

#include<iostream>
using namespace std;
#define N 4//我自己假設的例子,這裡N等於4,結果a=1,b=2,c=3,d=4
double m[N][N + 1] = {
    0,3,2,1, 16,
	1,1,1,1, 10,
	3,2,2,1, 17,
	2,0,1,2, 13
//不放心你就自己整幾個例子啊,學生也很忙的啊,現在都1:53了,肝死我了
};
double s[N];
int loc[N];
void Allmin(int s, int n);//宣告函式,不然會報錯
void make1(int h, int n)
{
	double t = m[h][n];
	if (m[h][n] == 0)
		return;
	for (int i = 0; i < N + 1; i++)
		m[h][i] /= t;
}
void setloc(int h, int n)
{
	loc[h] = n;
}
void Allmake1(int n)
{
	for (int i = 0; i < N; i++)
	{
		if (loc[i] == -1 && m[i][n] != 0)
			make1(i, n);
	}
}
void Onereplace(int n)
{
	int l;
	for (int i = 0; i < N; i++)
	{
		if (loc[i] == -1 && m[i][n] != 0)
		{
			l = i;
			setloc(i, n);
			make1(i, n);
			break;
		}	
	}
	Allmake1(n);
	Allmin(l,n);
}
void min(int s,int h,int n)
{
	if(loc[h]==-1&&m[h][n]!=0)
		for (int i = 0; i < N+1; i++)
			m[h][i] -= m[s][i];
}
void Allmin(int s,int n)
{
	for (int i = 0; i < N; i++)
		min(s, i, n);
}
void Allreplace()
{
	for (int i = 0; i < N; i++)
		Onereplace(i);
}
void cacular(int n)
{
	for(int i=0;i<N;i++)
		if (loc[i] == n)
		{
			for (int k = 0; k < N; k++)
				if (m[i][k]!=0&&k != n)
					m[i][N] -= m[i][k] * s[k];
			s[n] = m[i][N] / m[i][n];
			break;
		}
}
void Allcacular()
{
	for (int i = N - 1; i >= 0; i--)
		cacular(i);
}
void pc()
{
	fill(loc, loc + N, -1);
	Allreplace();
	Allcacular();
}
void Cout()
{
	for (int i = 0; i < N; i++)
		cout << s[i] << " ";
	cout << endl;
}

int main()
{
	pc();
	Cout();
}

相關文章