最近做线性代数练习,遇到一节练习,要求我们全部用cramer定理去解线性方程。这显然是很离谱的,因为cramer定理虽然正确,但计算量巨大。而练习涉及到的矩阵,大部分都是三阶和四阶矩阵,有一题甚至涉及6阶矩阵,简直丧心病狂。所以我用deepseek生成了一篇代码。这篇代码写的很好,思路清晰,结构规整而且有许多对numpy的调用值得学习,所以我准备把这篇代码整理下来,写篇文章放到博客上。
下面就是deepseek生成的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import numpy as npdef read_matrix (n ): """读取n阶方阵和常数项""" matrix = [] b = [] print (f"请输入{n} 行,每行包含{n + 1 } 个数值(系数 + 常数项):" ) for _ in range (n): while True : row = input ().split() if len (row) == n + 1 : try : coefficients = list (map (float , row[:n])) constant = float (row[-1 ]) matrix.append(coefficients) b.append(constant) break except ValueError: print ("输入包含非数字,请重新输入该行:" ) else : print (f"每行需要输入{n + 1 } 个数值,请重新输入该行:" ) return np.array(matrix), np.array(b)def cramer_solve (A, b ): """使用Cramer法则解线性方程组""" try : D = np.linalg.det(A) except np.linalg.LinAlgError: D = 0 if abs (D) < 1e-9 : return None , None D_values = [] for i in range (A.shape[1 ]): Ai = A.copy() Ai[:, i] = b D_values.append(np.linalg.det(Ai)) solutions = [D_i / D for D_i in D_values] return D, D_values, solutionsdef main (): n = int (input ("请输入方阵的阶数n:" )) A, b = read_matrix(n) D, D_values, solutions = cramer_solve(A, b) print ("\n计算过程:" ) print (f"系数矩阵的行列式 D = {D:.6 f} " ) for i, D_i in enumerate (D_values, 1 ): print (f"替换第{i} 列后的行列式 D{i} = {D_i:.6 f} " ) if solutions is None : print ("\n行列式D为0,方程组无唯一解!" ) else : print ("\n方程组的解:" ) for i, x in enumerate (solutions, 1 ): print (f"x{i} = {x:.6 f} " )if __name__ == "__main__" : main()
首先值得我学习的就是这个结构的设置,定义多个函数,最后再main函数里将他们组合。这样做就很容易看出代码的逻辑,同时也方便在代码发生错误时,对错误进行定位和修正。同时代码中用了多处 try: except 语句,对代码可能出现的问题进行一定的预测,规避代码出现故障。这一点对python还是蛮重要的,因为python的一些库中的函数,在遇到问题时就会返回错误值,程序就有可能直接在出现问题的地方崩溃。实际上一些会导致函数返回错误值的问题,比如输入错误,本身问题不大。在这个时候运用try–except语句就可以显著提升代码的流畅性。
其次就是利用numpy库对矩阵的处理。要善用冒号之类的符号,可以显著提高效率,同时在涉及到对数列或者矩阵进行函数操作时,要能想到用 map 进行处理。比如代码对输入进行处理的部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for _ in range (n): while True : row = input ().split() if len (row) == n + 1 : try : coefficients = list (map (float , row[:n])) constant = float (row[-1 ]) matrix.append(coefficients) b.append(constant) break except ValueError: print ("输入包含非数字,请重新输入该行:" ) else : print (f"每行需要输入{n + 1 } 个数值,请重新输入该行:" )
注意最后函数的返回,用到了 array 函数,这个函数可以创建一个数组,而我们需要返回的也是一整个数组。所以用 array 非常合适。经测试,假如这里不用array,虽然编译时不会报错,但是使用时会出现问题,程序会直接崩溃。我自己写似乎很难会想到这一点。。。
而后面主要学习的就是对矩阵列变换的处理。这里用了一个 shape 函数,用来获取矩阵的列数。根据 cramer 定理,我们需要把 b 带入到 A 中的每一列去求行列式。在 python 里,只要满足行数相同,我们就可以直接进行赋值。这里为了不干涉原矩阵 A ,所以用到了 copy 语句进行复制,再对复制出的 Ai 进行处理。随后再把得到的结果放进储存结果的数组中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def cramer_solve (A, b ): """使用Cramer法则解线性方程组""" try : D = np.linalg.det(A) except np.linalg.LinAlgError: D = 0 if abs (D) < 1e-9 : return None , None D_values = [] for i in range (A.shape[1 ]): Ai = A.copy() Ai[:, i] = b D_values.append(np.linalg.det(Ai)) solutions = [D_i / D for D_i in D_values] return D, D_values, solutions
在 main 函数部分有个小技巧,就是enumerate函数的运用。这个函数可以同时列出数据和下标,省事了不少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def main (): n = int (input ("请输入方阵的阶数n:" )) A, b = read_matrix(n) D, D_values, solutions = cramer_solve(A, b) print ("\n计算过程:" ) print (f"系数矩阵的行列式 D = {D:.6 f} " ) for i, D_i in enumerate (D_values, 1 ): print (f"替换第{i} 列后的行列式 D{i} = {D_i:.6 f} " ) if solutions is None : print ("\n行列式D为0,方程组无唯一解!" ) else : print ("\n方程组的解:" ) for i, x in enumerate (solutions, 1 ): print (f"x{i} = {x:.6 f} " )
以上就是对这份代码的整理与学习,目前我还没试着自己重写一遍,未来在涉及到其他矩阵问题时可以借鉴一下这份代码当然也可以再问小鲸鱼。希望自己能获得更高层次的提升吧。