在线性空间求基的时候,经常会遇到齐次方程组基础解系的问题,比如以下齐次线性方程组:
(321000000)v=(000)\begin{pmatrix}3&2&1\\0&0&0 \\0&0& 0\end{pmatrix}v =\begin{pmatrix}0\\0\\0\end{pmatrix} ⎝⎛300200100⎠⎞v=⎝⎛000⎠⎞
这种秩为1的三维矩阵,我们知道,它的基础解系肯定是2个向量,那么未知数肯定有6个,我们只有三个已知数,要求六个未知数,怎么办?数字简单还可以配出来,数字复杂了,很多人就不知道怎么搞了?其实技巧很简单,把齐次方程转变为非齐次方程就好了。以上面的齐次线性方程为例子,基础解系肯定是下面的样子:
v=k1(abc)+k2(def)v=k_1\begin{pmatrix}a\\b\\c\end{pmatrix}+k_2\begin{pmatrix}d\\e\\f\end{pmatrix}\\ v=k1⎝⎛abc⎠⎞+k2⎝⎛def⎠⎞
先让两个基的坐标正交,也就是k1,k2k_1,k_2k1,k2正交,得到两个方程,如下:
k1=1,k2=0,⇒3a+2b+c=0k1=0,k2=1,⇒3d+2e+f=0k_1=1,k_2=0,\Rightarrow 3a+2b+c=0\\ k_1=0,k_2=1,\Rightarrow 3d+2e+f=0\\ k1=1,k2=0,⇒3a+2b+c=0k1=0,k2=1,⇒3d+2e+f=0
因为这里的基的三个坐标可以任意取值,所以每个基的前两个坐标单位正交,去生成另外4个方程:
a=1b=0d=0e=1⇒c=−3f=−2a=1\\ b=0\\ d=0\\ e=1\\ \Rightarrow c=-3\\ f = -2 a=1b=0d=0e=1⇒c=−3f=−2
所以基础解系为:
v=k1(10−3)+k2(01−2)v=k_1\begin{pmatrix}1\\0\\-3\end{pmatrix}+k_2\begin{pmatrix}0\\1\\-2\end{pmatrix}\\ v=k1⎝⎛10−3⎠⎞+k2⎝⎛01−2⎠⎞
编程的思路是:
考虑到如果某行只有1个非零列,那么这列对应的所有基坐标必须为0,所以不能像上例一样生成方程了,举个例子:
(111010000)v=(000)\begin{pmatrix}1&1&1\\0&1&0 \\0&0& 0\end{pmatrix}v =\begin{pmatrix}0\\0\\0\end{pmatrix} ⎝⎛100110100⎠⎞v=⎝⎛000⎠⎞
对于这种情况,我们就必须标记基里必须为零的坐标。排除必须为0的,然后再用单位正交的方法去生成方程。那么前面的流程就又可以改一下:
再思考下,如果基的坐标中只有1个是1,其余的都是0的话,其实每个基都是孤立的,我们一个个基去解决就完事了。这个基中有xxx个已知数,只剩下n−xn-xn−x个未知数。所以求每个基的步骤如下:
private List homogeneousSolution(T[][] ts) {// 首先计算秩int n = ts.length;int rank = 0;List mustZeroLines = new ArrayList<>();List mustZeroColumns = new ArrayList<>();for (; rank < n; rank++) {// 非零列数量int nonZero = 0;int firstNonZeroColumn = 0;T[] line = ts[rank];for (int j = 0; j < line.length; j++) {T x = line[j];if (!this.zeroValue().equals(x)) {nonZero++;firstNonZeroColumn = j;}}if (nonZero == 0) {break;} else if (nonZero == 1) {mustZeroLines.add(rank);mustZeroColumns.add(firstNonZeroColumn);}}// 满秩只有零解if (rank == n) {final T[] o = this.newArray(n);Arrays.fill(o, this.zeroValue());return Collections.singletonList(o);}/** 转成齐次线性方程组,未知数为num * n个。* 将基座标分别取正交,baseNum * r个方程* 所以还需要 baseNum * num个方程* 生成方程,方程有(n−x)(n−r)个未知数* mustZeroColumns是必须为0的列*/List freeZeroColumns = new ArrayList<>();for (int i = 0; i < n; i++) {if (!mustZeroColumns.contains(i)) {freeZeroColumns.add(i);}}// 基数量int baseNum = n - rank;List solutions = new ArrayList<>();// 方程// 对每个基进行正交for (int i = 0; i < baseNum; i++) {T[] solution = getBase(ts, rank, mustZeroLines, mustZeroColumns, i);solutions.add(solution);}return solutions;}private T[] getBase(T[][] ts, int rank, List mustZeroLines, List mustZeroColumns, int baseIndex) {int n = ts.length;int x = mustZeroColumns.size();// 本质上是只取一个向量// 一个基得出一个解系,一个向量// 基的第一个为1的坐标List equations = new ArrayList<>();for (int j = 0; j < rank; j++) {if (mustZeroLines.contains(j)) {continue;}T[] equation = this.newArray(n - x + 1);Arrays.fill(equation, this.zeroValue());// 这个向量代入原方程// 比如1 2 3 4 5.其中2列非得为9T[] freeColumns = removeMustZero(ts[j], mustZeroColumns);System.arraycopy(freeColumns, 0, equation, 0, freeColumns.length);equations.add(equation);}// 首先代入非零行// 然后是第一个为1,其余为0for (int j = 0; j < n - rank; j++) {T[] equation = this.newArray(n - x + 1);Arrays.fill(equation, this.zeroValue());equation[j]=this.oneValue();if (j == baseIndex) {equation[n - x]=this.oneValue();} else {equation[n - x]=this.zeroValue();}equations.add(equation);}final T[][] array = equations.toArray(this.newArray(n - x, n - x + 1));final Matrix matrix = this.createMatrix(array);T[] solution = matrix.solution().get(0);// 这个解还需要拼接必须为0的列final T[] result = this.newArray(n);for (int i=0,j=0;iif (mustZeroColumns.contains(i)) {result[i] = this.zeroValue();} else {result[i]= solution[j++];}}return result;}private T[] removeMustZero(T[] line, List mustZero) {List ts = new ArrayList<>();for (int i = 0; i < line.length; i++) {if (!mustZero.contains(i)) {ts.add(line[i]);}}return ts.toArray(this.newArray(line.length - mustZero.size()));}
完整代码在https://e.coding.net/buildt/math/matrix.git。
package com.youngthing.matrix.test.image;import com.youngthing.matrix.DoubleMatrix;
import org.junit.Test;import java.util.Arrays;
import java.util.List;/*** 11/18/2022 9:43 PM 创建** @author 花书粉丝*/
public class HomogeneousTest {@Testpublic void homogeneousTest() {printSolution(new Double[][]{{3d, 2d, 1d, },{0d, 0d, 0d,},{0d, 0d, 0d}});printSolution(new Double[][]{{3d, 2d, 1d, 1d},{0d, 1d, 0d, 0d},{0d, 0d, 1d, 0d},{0d, 0d, 0d, 1d}});printSolution(new Double[][]{{3d, 2d, 1d, },{0d, 1d, 0d,},{0d, 0d, 0d}});printSolution(new Double[][]{{3d, 2d, 1d, 1d},{0d, 0d, 0d, 0d},{0d, 0d, 0d, 0d},{0d, 0d, 0d, 0d}});}private void printSolution(Double[][] matrix) {final List solution = new DoubleMatrix(matrix).solution();System.out.println("基为:");for (Double[] x : solution) {System.out.println(Arrays.toString(x));}}}
测试结果完全正确:
基为:
[1.0, 0.0, -3.0]
[0.0, 1.0, -2.0]
基为:
[0.0, 0.0, 0.0, 0.0]
基为:
[1.0, 0.0, -3.0]
基为:
[1.0, 0.0, -0.0, -3.0]
[0.0, 1.0, -0.0, -2.0]
[0.0, 0.0, 1.0, -1.0]