高斯消元基础
# 消元法
消元法来解线性方程组,通过不停的消减未知数,最终得到所有未知数的值。
常用的方式有三种基本操作:
- 一个方程的左右两边同时乘以一个常数。
- 用一个方程加或减另一个方程组。
- 交换两个方程的位置
# 增广矩阵
行数代表方程的个数,列代表方程的未知数。
在原来系数矩阵的基础上添加了一列结果列,那么这个矩阵就叫做增广矩阵。
# 运算过程
# 消元操作转矩阵操作
一个方程的左右两边同时乘以一个常数
矩阵中某一行乘以一个常数
用一个方程加或减另一个方程组
矩阵中的一行加或减另一行
交换两个方程的位置
交换矩阵的两行
# 高斯消元
主元就是每一行主要的系数。
将第n行的第n个元素的系数变为1。
有时会涉及到两行交换。
甚至还有向前回代,但也可以无回代,那就是高斯约旦消元法
# 高斯约旦消元法
让第二行 加上 第三行乘以10,就能求出y了。
让第 减去 四乘以第三行
让第一行 减去 二倍的第二行
最终结果,这就是逆序的再进行一下消元。
除了主元以外的其它元都被削去,这就是高斯约旦消元法。
高斯消元法只有一个前向的过程,也就是从上到下。而高斯约旦消元法完成了后向过程,也就是从下往上。
# 代码
class LinearSystem {
constructor(A, b) {
console.assert(A.length === b.length, "矩阵行数和值的个数得一样")
this.m = A.length
this.n = A[0].length
console.assert(this.m===this.n, '矩阵的行列必须相同')
this.Ab = [] // 增广矩阵
A.forEach((row, i) => {
this.Ab.push([...row, b[i]])
})
// 备份一下原方程
this.originAb = []
A.forEach((row, i) => {
this.originAb.push([...row, b[i]])
})
}
gaussJordanElimination() {
this._forward()
this._backward()
}
fancyPrint () {
const origin = this._fancyPrint(this.originAb)
const result = this._fancyPrint(this.Ab)
console.log("============= 原方程 =============")
console.log(origin)
console.log("=========消元后方程结果:=========")
console.log(result)
}
_fancyPrint (Ab) {
const result = Array(this.m).fill(1).map((_$i,i) => {
let content = ``
content = Array(this.n).fill(1).map((_$j, j) => {
return Ab[i][j]
}).join(' ')
content += ` |${Ab[i][this.n]}`
return content
}).join('\r\n')
return result
}
_forward() {
let n = this.m
let maxRow
for (let i = 0; i < n; i ++) {
// Ab[i][i] 为主元
maxRow = this._maxRow(i, n);
[this.Ab[i], this.Ab[maxRow]] = [this.Ab[maxRow], this.Ab[i]];
// 将主元归一,NOTE:它应该想的是让this.Ab[i] 这一行全都除以这个系数,从而实现主元归一
this.Ab[i] = this.Ab[i].map(num => _d(num, this.Ab[i][i])) // this.Ab[i] / this.Ab[i][i] TODO this.AB[i][i] === 0
// 高斯消元
for (let j = i + 1; j < n; j ++) {
let Abi = [...this.Ab[i]]
Abi = Abi.map(num => _m(num, this.Ab[j][i]))
this.Ab[j] = this.Ab[j].map((num, _) => _c(num, Abi[_]))
}
}
}
_backward() {
let n = this.m
for (let i = n - 1; i > -1; i --) {
// Ab[i][i] 为主元
for (let j = i - 1; j > -1; j --) {
let Abi = [...this.Ab[i]]
Abi = Abi.map(num => _m(num, this.Ab[j][i]))
this.Ab[j] = this.Ab[j].map((num, _) => _c(num, Abi[_]))
}
}
}
// 找到主元最大的那一行
_maxRow (index, n) {
let [best, ret] = [this.Ab[index][index], index];
for (let i = index + 1; i < n; i ++) {
if (this.Ab[i][index] < best) {
[best, ret] = [this.Ab[i][index], i];
}
}
return ret
}
}
(() => {
const A =[
[1, 2, 4],
[3, 7, 2],
[2, 3, 3],
]
const b = [
7, -11, 1
]
// const A =[
// [1, -3, 5],
// [2, -1, -3],
// [3, 1, 4],
// ]
// const b = [
// -9, 19, -13
// ]
// const A =[
// [1, 2, -2],
// [2, -3, 1],
// [3, -1, 3],
// ]
// const b = [
// 6, -10, -16
// ]
// const A =[
// [8, 2, 1, 2.5],
// [1, 8,-0.5, 2],
// [1.5, 2, 8, -1],
// [1, 0.5, 0.7, 8],
// ]
// const b = [
// 1.5, -3, -4.5, 3.2
// ]
const linearSystem = new LinearSystem(A, b)
linearSystem.gaussJordanElimination()
linearSystem.fancyPrint()
})()
// #region 精确数学运算
// 计算的地方很多的话,需要很精确的话,建议使用math.js这个插件库
//加法函数,用来得到精确的加法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
//调用:_p(arg1,arg2)
//返回值:arg1加上arg2的精确结果
function _p(arg1, arg2) {
arg1 = parseFloat(arg1);
arg2 = parseFloat(arg2);
var r1, r2, m;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(100, Math.max(r1, r2));
return (_m(arg1, m) + _m(arg2, m)) / m;
}
//减法函数,用来得到精确的减法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。
//调用:_c(arg1,arg2)
//返回值:arg1减去arg2的精确结果
function _c(arg1, arg2) {
arg1 = parseFloat(arg1);
arg2 = parseFloat(arg2);
var r1, r2, m, n;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2));
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((_m(arg1, m) - _m(arg2, m)) / m).toFixed(n);
}
//乘法函数,用来得到精确的乘法结果
//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
//调用:_m(arg1,arg2)
//返回值:arg1乘以arg2的精确结果
function _m(arg1, arg2) {
arg1 = parseFloat(arg1);
arg2 = parseFloat(arg2);
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try { m += s1.split(".")[1].length } catch (e) { }
try { m += s2.split(".")[1].length } catch (e) { }
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
//除法函数,用来得到精确的除法结果
//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
//调用:_d(arg1,arg2)
//返回值:arg1除以arg2的精确结果
function _d(arg1, arg2) {
arg1 = parseFloat(arg1);
arg2 = parseFloat(arg2);
var t1 = 0, t2 = 0, r1, r2;
try { t1 = arg1.toString().split(".")[1].length; } catch (e) { }
try { t2 = arg2.toString().split(".")[1].length; } catch (e) { }
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return _m(r1 / r2 , Math.pow(10, t2 - t1));
}
// #endregion
上次更新时间: 10年18月2023日 01时57分53秒