向量
- 线性代数是从研究一个数拓展到一组数
- 一组数的基本表示方法——向量(Vector)
- 向量是线性代数研究的基本元素
- 一组数的作用:最基本的出发点:表示方向
在二维空间中,不同的方向表示不同的终点,比如同样走5000米
它的终点是(4,3)
而它的终点是(4.6,1)
在三维空间中也是如此
在线性代数的世界里,起始点不重要
在这个图中,从(-1,-1)到(3,2)和从(0,0)到(4,3)是一样的。它们只是坐标系不同而已。
为了研究方便,我们定义向量都从原点起始,但是顺序是重要的,很显然(4,3)和(3,4)是不同的。
向量是一组有序的数。
- 如果只是表示方向,最多三个维度就够了,但为了扩展向量所能表达的范围,更加抽象的:n维向量。比如一个房子的维度
面积 卧室 卫生间 最近地铁站(km) 价格(万)
120 3 2 2 666
所以这是一个五维的向量(120,3,2,2,666),此时向量就是一组数,这组数的含义由使用者定义。
- 两个视角看似不同,但可以互相转换,比如房子的这个向量,我们可以想象它是一个五维空间的一个终点。
- 而一个方向就是一个点
- 空间中的一个点,可以看作从原点指向这个点的一个方向
- 在学习初始,使用方向的视角,更直观,更形象。
- 更关键的是:这两个视角,都不是简单的"一组数"
- 一个是一个有向线段
- 一个是空间中的点
更严格一些的定义
- 和向量相对应,一个数字,称为标量
- 代数,用符号代表数。和标量相区别,向量的符号画箭头:
- 个别情况下,尤其是几何学中,我们会考虑向量的起始点
行向量和列向量 (3,4)
通常教材,论文,提到向量,都指列向量。由于横版印刷,使用符号:,这个T是一个运算符号,把这个向量进行转置的操作。
向量的类,因为向量就是一组数,所以我们用数组来表示。
接口
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); }
实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); } }
运行结果
Vector{values=[5, 2]}
2
5
Python代码
class Vector: def __init__(self,lst): self._values = lst def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5,2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0],vec[1]))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
向量的基本运算
- 向量加法
向量的加法类似于我们中学物理中的矢量的平行四边形法则,它相当于先向(5,2)的点走一段距离,再从终点向(2,5)的方向再走一段距离。最终得到的地点就是(7,7)。
所以一个二维向量的加法就是
三维向量同理
N维向量同理
- 数量乘法
向量的乘法其实就是在向量的同一个方向上再移动乘数倍的距离,比如这里是2倍,就会移动2倍距离,得到终点为(10,4)
所以向量乘法就是
N维向量同理
在编码中,我们需要扩展Attribute接口
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); }
新增一个Addr的接口,不过该接口没有实现类,而是在实际编码中去编写匿名实现类
public interface Addr<T extends Number> { T zero(); T add(T lhs,T rhs); T mul(T k,T hs); }
最后是向量的实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } }; vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); } }
运行结果
Vector{values=[5, 2]}
2
5
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Python代码
class Vector: def __init__(self, lst): self._values = lst def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
向量运算的基本性质
加法交换律
加法结合律
乘法分配律
乘法交换律
零向量
对于任意一个向量,都存在一个向量O,满足
+ O =
,根据向量的加法,我们可以推导出
由此我们可以得到零向量的各个维度的值都是0.并且零向量O没有箭头。
同此,我们可以推导,对于任意一个向量,都存在一个向量
,满足
+
= O。上述
唯一。
我们给出一个创建零向量的静态方法
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } }; vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); } }
运行结果
Vector{values=[5, 2]}
2
5
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
Python代码
class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2)
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
向量的模
=(3,4)
的大小是多少?
根据勾股定理,的大小=
=5
我们用下面的符号(向量两边加双竖线),来表示向量的长度,我们称该长度为向量的模
在三维空间中,,我们首先知道
,则
,则最终得到
N维向量同理
我们来扩展Attribute接口,增加一个求模的方法
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); /** * 向量的模 * @return */ double norm(Addr<T> addr); }
Addr中增加一个平方和开方的方法
public interface Addr<T extends Number> { T zero(); T add(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); }
向量实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } @Override public double norm(Addr<T> addr) { T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.square(this.get(i))); } return addr.prescription(total); } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } }; vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); System.out.println(vector.norm(addr)); } }
运行结果
Vector{values=[5, 2]}
2
5
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
36.24913792078372
Python代码
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2) print("norm({}) = {}".format(vec,vec.norm()))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
norm((5, 2)) = 5.385164807134504
单位向量(unit vector)
就是向量的模为1的向量,所以每一个向量都有一个单位向量
对于一个N维向量来说,它的单位向量就是
,意思就是说对向量每一个维度的值去除以该向量的模,即为该向量的单位向量,这里我们需要记得,单位向量的模为1
,一般来说,单位向量只表示方向。我们把根据
求出
的过程称为归一化,规范化(normalize)
图中蓝色的部分就是(3,4)这个向量的单位向量
单位向量有无数个,比如在二维空间中
我们以原点为圆心,1为半径画一个圆,那么这个圆中的所有点都是一个单位向量。
在二维空间中有两个特殊的单位向量,如下图中蓝线所示
=(1,0)
=(0,1),像这种只由0、1组成的单位向量,又叫标准单位向量(Standard Unit Vector),标准单位向量指向坐标轴的正方向。
在三维空间中有三个标准单位向量
=(1,0,0)
=(0,1,0)
=(0,0,1)
N维空间中,有n个标准单位向量
=(1,0,...,0)
=(0,1,...,0) ...
=(0,0,...,1)
现在我们来扩展Attribute接口,新增一个单位向量的方法。
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); /** * 向量的模 * @return */ double norm(Addr<T> addr); /** * 单位向量 * @return */ Attribute<Double> normalize(Addr<T> addr); }
addr中增加一个除法方法
public interface Addr<T extends Number> { T zero(); T add(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); }
向量实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } @Override public double norm(Addr<T> addr) { T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.square(this.get(i))); } return addr.prescription(total); } @Override public Attribute<Double> normalize(Addr<T> addr) { Double[] newValues = new Double[this.len()]; double norm = this.norm(addr); for (int i = 0; i < this.len(); i++) { newValues[i] = addr.div(this.get(i),norm); } return new Vector<>(newValues); } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } }; Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } }; vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); System.out.println(vector.norm(addr)); Attribute<Double> unitVector = vector.normalize(addr); System.out.println(unitVector); System.out.println(unitVector.norm(addr1)); } }
运行结果
Vector{values=[5, 2]}
2
5
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
36.24913792078372
Vector{values=[0.9103664774626047, 0.413802944301184]}
0.9999999999999999
Python代码
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def normalize(self): # 返回向量的单位向量 return Vector([e / self.norm() for e in self]) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2) print("norm({}) = {}".format(vec, vec.norm())) print("normalize {} is {}".format(vec, vec.normalize())) print(vec.normalize().norm())
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
norm((5, 2)) = 5.385164807134504
normalize (5, 2) is (0.9284766908852594, 0.3713906763541037)
1.0
向量的点乘
两个向量相乘
我们先给出它的计算公式,然来再来说明
由结果,我们可以看出,两个向量相乘,其结果是一个数(标量)。更严格的说法,这是两个向量的点乘,又或者叫两个向量的内积。
实际上,这个结果又,即这两个向量的模乘以它们夹角的余弦。
我们在二维空间中来说明这个结论
在二维空间中,上述式子就转化成如下
我们假设它是这个样子的,由之前所知,是
与绿色向量的对角线,满足平行四边形法则,所以
是
+绿色向量,所以绿色向量=
,根据余弦定理,就有了
将式子调整一下就有了
根据模的计算方法,等式的右边展开,就有了
如此,我们就得到了上面的结论
在N维向量中,该等式依然成立
- 几何意义
我们来看一下的几何意义
我们以向
方向上做一个投影,它的长度就是
。这样一来,我们就可以把等式的右边以这样来看待
,也就是说向量的点乘就是
在
方向上投影的大小乘以
的大小,即让两个向量都指向同一个方向,在同一个方向上大小的乘积。
同理,我们也可以用在
的方向上做投影,为
,换句话说,等式的右边以这样来看待
我们也可以将、
分别投影到x轴和y轴上
排列组合后会得到4种情况
只有同方向上的投影相乘才是有意义的,而垂直方向上,它们彼此没有一点贡献,则为0.所以最终就只有和
。
我们对Attribute接口进行一个扩展,新增一个点乘的方法。
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); /** * 向量的模 * @return */ double norm(Addr<T> addr); /** * 单位向量 * @return */ Attribute<Double> normalize(Addr<T> addr); /** * 向量的点乘 * @param another * @param addr * @return */ T dot(Attribute<T> another,Addr<T> addr); }
向量实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } @Override public double norm(Addr<T> addr) { T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.square(this.get(i))); } return addr.prescription(total); } @Override public Attribute<Double> normalize(Addr<T> addr) { Double[] newValues = new Double[this.len()]; double norm = this.norm(addr); for (int i = 0; i < this.len(); i++) { newValues[i] = addr.div(this.get(i),norm); } return new Vector<>(newValues); } @Override public T dot(Attribute<T> another, Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("点乘错误,两个向量的维度必须相等"); } T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.mul(this.get(i),another.get(i))); } return total; } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } }; Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } }; //(5,2)*(6,3) System.out.println(vector.dot(vector1,addr)); //(5,2)+(6,3) vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); System.out.println(vector.norm(addr)); Attribute<Double> unitVector = vector.normalize(addr); System.out.println(unitVector); System.out.println(unitVector.norm(addr1)); } }
运行结果
Vector{values=[5, 2]}
2
5
36
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
36.24913792078372
Vector{values=[0.9103664774626047, 0.413802944301184]}
0.9999999999999999
Python代码
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def normalize(self): # 返回向量的单位向量 return Vector([e / self.norm() for e in self]) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def dot(self, another): # 向量点乘,返回结果标量 assert len(self) == len(another), \ "点乘错误,两个向量的维度必须相等" return sum(a * b for a, b in zip(self, another)) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2) print("norm({}) = {}".format(vec, vec.norm())) print("normalize {} is {}".format(vec, vec.normalize())) print(vec.normalize().norm()) print(vec.dot(vec2))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
norm((5, 2)) = 5.385164807134504
normalize (5, 2) is (0.9284766908852594, 0.3713906763541037)
1.0
36
向量点乘的应用
由该公式,我们可以得出两个向量之间的夹角的余弦等于两个向量的点乘除以它们模的积
如果,
。
反之,则两个向量是垂直的。知道两个向量是否是垂直的,是一件非常重要的事情。
如果,两个向量夹角为锐角。
如果,两个向量夹角为钝角。
计算两个向量的夹角
Attribute新增两个向量的夹角的方法
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); /** * 向量的模 * @return */ double norm(Addr<T> addr); /** * 单位向量 * @return */ Attribute<Double> normalize(Addr<T> addr); /** * 向量的点乘 * @param another * @param addr * @return */ T dot(Attribute<T> another,Addr<T> addr); /** * 向量的夹角 * @param another * @param addr * @return */ double degree(Attribute<T> another,Addr<T> addr); }
Addr新增反余弦和度数解析方法
public interface Addr<T extends Number> { T zero(); T add(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); double acos(double hs); double toDegree(double hs); }
向量实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } @Override public double norm(Addr<T> addr) { T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.square(this.get(i))); } return addr.prescription(total); } @Override public Attribute<Double> normalize(Addr<T> addr) { Double[] newValues = new Double[this.len()]; double norm = this.norm(addr); for (int i = 0; i < this.len(); i++) { newValues[i] = addr.div(this.get(i),norm); } return new Vector<>(newValues); } @Override public T dot(Attribute<T> another, Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("点乘错误,两个向量的维度必须相等"); } T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.mul(this.get(i),another.get(i))); } return total; } @Override public double degree(Attribute<T> another, Addr<T> addr) { T dot = this.dot(another,addr); double selfNorm = this.norm(addr); double anotherNorm = another.norm(addr); double productNorm = selfNorm * anotherNorm; return addr.toDegree(addr.acos(addr.div(dot,productNorm))); } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; //(5,2)*(6,3) System.out.println(vector.dot(vector1,addr)); //(5,2),(6,3)的夹角 System.out.println(vector.degree(vector1,addr)); //(5,2)+(6,3) vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); System.out.println(vector.norm(addr)); Attribute<Double> unitVector = vector.normalize(addr); System.out.println(unitVector); System.out.println(unitVector.norm(addr1)); } }
运行结果
Vector{values=[5, 2]}
2
5
36
4.763641690726143
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
36.24913792078372
Vector{values=[0.9103664774626047, 0.413802944301184]}
0.9999999999999999
Python代码
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def degree(self, another): # 返回向量的夹角 return math.degrees(math.acos(self.dot(another) / (self.norm() * another.norm()))) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def normalize(self): # 返回向量的单位向量 return Vector([e / self.norm() for e in self]) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def dot(self, another): # 向量点乘,返回结果标量 assert len(self) == len(another), \ "点乘错误,两个向量的维度必须相等" return sum(a * b for a, b in zip(self, another)) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2) print("norm({}) = {}".format(vec, vec.norm())) print("normalize {} is {}".format(vec, vec.normalize())) print(vec.normalize().norm()) print(vec.dot(vec2)) print(vec.degree(vec2))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
norm((5, 2)) = 5.385164807134504
normalize (5, 2) is (0.9284766908852594, 0.3713906763541037)
1.0
36
4.763641690726144
在N维向量的标准单位向量一定是两两互相垂直的
在二维空间中
=(1,0)
=(0,1)
=0
三维空间中
=(1,0,0)
=(0,1,0)
=(0,0,1)
=0
=0
=0
判断两个向量的相似程度(推荐系统)
在推荐系统中针对一组物品进行推荐,这组物品有多个维度的属性,我们把这组物品的属性组合成一个多维空间的向量。如果你在一个系统中留下了足迹,那么系统就会根据你的喜好,把相似的属性的物品(多维空间向量)推荐给你。当然这个是推荐系统最基本的理论——余弦相似度,现在成熟的推荐系统要比这个复杂的多,但是这个是最基本的理论基础。
两个物品的多维属性形成的向量的夹角如果是锐角,我们就说这两个物品相似
相似
两个物品的多维属性形成的向量的夹角如果是直角,我们就说这两个物品无关
无关
两个物品的多维属性形成的向量的夹角如果是钝角,我们就说这两个物品背离
背离
在几何计算中,我们想知道一个向量在另一个向量中的投影的坐标
我们知道投影到
上的长度为
,定义为d
则
之前我们知道单位向量只表示方向,它的模为1,则的单位向量为
,则投影点的坐标为
扩展Attribute接口,新增一个向量映射的方法
public interface Attribute<T extends Number> { /** * 向量的长度 * @return */ int len(); /** * 获取向量的元素 * @param index 索引 * @return */ T get(int index); /** * 向量相加 * @param another * @return */ Attribute<T> add(Attribute<T> another,Addr<T> addr); /** * 向量乘以一个数 * @param k * @param addr * @return */ Attribute<T> mul(T k,Addr<T> addr); /** * 向量的模 * @return */ double norm(Addr<T> addr); /** * 单位向量 * @return */ Attribute<Double> normalize(Addr<T> addr); /** * 向量的点乘 * @param another * @param addr * @return */ T dot(Attribute<T> another,Addr<T> addr); /** * 向量的夹角 * @param another * @param addr * @return */ double degree(Attribute<T> another,Addr<T> addr); /** * 其他向量在当前向量的投影 * @param another * @param addr * @return */ Attribute<Double> project(Attribute<T> another,Addr<T> addr); }
向量实现类
public class Vector<T extends Number> implements Attribute<T> { private T[] values; public Vector(T[] values) { this.values = values; } @SuppressWarnings("unchecked") public static <T extends Number> Attribute<T> zero(int dim,Addr<T> addr) { T[] values = (T[])new Number[dim]; Arrays.fill(values,addr.zero()); return new Vector<>(values); } @Override public String toString() { return "Vector{" + "values=" + Arrays.toString(values) + '}'; } @Override public int len() { return values.length; } @Override public T get(int index) { if (index >= len() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return values[index]; } @Override @SuppressWarnings("unchecked") public Attribute<T> add(Attribute<T> another,Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("加法错误,两个向量的维度必须相等"); } T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { List<T> list = new ArrayList<>(); list.add(this.get(i)); list.add(another.get(i)); newValues[i] = sumValue(list, addr); } this.values = newValues; return this; } @Override @SuppressWarnings("unchecked") public Attribute<T> mul(T k, Addr<T> addr) { T[] newValues = (T[])new Number[this.len()]; for (int i = 0; i < this.len(); i++) { newValues[i] = addr.mul(k,this.get(i)); } this.values = newValues; return this; } @Override public double norm(Addr<T> addr) { T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.square(this.get(i))); } return addr.prescription(total); } @Override public Attribute<Double> normalize(Addr<T> addr) { Double[] newValues = new Double[this.len()]; double norm = this.norm(addr); for (int i = 0; i < this.len(); i++) { newValues[i] = addr.div(this.get(i),norm); } return new Vector<>(newValues); } @Override public T dot(Attribute<T> another, Addr<T> addr) { if (this.len() != another.len()) { throw new IllegalArgumentException("点乘错误,两个向量的维度必须相等"); } T total = addr.zero(); for (int i = 0; i < this.len(); i++) { total = addr.add(total,addr.mul(this.get(i),another.get(i))); } return total; } @Override public double degree(Attribute<T> another, Addr<T> addr) { T dot = this.dot(another,addr); double selfNorm = this.norm(addr); double anotherNorm = another.norm(addr); double productNorm = selfNorm * anotherNorm; return addr.toDegree(addr.acos(addr.div(dot,productNorm))); } @Override public Attribute<Double> project(Attribute<T> another, Addr<T> addr) { T dot = this.dot(another,addr); double selfNorm = this.norm(addr); Attribute<Double> unitVector = this.normalize(addr); double d = addr.div(dot, selfNorm); return mul(d,unitVector); } private Attribute<Double> mul(double d,Attribute<Double> v) { Double[] values = new Double[v.len()]; for (int i = 0; i < v.len(); i++) { values[i] = d * v.get(i); } return new Vector<>(values); } private T sumValue(List<T> list, Addr<T> addr) { T total = addr.zero(); for (T n : list) { total = addr.add(total,n); } return total; } public static void main(String[] args) { Attribute<Integer> vector = new Vector<>(new Integer[]{5,2}); System.out.println(vector.toString()); System.out.println(vector.len()); System.out.println(vector.get(0)); Attribute<Integer> vector1 = new Vector<>(new Integer[]{6,3}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; //(5,2)*(6,3) System.out.println(vector.dot(vector1,addr)); //(5,2),(6,3)的夹角 System.out.println(vector.degree(vector1,addr)); //(6,3)在(5,2)上的映射 System.out.println(vector.project(vector1,addr)); //(5,2)+(6,3) vector.add(vector1, addr); System.out.println(vector); vector.mul(3,addr); System.out.println(vector); System.out.println(Vector.zero(4,addr)); System.out.println(vector.norm(addr)); Attribute<Double> unitVector = vector.normalize(addr); System.out.println(unitVector); System.out.println(unitVector.norm(addr1)); } }
运行结果
Vector{values=[5, 2]}
2
5
36
4.763641690726143
Vector{values=[6.20689655172414, 2.4827586206896552]}
Vector{values=[11, 5]}
Vector{values=[33, 15]}
Vector{values=[0, 0, 0, 0]}
36.24913792078372
Vector{values=[0.9103664774626047, 0.413802944301184]}
0.9999999999999999
Python代码
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def project(self, another): # 其他向量在当前向量的投影 return self.normalize() * (self.dot(another) / self.norm()) def degree(self, another): # 返回向量的夹角 return math.degrees(math.acos(self.dot(another) / (self.norm() * another.norm()))) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def normalize(self): # 返回向量的单位向量 return Vector([e / self.norm() for e in self]) def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def dot(self, another): # 向量点乘,返回结果标量 assert len(self) == len(another), \ "点乘错误,两个向量的维度必须相等" return sum(a * b for a, b in zip(self, another)) def __mul__(self, k): # 返回数量乘法的结果向量 return Vector([k * e for e in self]) def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
from playLA.Vector import Vector if __name__ == "__main__": vec = Vector([5, 2]) print(vec) print(len(vec)) print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1])) vec2 = Vector([6, 3]) print("{} + {} = {}".format(vec, vec2, vec + vec2)) print("{} * {} = {}".format(vec, 3, vec * 3)) zero2 = Vector.zero(4) print(zero2) print("norm({}) = {}".format(vec, vec.norm())) print("normalize {} is {}".format(vec, vec.normalize())) print(vec.normalize().norm()) print(vec.dot(vec2)) print(vec.degree(vec2)) print(vec.project(vec2))
运行结果
(5, 2)
2
vec[0] = 5, vec[1] = 2
(5, 2) + (6, 3) = (11, 5)
(5, 2) * 3 = (15, 6)
(0, 0, 0, 0)
norm((5, 2)) = 5.385164807134504
normalize (5, 2) is (0.9284766908852594, 0.3713906763541037)
1.0
36
4.763641690726144
(6.20689655172414, 2.4827586206896552)
Numpy中向量的基本使用
import numpy as np if __name__ == "__main__": print(np.__version__) # 在Python中,列表可以任意更改元素类型 lst = [1, 2, 3] lst[0] = "早上好" print(lst) # 在numpy中则不可以 vec = np.array([1, 2, 3]) print(vec) # numpy中的向量可以修改元素内容 # vec[0] = 666 print(vec) # 创建零向量 print(np.zeros(4)) print(np.ones(4)) print(np.full(4, 666)) # 获取向量的长度 print("size =", vec.size) print("size =", len(vec)) # 获取向量中的元素 print(vec[0]) # 获取向量最后一个元素 print(vec[-1]) # 获取向量中的一段元素,返回一个新向量 print(vec[0: 2]) print(type(vec[0: 2])) vec2 = np.array([4, 5, 6]) # 向量相加 print("{} + {} = {}".format(vec, vec2, vec + vec2)) # 向量相减 print("{} - {} = {}".format(vec, vec2, vec - vec2)) # 向量的数量乘 print("{} * {} = {}".format(2, vec, 2 * vec)) print("{} * {} = {}".format(vec, 2, vec * 2)) # 向量与向量的*号乘法,本身没有数学意义 print("{} * {} = {}".format(vec, vec2, vec * vec2)) # 向量与向量的点乘 print("{}.dot({}) = {}".format(vec, vec2, vec.dot(vec2))) # 向量的模 print(np.linalg.norm(vec)) # numpy中没有实现单位向量,需要自己实现 print(vec / np.linalg.norm(vec)) # 单位向量的模 print(np.linalg.norm(vec / np.linalg.norm(vec)))
运行结果
1.18.1
['早上好', 2, 3]
[1 2 3]
[1 2 3]
[0. 0. 0. 0.]
[1. 1. 1. 1.]
[666 666 666 666]
size = 3
size = 3
1
3
[1 2]
<class 'numpy.ndarray'>
[1 2 3] + [4 5 6] = [5 7 9]
[1 2 3] - [4 5 6] = [-3 -3 -3]
2 * [1 2 3] = [2 4 6]
[1 2 3] * 2 = [2 4 6]
[1 2 3] * [4 5 6] = [ 4 10 18]
[1 2 3].dot([4 5 6]) = 32
3.7416573867739413
[0.26726124 0.53452248 0.80178373]
1.0
矩阵
像这样排成行列的一组数,我们称之为矩阵。向量对数的拓展,一个向量表示一组数。矩阵是对向量的拓展,一个矩阵表示一组向量。
根据看待的视角不同,我们可以把矩阵以行、列来划分成不同的向量
对于这样的一个矩阵,我们可以把它称为一个4 * 4矩阵,因为它行数为4,列数为4
对于这样一个矩阵,我们则称它为3 * 4矩阵,因为它行数为3,列数为4.
对于行数 = 列数的矩阵,有一个特殊的名字——方阵,方阵有很多特殊的性质,有很多特殊的矩阵是方阵。
我们通常用A来代表一个矩阵,而矩阵中的元素用来表示,其中i表示第i行,j表示第j列。
矩阵在不同的应用中,可以表示不同的意义。比如这张成绩表就可以看成一个矩阵,它的行向量是一个一个学生的各个科目的成绩。而它的列向量则是某一个具体科目的所有学生成绩。
现在我们来实现矩阵类,先定义一个接口
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>{ private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } public static void main(String[] args) { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(2)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(0)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
3
1
Vector{values=[3, 4, 5]}
Vector{values=[1, 3]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
矩阵的基本运算
类比于向量的基本运算,矩阵的基本运算也包括矩阵的加法A + B,矩阵的数量乘法
对于矩阵A+B
则有
而矩阵的数量乘法
假设有一个矩阵如下
则它乘以2之后为
它们的几何意义为
P即为二维平面的一个三角形,而2P则是将该三角形进行了一次缩放。矩阵的运算在计算机图形上有很多的应用。
矩阵的基本运算性质
加法交换律 A + B = B + A
加法结合律 (A + B) + C = A + (B + C)
存在矩阵O,满足 A + O = A
存在矩阵-A,满足 A + (-A) = O,-A唯一,-A = - 1 * A
乘法结合律 (ck)A = c(kA)
乘法分配律 k(A + B) = kA + kB
(c + k)A = cA + kA
扩充Ranks接口
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); }
addr增加减法运算
public interface Addr<T extends Number> { T zero(); T add(T lhs,T rhs); T sub(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); double acos(double hs); double toDegree(double hs); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override public Matrix clone() throws CloneNotSupportedException { return (Matrix)super.clone(); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; Ranks<Integer> copy = ((Matrix)matrix).clone(); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3)))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
矩阵和向量的乘法
矩阵可以表示一个系统
比如在经济系统中,对IT、电子、矿产、房产的投入分别为、
、
、
它们满足这样一个方程组
这里面每一个方程的意思为,如果我要发展一个行业,首先需要基础投资多少钱,比如要发展IT业,首先要基础投资100万亿,但是电子业对IT业有影响,为了发展IT业,需要对电子业多投资20%,需要对矿产业多投资10%,需要对房产业多投资50%。其他的方程的意思类同。这里是一个举例,不代表实际意义。
上面的方程组可以转化成下面这种形式
类似于上面这种方程组,我们称为线性方程组,线性方程组在各个领域,有着重要的作用。而这样的系统,在线性代数中,称为线性系统。
在上面的线形方程组中,我们将每一个未知数的系数提取出来,形成一个矩阵,就有了如下的矩阵
,而右侧部分的数提取出来,就形成了一个列向量
我们将该线形方程组的所有的未知数也全部提取出来,形成一个列向量
那么这个线形方程组,就等价于下面这种形式,系数矩阵乘以未知数列向量等于常数列向量
上述这个式子可以表示为,其中A为矩阵,
是一个向量,
是一个向量。
因为该式子也之前的线形方程组等价,所以,该等式的左边可以用这样一个向量来表示
我们应该注意,这是一个向量,而不是一个矩阵。则我们可以得到如下的形式
根据这个等式,我们可以看出,矩阵与向量相乘
则是矩阵中的每一个行向量与向量进行点乘得到一个新的向量。我们之前知道,一个向量与一个向量进行点乘,得到的是一个标量,那么多个向量与一个向量进行点乘,就得到多个标量,再将这多个标量就可以组成一个向量。则我们得到一个法则
矩阵与向量相乘,就是该矩阵的行向量与该向量的点乘,得到一个新的向量。
转换成具体的公式,则为
我们知道向量与向量相乘,就是各个维度的分量值相乘再相加,但前提条件是这两个向量的长度必须向等。换成矩阵来说,就是矩阵的列数必须与向量的长度相等。即矩阵A的列数必须和向量u的元素个数一致。但矩阵A的行数没有限制。
换一种形式即为
这里需要说明的是,这里是从矩阵的行视角来看待矩阵的乘法的,后续会从矩阵的列视角来看待这一问题。
从来看,实际上就是矩阵A将向量x转换成了向量b,可以把矩阵理解成向量的函数。函数f(x)=b就是将x转换成b,而这里是向量x转换成了向量b。
首先,我们来扩展Ranks接口,新增一个矩阵与向量的点乘
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override public Matrix clone() throws CloneNotSupportedException { return (Matrix)super.clone(); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{23,65,79}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Vector{values=[390, 724]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec)))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
矩阵和矩阵的乘法
我们之前在说矩阵的数量乘法的时候,说到
则它乘以2之后为
但如果让每个点的横坐标扩大1.5倍,纵坐标扩大2倍
对于P中的每一个点其实是一个向量,只不过我们以列向量的形式来表现它
,其中的x可能为0、4、5;y可能为0、0、3
如此我们需要将其转换为,由于矩阵是向量的一个函数,相当于我们要找到一个矩阵T满足
首先要满足矩阵与向量相乘,则T的列数一定与向量的长度相同,则T的列数一定是2。由于结果向量只有两行,所以T的行数也只有2.
由此我们可以得到下面这个式子,来满足矩阵乘以一个向量
因为结果为,由此我们可以很容易得出
最终我们可以得出
因为在矩阵P中有3个点,我们将每一个点都转成一个列向量,从而形成一个新的矩阵
这样一来,我们就可以让矩阵T与矩阵P相乘
来批量的把P中的点坐标变成另外一组点坐标
我们可以把P中的每一列都当成一个单独的向量,则对应的结果为
对应二维平面坐标中就是这个样子
由这个式子
我们可以看出矩阵和矩阵相乘,满足下面这个样子
矩阵和矩阵的乘法其实就是矩阵和向量乘法的批处理,由于矩阵和向量的乘法需要满足矩阵的列数必须和向量的长度相等,放在这里就是矩阵A的列数必须和矩阵B的行数相等。我们可以用这种形式来表示
其中的到
就是一个一个的列向量,结果也是由矩阵A与这一个一个列向量点乘后的新的列向量所形成的新的矩阵。
最终得到的公式就是
如果A是m * k的矩阵,B是k * n的矩阵,则结果矩阵为m * n的矩阵。
矩阵乘法不遵守交换律 ,因为它们很有可能根本不能相乘,即便可以相乘,结果也不一样。
扩展Ranks接口,新增一个矩阵乘以矩阵的方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override public Matrix clone() throws CloneNotSupportedException { return (Matrix)super.clone(); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{23,65,79}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Vector{values=[390, 724]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3)))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
矩阵乘法的性质
矩阵乘法不遵守交换律,但是矩阵乘法遵守下面的性质
乘法结合律
乘法分配律
因为矩阵乘法不遵守交换律,所以上面两条分配律是不等价的
对任意r*c的矩阵A,存在c*x的矩阵O,满足:,这里需要注意的是,虽然
与
都是零矩阵,但它们的形状是不同的。
对任意r*c的矩阵A,存在x*r的矩阵O,满足:
矩阵的幂
矩阵A的k次方,也就是k个A相乘。由于矩阵相乘有条件限制,所以这里要做到k个A能够相乘,则A必须是一个方阵,只有方阵才可以进行矩阵的幂运算。
同样,由于矩阵的乘法不满足交换律,则
它最多可以扩展到
而中间的不能合并成
矩阵的转置
之前我们说把变成
这个样子的情况,称为矩阵的转置。因为在很多情况下,我们拿到的矩阵很有可能不是列矩阵,而是第一种情况的行矩阵,而为了方便我们做矩阵乘法,我们需要将第一种情况的行矩阵给变成列矩阵。
而我们对进行转置,可以写成
矩阵的转置:行变成列,列变成行。即为,则
对于向量来说,有行向量和列向量
,一般我们说向量如果不特殊声明的话,都是指一个列向量。由于横版印刷的原因,使用符号:
。
现在我们给Ranks接口增加一个矩阵转置的方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); /** * 转置矩阵 * @return */ Ranks<T> trans(); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override public Matrix clone() throws CloneNotSupportedException { return (Matrix)super.clone(); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; System.out.println(matrix.trans()); Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{9,33,97}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[1, 3],[2, 4],[3, 5]]}
Vector{values=[366, 644]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) def trans(self): # 返回矩阵的转置矩阵 return Matrix([[e for e in self.col_vector(i)] for i in range(self.col_num())]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3))) print("matrix.trans = {}".format(matrix.trans()))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
matrix.trans = Matrix([[1, 3], [2, 4], [3, 5]])
矩阵转置的性质
矩阵A的转置的转置就等于A本身
两个矩阵相加后的转置等于分别对两个矩阵做转置后再相加
矩阵数量乘后的转置等于数量乘以转置后的矩阵
两个矩阵相乘后的转置等于第二个矩阵的转置乘以第一个矩阵的转置
A是m*k的矩阵,B是k*n的矩阵,则A的转置就是k*m的矩阵,B的转置就是n*k的矩阵,则AB是m*n的矩阵,AB的转置就是n*m的矩阵。则(B的转置)(A的转置)是n*m的矩阵,满足上面的式子。
Numpy中矩阵的基本操作
import numpy as np if __name__ == "__main__": A = np.array([[1, 2], [3, 4]]) print(A) # 矩阵的形状 print(A.shape) # 矩阵的转置 print(A.T) # 获取矩阵的元素 print(A[1, 1]) # 矩阵的行向量 print(A[0]) print(A[1, :]) # 矩阵的列向量 print(A[:, 0]) B = np.array([[5, 6], [7, 8]]) # 矩阵加减法 print(A + B) print(A - B) # 矩阵的数量乘 print(10 * A) print(A * 10) # 矩阵的*号乘法,没有数学意义 print(A * B) # 矩阵与矩阵的点乘 print(A.dot(B)) vec = np.array([10, 100]) # 矩阵与向量的加法,没有数学意义 print(A + vec) print(A + 1) # 矩阵与向量的点乘 print(A.dot(vec))
运行结果
[[1 2]
[3 4]]
(2, 2)
[[1 3]
[2 4]]
4
[1 2]
[3 4]
[1 3]
[[ 6 8]
[10 12]]
[[-4 -4]
[-4 -4]]
[[10 20]
[30 40]]
[[10 20]
[30 40]]
[[ 5 12]
[21 32]]
[[19 22]
[43 50]]
[[ 11 102]
[ 13 104]]
[[2 3]
[4 5]]
[210 430]
图形变换矩阵(向量的函数)
之前我们知道了在直角坐标系中,让横坐标扩大1.5倍,纵坐标扩大2倍,则该变换矩阵是
则可以得出,让每个点到横坐标扩大a倍,纵坐标扩大b倍
因为矩阵与一个向量相乘,可得
现在我们来看一下更多的图形变换矩阵
- 让每个点关于x轴翻转
换句话说,即,我们让
,代入进去,即
,这样我们就得到了
最终结果就是
- 让每个点关于y轴翻转
即最终结果为
- 让每个点关于原点翻转(x轴,y轴均翻转)
即最终结果为
我们也可以使用之前的结果来进行推导
沿x轴翻转的矩阵为
沿y轴翻转的矩阵为
则我们只需要把沿x轴翻转之后的点再沿y轴翻转就得到原点翻转的结果
- 沿x轴方向错切
即y轴不变,沿x轴进行变换,相当于,最终得到
- 沿y方向的错切
即x轴不变,沿y轴进行变换,相当于,最终得到
- 旋转
即坐标点沿着某个方向旋转了角,这道题的关键在于需要知道(x,y)旋转之后的坐标
即我们需要知道是多少?
由于一个向量旋转后,它的模是不变的(用d表示模),则我们有
最终可以得到
根据两个角的差的余弦公式,可得
根据两个角的差的正弦公式,可得
这样,我们就知道了旋转后的坐标
最终可得
Python代码实现
我们先画一个图形
import matplotlib.pyplot as plt if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10, 10) plt.ylim(-10, 10) plt.plot(x, y) plt.show()
它就是这个样子的
现在我们要将该图形沿着x轴扩大2倍,沿着y轴扩大1.5倍
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 T = Matrix([[2, 0], [0, 1.5]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图沿着x轴翻转
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 T = Matrix([[1, 0], [0, -1]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图沿着y轴翻转
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 # T = Matrix([[1, 0], [0, -1]]) # y轴翻转矩阵 T = Matrix([[-1, 0], [0, 1]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图沿着原点翻转
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 # T = Matrix([[1, 0], [0, -1]]) # y轴翻转矩阵 # T = Matrix([[-1, 0], [0, 1]]) # 完全翻转矩阵 T = Matrix([[-1, 0], [0, -1]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图沿着x轴错切
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 # T = Matrix([[1, 0], [0, -1]]) # y轴翻转矩阵 # T = Matrix([[-1, 0], [0, 1]]) # 完全翻转矩阵 # T = Matrix([[-1, 0], [0, -1]]) # x轴错切矩阵 T = Matrix([[1, 0.5], [0, 1]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图沿着y轴错切
import matplotlib.pyplot as plt from playLA.Matrix import Matrix if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 # T = Matrix([[1, 0], [0, -1]]) # y轴翻转矩阵 # T = Matrix([[-1, 0], [0, 1]]) # 完全翻转矩阵 # T = Matrix([[-1, 0], [0, -1]]) # x轴错切矩阵 # T = Matrix([[1, 0.5], [0, 1]]) # y轴错切矩阵 T = Matrix([[1, 0], [0.5, 1]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
现在我们让该图旋转
import matplotlib.pyplot as plt from playLA.Matrix import Matrix import math if __name__ == "__main__": points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]] x = [point[0] for point in points] y = [point[1] for point in points] plt.figure(figsize=(5, 5)) plt.xlim(-10,10) plt.ylim(-10,10) plt.plot(x,y) # plt.show() P = Matrix(points) # 缩放矩阵 # T = Matrix([[2, 0], [0, 1.5]]) # x轴翻转矩阵 # T = Matrix([[1, 0], [0, -1]]) # y轴翻转矩阵 # T = Matrix([[-1, 0], [0, 1]]) # 完全翻转矩阵 # T = Matrix([[-1, 0], [0, -1]]) # x轴错切矩阵 # T = Matrix([[1, 0.5], [0, 1]]) # y轴错切矩阵 # T = Matrix([[1, 0], [0.5, 1]]) # 旋转操作 # 60度 theta = math.pi / 3 T = Matrix([[math.cos(theta), math.sin(theta)], [-math.sin(theta), math.cos(theta)]]) P2 = T.dot(P.trans()) plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())], [P2.col_vector(i)[1] for i in range(P2.col_num())]) plt.show()
得到的图形如下
单位矩阵
在我们的图形变换矩阵中,有一种特殊的变换矩阵,它不会改变其他图形的任何形状,即让每个点的横坐标保持1倍,纵坐标也保持1倍。
则该矩阵类似于这个样子,当然这是在二维的平面直角坐标中
通常我们称这样的矩阵为单位矩阵
其中的2表示是二维的,而在三维空间中,单位矩阵则为
,同理在N维空间中单位矩阵就是
,又或者它可以记为
由以上可知,单位矩阵一定是方阵
单位矩阵的性质
则可得出
但是这里需要注意的是这个A不一定是一个方阵,如果A不为方阵,则上面的单位矩阵I的维度则不同,所以上面的等式并不是一个简单的乘法交换律,比如
如果要将式子左边的两个矩阵交换位置,则要满足矩阵乘法的条件(第一个矩阵的列数必须等于第二个矩阵的行数),需要变成
所以这个单位矩阵需要变成一个三维的单位矩阵,但结果依然不变
我们需要在Addr接口中增加一个给出1的方法
public interface Addr<T extends Number> { T zero(); T one(); T add(T lhs,T rhs); T sub(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); double acos(double hs); double toDegree(double hs); }
矩阵实现类,增加一个单位矩阵的静态方法
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } /** * 创建一个单位矩阵 * @param n * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> identity(int n,Addr<T> addr) { T[][] values = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(values[i],addr.zero()); } for (int i = 0; i < n; i++) { values[i][i] = addr.one(); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override public Matrix clone() throws CloneNotSupportedException { return (Matrix)super.clone(); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer one() { return 1; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } }; System.out.println(matrix.trans()); Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{9,33,97}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); Ranks<Integer> I = Matrix.identity(4, addr); System.out.println(I); Matrix<Integer> idenTest = new Matrix<>(new Integer[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}); System.out.println(idenTest.dot(I,addr)); System.out.println(I.dot(idenTest,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[1, 3],[2, 4],[3, 5]]}
Vector{values=[366, 644]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Matrix{values=[[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Python代码
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) @classmethod def identity(cls, n): # 返回一个n行n列的单位矩阵 m = [[0] * n for _ in range(n)] for i in range(n): m[i][i] = 1 return cls(m) def trans(self): # 返回矩阵的转置矩阵 return Matrix([[e for e in self.col_vector(i)] for i in range(self.col_num())]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3))) print("matrix.trans = {}".format(matrix.trans())) I = Matrix.identity(4) print(I) idenTest = Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) print(idenTest.dot(I)) print(I.dot(idenTest))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
matrix.trans = Matrix([[1, 3], [2, 4], [3, 5]])
Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
用矩阵表示空间
我们之前在矩阵与向量的点乘中,知道
矩阵与向量相乘,就是该矩阵的行向量与该向量的点乘,得到一个新的向量。例如
这其实是从行视角来看待的。但其实我们也可以通过列视角来看待矩阵与向量的点乘
它们二者其实是统一的,是同一个结果,只不过表达出来的形式是不同的。用图示来表示,即为
之前我们知道单位矩阵乘以一个矩阵依然为该矩阵本身,单位矩阵乘以向量依然如此,如
我们在之前的单位向量中知道,单位向量就是模为1的向量,在二维空间中,有两个标准单位向量
则上面的单位矩阵与向量相乘又可以表述为,这就是(x,y)这个点坐标的定义!在(1,0)轴上的分量为x;在(0,1)轴上的分量为y
这就是通过列向量视角最终得到的结果。
由于单位向量只表示方向,则我们可以把该单位矩阵与向量点乘的式子看成是定义出的空间坐标系的延展,其中代表一个方向,
代表一个方向共建了一个平面空间。由此可见,矩阵定义了两个坐标轴。现在我们以任意一个矩阵
来看待这个问题,同理,它也有两个列向量
,它同样可以交织构成一个平面空间,在平面直角坐标系中
是这样的。
现在我们用该矩阵点乘一个向量,则意味着矩阵中的两个列向量都沿着各自的方向放大两倍,最终得到
那么我们如何来理解这两个列向量交织的新的空间呢
我们将直角坐标系去掉,就变成了
而(2,2)这个点就是这个这个新空间坐标系中的红点
再把新空间的坐标转换成直角坐标系中的坐标。而之前的图形变换亦是如此,我们将变换矩阵想象成一个新的空间坐标系,就是将原图放入新的空间坐标系中的样子,只不过原图在原坐标系中的坐标原样不动的放入新空间坐标系中同样的坐标
比如图形沿着y轴翻转,它的变换矩阵为,该变换矩阵所组成的新的空间坐标系如下
则我们把原图中的坐标原封不动的放入新的坐标系就变成了这样
而错切的变换矩阵为,则它所交织的新的空间坐标系如下
同样,我们将原图坐标原封不动的放入新的坐标系中,就是这样
线性系统
什么是线性?未知数只能是一次方项,例如如下就是一个线性的方程
非线性方程:
因为这些方程在坐标系中的图像都不是直线,所以它们都不是线性的
而这样的方程得到的是一根直线,所以称为线性方程。
在二维的空间中,表现的是一根直线,而在三维的空间中则是一个平面
消元法
一个方程的左右两边同时乘以一个常数,一个方程加(减)另一个方程,交换两个方程的位置
高斯消元法
假设有这样一个方程组,根据之前的内容,我们可以知道,它可以等价于系数矩阵与未知数组成的向量相乘等于右边的常数向量
现在我们可以扔掉所有的未知数,而将常数向量也放入矩阵中
这样就形成了一个新的矩阵,我们称之为增广矩阵,我们所有的解方程的消元法的操作都可以在增广矩阵中完成。
首先我们可以用矩阵的第二个行向量减去第一个行向量乘以3,得到如下结果
再使用矩阵的第三个行向量减去第一个行向量乘以2,得到如下结果
再把矩阵的第三个行向量与第二个行向量相加,消去第三个行向的-1,得到如下结果
将矩阵的第三个行向量除以-15,得到结果如下
通过以上的过程,我们可以看出我们是在不断的消除第一个列向量中的元,我们称该元为主元
同样,第二个行向量的第二个元素,是第二行的主元,第三个行向量的第三个元素,是第三行的主元
所以我们每次消元的过程要首先确定每一行的主元是谁,就目前来看,其实第i行的主元就是第i个元素。而这种消元的方法就叫做高斯消元法。
但如果对于一个线性方程组的某一行没有主元该如何呢?
这里该方程组的增广矩阵为
由于第一个行向量没有主元,所以我们需要找到所有行中第一个元素最大的那个行向量交换位置,主要是为了尽量小的避免误差,就得到如下的形式,再去进行高斯消元法消元
再回来之前的例子,我们可以看到消元的目的是为了把所有的主元都变成1,而主元下面所有的元素都为0
现在这个增广矩阵实际上就相当于这样一个线性方程组
现在我们只要将所有行的除主元外的其他元素都变成0,就得到了该方程组的解。我们将矩阵的第二个行向量加上第三个行向量乘以10,就得到
此时我们将第一个行向量减去第三个行向量乘以4,得到
之后再将第一个行向量减去第二个行向量乘以2,得到
最终相当于
我们整个过程可以分为两部分来进行,第一部分为前向过程(从上到下)
- 选择最上的主元,化为1
- 主元下面的所有行减去主元所在行的某个倍数,使得主元下面所有元素都为0
第二部分为后向过程(从下到上)
- 选择最下的主元
- 主元上面的所有行减去主元所在行的某个倍数,使得主元上面的所有元素都为0
这两部分统称为高斯-约旦消元法,高斯负责了第一部分,约旦负责了第二部分
Addr接口增加相同类型的除法和比较大小的方法
public interface Addr<T extends Number> { T zero(); T one(); T add(T lhs,T rhs); T sub(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); T div(T hs,T nhs); double acos(double hs); double toDegree(double hs); int compair(T lhs,T rhs); }
Ranks接口增加set方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 设置矩阵中某个位置的元素 * @param pos * @param value */ void set(int[] pos,T value); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); /** * 转置矩阵 * @return */ Ranks<T> trans(); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } /** * 创建一个单位矩阵 * @param n * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> identity(int n,Addr<T> addr) { T[][] values = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(values[i],addr.zero()); } for (int i = 0; i < n; i++) { values[i][i] = addr.one(); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public void set(int[] pos, T value) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } values[pos[0]][pos[1]] = value; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Matrix clone() throws CloneNotSupportedException { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = this.values[i][j]; } } return new Matrix(values); } }
新增一个接口
public interface FuncGroup<T extends Number> { /** * 高斯-约旦消元法 * @param addr */ void gaussJordanElimination(Addr<T> addr); /** * 打印增广矩阵 */ void fancyPrint(); }
线性系统实现类
/** * 线性系统 * @param <T> */ public class LinearSystem<T extends Number> implements FuncGroup<T>{ //系数矩阵的行数 private int matrixRow; //系数矩阵的列数 private int matrixCol; //增广矩阵 @Getter private Ranks<T> Ab; @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A,Attribute<T> b) { if (A.rowNum() != b.len()) { throw new IllegalArgumentException("矩阵的行数必须等于向量的长度"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); if (matrixRow != matrixCol) { throw new IllegalArgumentException("要保证线性方程组具有唯一解"); } T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + 1]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j <= A.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(i); } } } Ab = new Matrix<>(values); } @Override public void gaussJordanElimination(Addr<T> addr) { forward(addr); backword(addr); } @Override public void fancyPrint() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < matrixCol + 1; j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i,j})); builder.append("\n"); } } } System.out.println(builder.toString()); } /** * 前向 * @param addr */ @SuppressWarnings("unchecked") private void forward(Addr<T> addr) { int n = matrixRow; for (int i = 0; i < n; i++) { //Ab[i][i]为主元 int maxRow = maxRow(i, n, addr); T[] temp = (T[]) new Number[matrixCol + 1]; //每一行无论是否有主元,都与主元最大值所在行进行交换 for (int j = 0; j < matrixCol + 1; j++) { temp[j] = Ab.get(new int[]{i, j}); Ab.set(new int[]{i, j}, Ab.get(new int[]{maxRow, j})); Ab.set(new int[]{maxRow, j}, temp[j]); } T u = Ab.get(new int[]{i, i}); //将主元归为一 for (int j = 0; j < matrixCol + 1; j++) { Ab.set(new int[]{i, j}, addr.div(Ab.get(new int[]{i, j}), u)); } //将主元所在行以下的行的所有非主元数据全部变成0 for (int k = i + 1; k < n; k++) { T u1 = Ab.get(new int[]{k, i}); for (int l = 0; l < matrixCol + 1; l++) { Ab.set(new int[]{k, l}, addr.sub(Ab.get(new int[]{k, l}), addr.mul(u1, Ab.get(new int[]{i, l})))); } } } } /** * 后向 */ private void backword(Addr<T> addr) { int n = matrixRow; for (int i = n - 1; i >= 0; i--) { //Ab[i][i]为主元 for (int j = i - 1; j >= 0; j--) { T u = Ab.get(new int[]{j, i}); for (int k = 0; k < matrixCol + 1; k++) { Ab.set(new int[]{j,k},addr.sub(Ab.get(new int[]{j,k}), addr.mul(u,Ab.get(new int[]{i,k})))); } } } } /** * 获取主元最大值所在的行 * @param index * @param n * @param addr * @return */ private int maxRow(int index,int n,Addr<T> addr) { T best = Ab.get(new int[]{index,index}); int ret = index; for (int i = index + 1; i < n; i++) { if (addr.compair(Ab.get(new int[]{i,index}),best) == 1) { best = Ab.get(new int[]{i,index}); ret = i; } } return ret; } public static void main(String[] args) { Ranks<Double> A = new Matrix<>(new Double[][]{{1.0,2.0,4.0},{3.0,7.0,2.0},{2.0,3.0,3.0}}); Attribute<Double> b = new Vector<>(new Double[]{7.0,-11.0,1.0}); FuncGroup<Double> ls = new LinearSystem<>(A,b); Addr<Double> addr = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double one() { return 1.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double sub(Double lhs, Double rhs) { return lhs - rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public Double div(Double hs, Double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Double lhs, Double rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } }; ls.gaussJordanElimination(addr); ls.fancyPrint(); } }
运行结果
1.0 0.0 0.0 | -0.9999999999999991
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 2.9999999999999996
Python代码
向量类增加部分方法
import math class Vector: def __init__(self, lst): self._values = lst @classmethod def zero(cls,dim): # 返回一个dim维的零向量 return cls([0] * dim) def project(self, another): # 其他向量在当前向量的投影 return self.normalize() * (self.dot(another) / self.norm()) def degree(self, another): # 返回向量的夹角 return math.degrees(math.acos(self.dot(another) / (self.norm() * another.norm()))) def norm(self): # 返回向量的模 return math.sqrt(sum(e**2 for e in self)) def normalize(self): # 返回向量的单位向量 return Vector([e / self.norm() for e in self]) def underlying_list(self): # 返回向量的底层列表 return self._values[:] def __add__(self, another): # 向量加法,返回结果向量 assert len(self) == len(another),\ "加法错误,两个向量的维度必须相等" return Vector([a + b for a, b in zip(self, another)]) def __sub__(self, another): # 向量减法,返回结果向量 assert len(self) == len(another), \ "减法错误,两个向量的维度必须相等" return Vector([a - b for a, b in zip(self, another)]) def __pos__(self): # 返回向量取正的结果向量 return 1 * self def __neg__(self): # 返回向量取负的结果向量 return -1 * self def dot(self, another): # 向量点乘,返回结果标量 assert len(self) == len(another), \ "点乘错误,两个向量的维度必须相等" return sum(a * b for a, b in zip(self, another)) def __mul__(self, k): # 返回数量乘法的结果向量:self * k return Vector([k * e for e in self]) def __rmul__(self, k): # 返回数量乘法的结果向量:k * self return self * k def __truediv__(self, k): # 返回数量除法的结果向量:self / k return (1 / k) * self def __iter__(self): # 返回向量的迭代器 return self._values.__iter__() def __getitem__(self, index): # 取向量的第index个元素 return self._values[index] def __len__(self): # 返回向量的长度 return len(self._values) def __repr__(self): return "Vector({})".format(self._values) def __str__(self): return "({})".format(", ".join(str(e) for e in self._values))
线性系统类
from .Vector import Vector class LinearSystem: def __init__(self, A, b): assert A.row_num() == len(b), "矩阵的行数必须等于向量的长度" self._matrix_row = A.row_num() self._matrix_col = A.col_num() assert self._matrix_row == self._matrix_col, "要保证线性方程组具有唯一解" self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]]) for i in range(self._matrix_row)] def _max_row(self, index, n): # 主元最大值所在的行 best, ret = self.Ab[index][index], index for i in range(index + 1, n): if self.Ab[i][index] > best: best, ret = self.Ab[i][index], i return ret def _forward(self): # 前向 n = self._matrix_row for i in range(n): # Ab[i][i]为主元 max_row = self._max_row(i, n) self.Ab[i], self.Ab[max_row] = self.Ab[max_row], self.Ab[i] # 将主元归为一 self.Ab[i] = self.Ab[i] / self.Ab[i][i] for j in range(i + 1, n): self.Ab[j] = self.Ab[j] - self.Ab[j][i] * self.Ab[i] def _backward(self): # 后向 n = self._matrix_row for i in range(n - 1, -1, -1): # Ab[i][i]为主元 for j in range(i - 1, -1, -1): self.Ab[j] = self.Ab[j] - self.Ab[j][i] * self.Ab[i] def gauss_jordan_elimination(self): # 高斯-约旦消元法 self._forward() self._backward() def fancy_print(self): # 打印增广矩阵 for i in range(self._matrix_row): print(" ".join(str(self.Ab[i][j]) for j in range(self._matrix_col)), end=" ") print("|", self.Ab[i][-1])
from playLA.Matrix import Matrix from playLA.Vector import Vector from playLA.LinearSystem import LinearSystem if __name__ == "__main__": A = Matrix([[1, 2, 4], [3, 7, 2], [2, 3, 3]]) b = Vector([7, -11, 1]) ls = LinearSystem(A, b) ls.gauss_jordan_elimination() ls.fancy_print()
运行结果
1.0 0.0 0.0 | -1.0
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 3.0
行最简形式和线性方程组解的结构
之前我们在代码中可以看到这样一段
if (matrixRow != matrixCol) { throw new IllegalArgumentException("要保证线性方程组具有唯一解"); }
但一个线性方程组未必具有唯一解,它可能有无数个解,也可能没有解。
我们来看这样一组线性方程组
则它的增广矩阵如下
我们用第二个行向量加上第一个行向量,第三个行向量减去第一个行向量乘以2得
再用第三个行向量加上第二个行向量,得
现在第三行表示,这显然是不可能的,此时这个线性方程组无解
我们再来看一组线性方程组
则它的增广矩阵如下
我们将第二个行向量加上第一个行向量,第三个行向量减去第一个行向量乘以3,得
我们将第二行进行归一操作,得
现在我们将第三个行向量加上第二个行向量,得
再将第一个行向量减去第二个行向量乘以2,得
现在这个增广矩阵表示的是
经过转化,最后变为
这就是该线性系统的解,z任意取值,都能得到一组x,y,z满足方程组,则方程组有无数组解。
无论一个线性方程组有唯一解,无解还是有无数组解,它们都满足一种阶梯型,称为阶梯型矩阵
如上面的三种情况,我们都可以绘制成阶梯形状
根据上图,我们能很清楚的看到,在阶梯的下层的元素都为0。而阶梯的上层与阶梯相邻的都是它的主元。非零行的第一个元素(主元)为1.主元所在列的其他元素均为0.而经过了高斯-约旦消元法之后得到的这个矩阵,我们称为——行最简形式(reduced row echelon form(RREF))
对于第一行来说,主元未必是第一个元素,而在下面的第i行来说,主元的位置也未必在第i列上
而以下的例子中则不是行最简形式
现在我们可以来总结一下方程组的解的各种情况
而满足上面三种情况,需要达到下面的条件
- 方程组有唯一解
系数矩阵非零行=未知数个数
- 方程组无解
系数矩阵非零行<行最简形式非零行(增广矩阵非零行)
- 方程组有无数解
系数矩阵非零行<未知数个数
在下面的增广矩阵中
我们可以判断出,该方程组是有无数解的,因为系数矩阵非零行3<未知数个数4
而
是有唯一解的,系数矩阵非零行3等于未知数个数3
更一般的线性系统求解
面对上面的不同解的情况,我们就需要调整方法。而高斯-约旦消元法的主要思想不变,但需要调整细节。
这是一个有四个方程,五个未知数的方程组,转化为增广矩阵如下
使用高斯-约旦消元法,我们将第二个行向量与第一个行向量相加,第三个行向量减去第一个行向量,第四个行向量加上第一个行向量乘以2,得到
这里第一行第一个1即为首元。这里我们可以看到,第一行下面,不仅是第一列变成0了,第二列也变成0了。按照之前的情况,如果第二行第二列的元素为0,则我们会向下找一个不为0的最大的元素进行行交换,来获取第二行的主元。但是很可惜,第二行之下第二列之下,所有元素都为0。此时就需要继续相后寻找第二行的主元,那就是第三列的2作为第二行的主元,再将该主元归一,得到
再将该主元下面的元素变成0.第三个行向量减去第二个行向量乘以2,第四个行向量加上第二个行向量,得到
跟之前的情况一样,我们在第三行第四列也找不到主元,于是我们继续向后寻找主元,便找到第三行第五列的3作为主元。再将该主元归一。
将第三行主元下面的元素变成0.第四个行向量减去第三个行向量乘以2,得到
我们可以看到,第四行已经全部变成了0,则第四行没有主元。现在我们要将最后一个主元上面的元素变成0,第一个行向量减去第三个行向量乘以3,第二个行向量加上第三个行向量,得到
此时我们需要将第二行的主元上面的元素化为0.第一个行向量减去第二个行向量乘以2,得到
此时我们的高斯-约旦消元法到此就结束了,得到了行最简形式。由之前的系数矩阵非零行3<未知数个数5可知,该方程组有无数个解
跟唯一解的线性系统不同,它不是所有的列都包含了主元,我们把含有主元的列叫做主元列,如这里的第一列、第三列、第五列为主元列。而不含有主元的列,称为自由列,如这里的第二列、第四列为自由列。而把该行最简形式化成方程组,则为
其中x、z、u是主元列的未知数,而y、w是自由列的未知数,而自由列的未知数可以任意取值,得到方程组的无数个解。
Addr接口增加判断两个元素相等的方法
public interface Addr<T extends Number> { T zero(); T one(); T add(T lhs,T rhs); T sub(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); T div(T hs,T nhs); double acos(double hs); double toDegree(double hs); int compair(T lhs,T rhs); boolean equals(T lhs,T rhs); }
FuncGroup接口修改高斯-约旦消元法方法,返回是否有解
public interface FuncGroup<T extends Number> { /** * 高斯-约旦消元法,并判断是否有解 * @param addr */ boolean gaussJordanElimination(Addr<T> addr); /** * 打印增广矩阵 */ void fancyPrint(); }
线性系统实现类
/** * 线性系统 * @param <T> */ public class LinearSystem<T extends Number> implements FuncGroup<T>{ //系数矩阵的行数 private int matrixRow; //系数矩阵的列数 private int matrixCol; //增广矩阵 @Getter private Ranks<T> Ab; //主元列 @Getter private List<Integer> pivots = new ArrayList<>(); @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A,Attribute<T> b) { if (A.rowNum() != b.len()) { throw new IllegalArgumentException("矩阵的行数必须等于向量的长度"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); // if (matrixRow != matrixCol) { // throw new IllegalArgumentException("要保证线性方程组具有唯一解"); // } T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + 1]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j <= A.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(i); } } } Ab = new Matrix<>(values); } @Override public boolean gaussJordanElimination(Addr<T> addr) { forward(addr); backword(addr); //无主元的行的非系数元素如果为0则无解 for (int i = pivots.size(); i < matrixRow; i++) { if (!addr.equals(Ab.get(new int[]{i,matrixCol}),addr.zero())) { return false; } } return true; } @Override public void fancyPrint() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < matrixCol + 1; j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i,j})); builder.append("\n"); } } } System.out.println(builder.toString()); } /** * 前向 * @param addr */ @SuppressWarnings("unchecked") private void forward(Addr<T> addr) { // int n = matrixRow; int i = 0; int k = 0; // for (int i = 0; i < n; i++) { while (i < matrixRow && k < matrixCol) { //看Ab[i][k]位置是否可以是主元 int maxRow = maxRow(i, k, matrixRow, addr); T[] temp = (T[]) new Number[matrixCol + 1]; //每一行无论是否有主元,都与主元最大值所在行进行交换 for (int j = 0; j < matrixCol + 1; j++) { temp[j] = Ab.get(new int[]{i, j}); Ab.set(new int[]{i, j}, Ab.get(new int[]{maxRow, j})); Ab.set(new int[]{maxRow, j}, temp[j]); } if (addr.equals(Ab.get(new int[]{i,k}),addr.zero())) { k++; }else { T u = Ab.get(new int[]{i, k}); //将主元归为一 for (int j = 0; j < matrixCol + 1; j++) { Ab.set(new int[]{i, j}, addr.div(Ab.get(new int[]{i, j}), u)); } //将主元所在行以下的行的所有非主元数据全部变成0 for (int m = i + 1; m < matrixRow; m++) { T u1 = Ab.get(new int[]{m, k}); for (int l = 0; l < matrixCol + 1; l++) { Ab.set(new int[]{m, l}, addr.sub(Ab.get(new int[]{m, l}), addr.mul(u1, Ab.get(new int[]{i, l})))); } } pivots.add(k); i++; } } } /** * 后向 */ private void backword(Addr<T> addr) { int n = pivots.size(); for (int i = n - 1; i >= 0; i--) { int k = pivots.get(i); //Ab[i][k]为主元 for (int j = i - 1; j >= 0; j--) { T u = Ab.get(new int[]{j, k}); for (int l = 0; l < matrixCol + 1; l++) { Ab.set(new int[]{j,l},addr.sub(Ab.get(new int[]{j,l}), addr.mul(u,Ab.get(new int[]{i,l})))); } } } } /** * 获取主元最大值所在的行 * @param addr * @return */ private int maxRow(int indexI,int indexJ,int n,Addr<T> addr) { T best = Ab.get(new int[]{indexI,indexJ}); int ret = indexI; for (int i = indexI + 1; i < n; i++) { if (addr.compair(Ab.get(new int[]{i,indexJ}),best) == 1) { best = Ab.get(new int[]{i,indexJ}); ret = i; } } return ret; } public static void main(String[] args) { Ranks<Double> A = new Matrix<>(new Double[][]{{1.0,2.0,4.0},{3.0,7.0,2.0},{2.0,3.0,3.0}}); Attribute<Double> b = new Vector<>(new Double[]{7.0,-11.0,1.0}); FuncGroup<Double> ls = new LinearSystem<>(A,b); Addr<Double> addr = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double one() { return 1.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double sub(Double lhs, Double rhs) { return lhs - rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public Double div(Double hs, Double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Double lhs, Double rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Double lhs, Double rhs) { double dis = 1e-8; if (Math.abs(lhs - rhs) < dis) { return true; } return false; } }; if (!ls.gaussJordanElimination(addr)) { System.out.println(" 无解"); } ls.fancyPrint(); System.out.println(); Ranks<Double> A1 = new Matrix<>(new Double[][]{{1.0,-1.0,2.0,0.0,3.0}, {-1.0,1.0,0.0,2.0,-5.0},{1.0,-1.0,4.0,2.0,4.0},{-2.0,2.0,-5.0,-1.0,-3.0}}); Attribute<Double> b1 = new Vector<>(new Double[]{1.0,5.0,13.0,-1.0}); FuncGroup<Double> ls1 = new LinearSystem<>(A1,b1); if (!ls1.gaussJordanElimination(addr)) { System.out.println(" 无解"); } ls1.fancyPrint(); System.out.println(); Ranks<Double> A2 = new Matrix<>(new Double[][]{{2.0,2.0},{2.0,1.0},{1.0,2.0}}); Attribute<Double> b2 = new Vector<>(new Double[]{3.0,2.5,7.0}); FuncGroup<Double> ls2 = new LinearSystem<>(A2,b2); if (!ls2.gaussJordanElimination(addr)) { System.out.println(" 无解"); } ls2.fancyPrint(); } }
运行结果
1.0 0.0 0.0 | -0.9999999999999991
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 2.9999999999999996
1.0 -1.0 0.0 -2.0 0.0 | -15.0
0.0 0.0 1.0 1.0 0.0 | 5.0
0.0 0.0 0.0 0.0 1.0 | 2.0
0.0 0.0 0.0 0.0 0.0 | 0.0
无解
1.0 0.0 | -4.0
0.0 1.0 | 5.5
0.0 0.0 | 5.0
Python代码
EPSILON = 1e-8 def is_zero(x): return abs(x) < EPSILON def is_equal(a, b): return abs(a - b) < EPSILON
from .Vector import Vector from ._global import is_zero class LinearSystem: def __init__(self, A, b): assert A.row_num() == len(b), "矩阵的行数必须等于向量的长度" self._matrix_row = A.row_num() self._matrix_col = A.col_num() self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]]) for i in range(self._matrix_row)] self.pivots = [] def _max_row(self, index_i, index_j, n): # 主元最大值所在的行 best, ret = self.Ab[index_i][index_j], index_i for i in range(index_i + 1, n): if self.Ab[i][index_j] > best: best, ret = self.Ab[i][index_j], i return ret def _forward(self): # 前向 i, k = 0, 0 while i < self._matrix_row and k < self._matrix_col: # 看Ab[i][k]位置是否可以为主元 max_row = self._max_row(i, k, self._matrix_row) self.Ab[i], self.Ab[max_row] = self.Ab[max_row], self.Ab[i] if is_zero(self.Ab[i][k]): k += 1 else: # 将主元归为一 self.Ab[i] = self.Ab[i] / self.Ab[i][k] for j in range(i + 1, self._matrix_row): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] self.pivots.append(k) i += 1 def _backward(self): # 后向 n = len(self.pivots) for i in range(n - 1, -1, -1): k = self.pivots[i] # Ab[i][k]为主元 for j in range(i - 1, -1, -1): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] def gauss_jordan_elimination(self): # 高斯-约旦消元法,如果有解,返回True;如果没有解,返回False self._forward() self._backward() for i in range(len(self.pivots), self._matrix_row): if not is_zero(self.Ab[i][-1]): return False return True def fancy_print(self): # 打印增广矩阵 for i in range(self._matrix_row): print(" ".join(str(self.Ab[i][j]) for j in range(self._matrix_col)), end=" ") print("|", self.Ab[i][-1])
from playLA.Matrix import Matrix from playLA.Vector import Vector from playLA.LinearSystem import LinearSystem if __name__ == "__main__": A = Matrix([[1, 2, 4], [3, 7, 2], [2, 3, 3]]) b = Vector([7, -11, 1]) ls = LinearSystem(A, b) ls.gauss_jordan_elimination() ls.fancy_print() print() A1 = Matrix([[1, -1, 2, 0, 3], [-1, 1, 0, 2, -5], [1, -1, 4, 2, 4], [-2, 2, -5, -1, -3]]) b1 = Vector([1, 5, 13, -1]) ls1 = LinearSystem(A1, b1) ls1.gauss_jordan_elimination() ls1.fancy_print() print() A2 = Matrix([[2, 2], [2, 1], [1, 2]]) b2 = Vector([3, 2.5, 7]) ls2 = LinearSystem(A2, b2) if not ls2.gauss_jordan_elimination(): print("无解") ls2.fancy_print()
运行结果
1.0 0.0 0.0 | -1.0
-0.0 1.0 0.0 | -2.0
-0.0 -0.0 1.0 | 3.0
1.0 -1.0 0.0 -2.0 0.0 | -15.0
0.0 0.0 1.0 1.0 0.0 | 5.0
0.0 0.0 0.0 0.0 1.0 | 2.0
0.0 0.0 0.0 0.0 0.0 | 0.0
无解
1.0 0.0 | -4.0
0.0 1.0 | 5.5
0.0 0.0 | 5.0
直观理解线性方程组解的结构
n个未知数有n个方程,才可能有唯一解
如果有一个方程为2x + 2y = 3,在直角坐标系中,它可以表示为一条直线,这条直线上的任意一个点都满足这个方程
这个时候我们再增加一个方程2x + y = 2,那么它也是一根直线
很显然,这两根直线相交的点就是方程组的唯一解
当然,在这种情况下它还有可能无解,如
如果有3个未知数,如x + y + 2z = 0,在三维空间中,它就是一个平面
如果有两个三元方程联立,则可能表示两个平面相交,它是一条直线
当方程组个数小于未知数个数,一定没有唯一解,而这两个平面相交的直线代表了无数个解。当然如果两个平面是平行的,则代表方程组无解
三个三元方程联立,它们代表三个平面,它们有可能相交于一个点
这个时候,方程组就有唯一解
它们也有可能相交于一条线
此时方程组就有无数解
如果三个平面平行
此时方程组就无解,但是三个三元方程联立没有解不仅仅是这一种情况
以上的情况,方程组都是无解的。
四个三元方程联立,情况也是类似的,代表了四个平面的各种情况
同理,三个二元方程联立,就代表了三条直线,它们有可能无解
也可能有唯一解
也可能有无数解(三个方程是同一条直线)
则方程个数和未知数个数的关系为
方程个数<未知数 | 方程个数=未知数 | 方程个数>未知数 |
无解 | 无解 | 无解 |
唯一解 | 唯一解 | |
无数解 | 无数解 | 无数解 |
对于行最简形式,系数矩阵的非零行不可能大于未知数个数
行最简形式系数矩阵非零行<未知数 | 行最简形式系数矩阵非零行=未知数 |
无数解 | 唯一解 |
以上是当方程组有解的情况
行最简形式系数矩阵非零行<行最简形式增广矩阵非零行,则无解
齐次线性方程组
齐次线性方程组的特性就是它的等号的右面都为0.满足这个条件的方程组就叫齐次线性方程组。
齐次线性方程和我们求解普通线性方程组的过程没有任何区别。首先划成增广矩阵
再第一行的主元归一,将主元下面的元素变成0
再将第二行的主元归一,将主元下面的元素变成0
最后化成行最简形式
对于齐次线性方程组来说,是一定有解的。因为对于齐次线性方程组来说,如果每一个未知数都取0,则肯定是它的一个解。对于齐次线性方程组来说,我们要看的是它是有唯一的0解还是它有无数个解。这个判断方式跟普通的线性方程组的方式是一样的。对于上面的这个行最简形式来说,它的系数矩阵的非零行的个数2小于它的未知数的个数3,所以它有无数个解。由于它的最后一列永远为0,所以它肯定不会出现普通线性方程组可能出现的那个矛盾——系数矩阵非零行<增广矩阵非零行,而导致的无解。
而我们在齐次线性方程组的计算中,也可以不对增广矩阵进行操作,只看它的系数矩阵就可以了。
当然我们也可以不这么操作,而是只当作普通线性方程组来看,使用增广矩阵来进行操作。
当然相对齐次线性方程组来说,就有非齐次线性方程组,那就是等号右边的值不都为0
矩阵的逆
在数字系统中,有,而在矩阵中,如果有AB=BA=I(I是单位矩阵),则称B是A的逆矩阵,记做
由于数字乘法满足乘法交换律,但在矩阵的乘法并不满足乘法交换律,如果A能找到逆矩阵的话,A称为可逆矩阵,或者叫做非奇异矩阵(non-singular)。有些矩阵是不可逆的,称为不可逆矩阵,或者奇异矩阵(singular)
比如有这样一个矩阵,则它的逆矩阵为
如果BA=I,则称B是A的左逆矩阵
如果AC=I, 则称C是A的右逆矩阵
如果一个矩阵A即存在左逆矩阵B,又存在右逆矩阵C,则B=C。对于矩阵A,存在矩阵B,满足BA=AB=I,矩阵A可逆
可逆矩阵一定是方阵,非方阵一定不可逆
现在我们知道了,
就是矩阵的逆
求解矩阵的逆
我们还是以
来说明
由矩阵的乘法,由于A是一个2*2的矩阵,而I也是一个2*2的矩阵,则A的逆也一定是一个2*2的矩阵,则我们给A的逆矩阵设为
则满足矩阵乘法
则我们可以把该式子拆成
变换后为
则它们可以转变成这样一个线性系统
由于第一、第二个方程和第三、第四个方程的未知数并不想干,我们可以分别将第一、第二;第三、第四个方程组分别化成两个增广矩阵
将其通过高斯-约旦消元法化成行最简形式,可得
由于两个增广矩阵的系数矩阵相同,我们可以将其合并成一个新的增广矩阵
再通过高斯-约旦消元法化成行最简形式
则可以得出,对于任意一个有逆矩阵的矩阵A有如下的形式
现在我们用高斯-约旦消元法来求出这个逆矩阵
我们将第二个行向量减去第一个行向量乘以3,得到
再将第二行的主元归一
再将第一个行向量减去第二个行向量乘以2,得
至此,我们就化成了行最简形式,则我们可以看出原矩阵的逆矩阵就是
现在我们来看一个这个过程的一般性
首先我们可以看出,这个增广矩阵是不可能有无数解的,因为有无数解的前提是有一个化为行最简形式的时候有一个全零行,而该增广矩阵的右边是一个单位矩阵,无论你怎么变换,都不可能将单位矩阵的元素全部变成0.也就是说,对于一个方阵,要不然我们可以求出唯一一个矩阵的逆,要不然我们求不出矩阵的逆。如果一个矩阵存在逆矩阵,则它的逆矩阵是唯一的。但这个线性系统也可能是无解的,即没有逆矩阵,如果在变成行最简形式后,系数矩阵的某一行变成全零行,此时就是无解的,因为单位矩阵的每一行是必有值的,不可能变成全零行。
如果一个方阵A有右逆B,则B也是A的左逆,即B是A的逆。
给Ranks接口增加一个求矩阵的逆的方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 设置矩阵中某个位置的元素 * @param pos * @param value */ void set(int[] pos,T value); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); /** * 转置矩阵 * @return */ Ranks<T> trans(); /** * 矩阵的逆 * @param addr * @return */ Ranks<T> inv(Addr<T> addr); }
修改线性系统类,增加一个新的构造方法
/** * 线性系统 * @param <T> */ public class LinearSystem<T extends Number> implements FuncGroup<T>{ //系数矩阵的行数 private int matrixRow; //系数矩阵的列数 private int matrixCol; //增广矩阵 @Getter private Ranks<T> Ab; //主元列 @Getter private List<Integer> pivots = new ArrayList<>(); @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A,Attribute<T> b) { if (A.rowNum() != b.len()) { throw new IllegalArgumentException("系数矩阵的行数必须等于常数向量的长度"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + 1]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j <= A.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(i); } } } Ab = new Matrix<>(values); } @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A,Ranks<T> b) { if (A.rowNum() != b.colNum()) { throw new IllegalArgumentException("系数矩阵的行数必须等于单位矩阵的列数"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + b.colNum()]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j < A.colNum() + b.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(new int[]{i, j - A.colNum()}); } } } Ab = new Matrix<>(values); } @Override public boolean gaussJordanElimination(Addr<T> addr) { forward(addr); backword(addr); //无主元的行的非系数元素如果为0则无解 for (int i = pivots.size(); i < matrixRow; i++) { if (!addr.equals(Ab.get(new int[]{i,matrixCol}),addr.zero())) { return false; } } return true; } @Override public void fancyPrint() { StringBuilder builder = new StringBuilder(); if (Ab.colNum() - matrixCol > 1) { for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < Ab.colNum(); j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } else if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i, j})); } else { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); builder.append("\n"); } } } }else if (Ab.colNum() - matrixCol == 1) { for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < Ab.colNum(); j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } else if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i, j})); builder.append("\n"); } } } } System.out.println(builder.toString()); } /** * 前向 * @param addr */ @SuppressWarnings("unchecked") private void forward(Addr<T> addr) { // int n = matrixRow; int i = 0; int k = 0; // for (int i = 0; i < n; i++) { while (i < matrixRow && k < matrixCol) { //看Ab[i][k]位置是否可以是主元 int maxRow = maxRow(i, k, matrixRow, addr); T[] temp = (T[]) new Number[Ab.colNum()]; //每一行无论是否有主元,都与主元最大值所在行进行交换 for (int j = 0; j < Ab.colNum(); j++) { temp[j] = Ab.get(new int[]{i, j}); Ab.set(new int[]{i, j}, Ab.get(new int[]{maxRow, j})); Ab.set(new int[]{maxRow, j}, temp[j]); } if (addr.equals(Ab.get(new int[]{i,k}),addr.zero())) { k++; }else { T u = Ab.get(new int[]{i, k}); //将主元归为一 for (int j = 0; j < Ab.colNum(); j++) { Ab.set(new int[]{i, j}, addr.div(Ab.get(new int[]{i, j}), u)); } //将主元所在行以下的行的所有非主元数据全部变成0 for (int m = i + 1; m < matrixRow; m++) { T u1 = Ab.get(new int[]{m, k}); for (int l = 0; l < Ab.colNum(); l++) { Ab.set(new int[]{m, l}, addr.sub(Ab.get(new int[]{m, l}), addr.mul(u1, Ab.get(new int[]{i, l})))); } } pivots.add(k); i++; } } } /** * 后向 */ private void backword(Addr<T> addr) { int n = pivots.size(); for (int i = n - 1; i >= 0; i--) { int k = pivots.get(i); //Ab[i][k]为主元 for (int j = i - 1; j >= 0; j--) { T u = Ab.get(new int[]{j, k}); for (int l = 0; l < Ab.colNum(); l++) { Ab.set(new int[]{j,l},addr.sub(Ab.get(new int[]{j,l}), addr.mul(u,Ab.get(new int[]{i,l})))); } } } } /** * 获取主元最大值所在的行 * @param addr * @return */ private int maxRow(int indexI,int indexJ,int n,Addr<T> addr) { T best = Ab.get(new int[]{indexI,indexJ}); int ret = indexI; for (int i = indexI + 1; i < n; i++) { if (addr.compair(Ab.get(new int[]{i,indexJ}),best) == 1) { best = Ab.get(new int[]{i,indexJ}); ret = i; } } return ret; } }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } /** * 创建一个单位矩阵 * @param n * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> identity(int n,Addr<T> addr) { T[][] values = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(values[i],addr.zero()); } for (int i = 0; i < n; i++) { values[i][i] = addr.one(); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public void set(int[] pos, T value) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } values[pos[0]][pos[1]] = value; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> inv(Addr<T> addr) { if (this.rowNum() != this.colNum()) { return null; } int n = this.rowNum(); FuncGroup<T> ls = new LinearSystem<>(this,Matrix.identity(n,addr)); if (!ls.gaussJordanElimination(addr)) { return null; } T[][] values = (T[][])new Number[this.rowNum()][((LinearSystem)ls).getAb().colNum() - this.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < ((LinearSystem)ls).getAb().colNum() - this.colNum(); j++) { values[i][j] = (T)((LinearSystem)ls).getAb().get(new int[]{i,((LinearSystem)ls).getAb().colNum() - this.colNum() + j}); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Matrix clone() throws CloneNotSupportedException { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = this.values[i][j]; } } return new Matrix(values); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer one() { return 1; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public Integer div(Integer hs, Integer nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Integer lhs, Integer rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Integer lhs, Integer rhs) { return lhs == rhs; } }; System.out.println(matrix.trans()); Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{9,33,97}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); Ranks<Integer> I = Matrix.identity(4, addr); System.out.println(I); Matrix<Integer> idenTest = new Matrix<>(new Integer[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}); System.out.println(idenTest.dot(I,addr)); System.out.println(I.dot(idenTest,addr)); Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double one() { return 1.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double sub(Double lhs, Double rhs) { return lhs - rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public Double div(Double hs, Double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Double lhs, Double rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Double lhs, Double rhs) { double dis = 1e-8; if (Math.abs(lhs - rhs) < dis) { return true; } return false; } }; Ranks<Double> A = new Matrix<>(new Double[][]{{1.0,2.0},{3.0,4.0}}); System.out.println(A.inv(addr1)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[1, 3],[2, 4],[3, 5]]}
Vector{values=[366, 644]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Matrix{values=[[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[-1.9999999999999996, 0.9999999999999998],[1.4999999999999998, -0.49999999999999994]]}
Python代码
from .Vector import Vector from .Matrix import Matrix from ._global import is_zero class LinearSystem: def __init__(self, A, b): assert A.row_num() == len(b), "矩阵的行数必须等于向量的长度" self._matrix_row = A.row_num() self._matrix_col = A.col_num() if isinstance(b, Vector): self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]]) for i in range(self._matrix_row)] if isinstance(b, Matrix): self.Ab = [Vector(A.row_vector(i).underlying_list() + b.row_vector(i).underlying_list()) for i in range(self._matrix_row)] self.pivots = [] def _max_row(self, index_i, index_j, n): # 主元最大值所在的行 best, ret = self.Ab[index_i][index_j], index_i for i in range(index_i + 1, n): if self.Ab[i][index_j] > best: best, ret = self.Ab[i][index_j], i return ret def _forward(self): # 前向 i, k = 0, 0 while i < self._matrix_row and k < self._matrix_col: # 看Ab[i][k]位置是否可以为主元 max_row = self._max_row(i, k, self._matrix_row) self.Ab[i], self.Ab[max_row] = self.Ab[max_row], self.Ab[i] if is_zero(self.Ab[i][k]): k += 1 else: # 将主元归为一 self.Ab[i] = self.Ab[i] / self.Ab[i][k] for j in range(i + 1, self._matrix_row): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] self.pivots.append(k) i += 1 def _backward(self): # 后向 n = len(self.pivots) for i in range(n - 1, -1, -1): k = self.pivots[i] # Ab[i][k]为主元 for j in range(i - 1, -1, -1): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] def gauss_jordan_elimination(self): # 高斯-约旦消元法,如果有解,返回True;如果没有解,返回False self._forward() self._backward() for i in range(len(self.pivots), self._matrix_row): if not is_zero(self.Ab[i][-1]): return False return True def fancy_print(self): # 打印增广矩阵 for i in range(self._matrix_row): print(" ".join(str(self.Ab[i][j]) for j in range(self._matrix_col)), end=" ") print("|", self.Ab[i][-1])
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) @classmethod def identity(cls, n): # 返回一个n行n列的单位矩阵 m = [[0] * n for _ in range(n)] for i in range(n): m[i][i] = 1 return cls(m) def trans(self): # 返回矩阵的转置矩阵 return Matrix([[e for e in self.col_vector(i)] for i in range(self.col_num())]) def inv(self): # 返回矩阵的逆 from .LinearSystem import LinearSystem if self.row_num() != self.col_num(): return None n = self.row_num() ls = LinearSystem(self, Matrix.identity(n)) if not ls.gauss_jordan_elimination(): return None invA = [[row[i] for i in range(n, 2 * n)] for row in ls.Ab] return Matrix(invA) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3))) print("matrix.trans = {}".format(matrix.trans())) I = Matrix.identity(4) print(I) idenTest = Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) print(idenTest.dot(I)) print(I.dot(idenTest)) A = Matrix([[1, 2], [3, 4]]) print(A.inv())
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
matrix.trans = Matrix([[1, 3], [2, 4], [3, 5]])
Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]])
初等矩阵
在我们使用高斯-约旦消元法中,我们会矩阵的某一行乘以一个常数,矩阵的一行加(减)零一行,交换矩阵的两行
这些都是在矩阵的内部,对内部元素进行操作。但矩阵本身表示变换,对于这些操作,同样可以使用矩阵来表示
我们知道单位矩阵乘以一个矩阵等于该矩阵本身,其意义就是单位矩阵作为一个变换矩阵,没有对该矩阵产生变换而已。
如果想让矩阵的某一行乘以一个常数k,我们可以对单位矩阵做出如下的改变(比如说让矩阵的第一行乘以k)
如果想让矩阵的一行加(减)另一行,我们可以对单位矩阵做出如下改变(比如说让矩阵的第三行加上第一行)
再比如说让矩阵的第一行减去第三行,我们可以对单位矩阵做出如下改变
当然我们也可以加(减)另一行的若干倍,我们可以对单位矩阵做出如下改变(比如说让矩阵的第二行减去p倍的第三行)
现在我们想交换矩阵的两行,我们可以对单位矩阵做出如下改变(比如说交换矩阵的第二行和第三行)
以上的这些变换过程,是矩阵的基本操作,称为矩阵的初等变换,而这些单位矩阵变换后的矩阵,称为初等矩阵
则初等矩阵就是形如下面这样的矩阵
而初等矩阵就是对单位矩阵进行一次初等变换得到的结果矩阵,通常记作E
我们在高斯-约旦消元法把矩阵化为行最简形式的过程就是对增广矩阵A进行了一系列的初等变换,就相当于是寻找一系列的初等矩阵E,使得:
其中rref(A)表示A的行最简形式。
Addr接口增加一个取负的方法neg()
public interface Addr<T extends Number> { T zero(); T one(); T add(T lhs,T rhs); T sub(T lhs,T rhs); T mul(T k,T hs); T square(T hs); double prescription(T hs); double div(T hs,double nhs); T div(T hs,T nhs); double acos(double hs); double toDegree(double hs); int compair(T lhs,T rhs); boolean equals(T lhs,T rhs); T neg(T hs); }
我们将之前的线性系统类改成一个抽象类
public abstract class LinearSystem<T extends Number> implements FuncGroup<T> { //系数矩阵的行数 protected int matrixRow; //系数矩阵的列数 protected int matrixCol; //增广矩阵 @Getter protected Ranks<T> Ab; //主元列 @Getter protected List<Integer> pivots = new ArrayList<>(); @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A, Attribute<T> b) { if (A.rowNum() != b.len()) { throw new IllegalArgumentException("系数矩阵的行数必须等于常数向量的长度"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + 1]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j <= A.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(i); } } } Ab = new Matrix<>(values); } @SuppressWarnings("unchecked") public LinearSystem(Ranks<T> A, Ranks<T> b) { if (A.rowNum() != b.colNum()) { throw new IllegalArgumentException("系数矩阵的行数必须等于单位矩阵的列数"); } matrixRow = A.rowNum(); matrixCol = A.colNum(); T[][] values = (T[][])new Number[A.rowNum()][A.colNum() + b.colNum()]; for (int i = 0; i < A.rowNum(); i++) { for (int j = 0; j < A.colNum() + b.colNum(); j++) { if (j < A.colNum()) { values[i][j] = A.get(new int[]{i, j}); }else { values[i][j] = b.get(new int[]{i, j - A.colNum()}); } } } Ab = new Matrix<>(values); } @Override public boolean gaussJordanElimination(Addr<T> addr) { forward(addr); backword(addr); //无主元的行的非系数元素如果为0则无解 for (int i = pivots.size(); i < matrixRow; i++) { if (!addr.equals(Ab.get(new int[]{i,matrixCol}),addr.zero())) { return false; } } return true; } @Override public void fancyPrint() { StringBuilder builder = new StringBuilder(); if (Ab.colNum() - matrixCol > 1) { for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < Ab.colNum(); j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } else if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i, j})); } else { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); builder.append("\n"); } } } }else if (Ab.colNum() - matrixCol == 1) { for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < Ab.colNum(); j++) { if (j < matrixCol) { builder.append(" "); builder.append(Ab.get(new int[]{i, j})); } else if (j == matrixCol) { builder.append(" | "); builder.append(Ab.get(new int[]{i, j})); builder.append("\n"); } } } } System.out.println(builder.toString()); } protected abstract void forward(Addr<T> addr); protected abstract void backword(Addr<T> addr); /** * 获取主元最大值所在的行 * @param addr * @return */ protected int maxRow(int indexI,int indexJ,int n,Addr<T> addr) { T best = Ab.get(new int[]{indexI,indexJ}); int ret = indexI; for (int i = indexI + 1; i < n; i++) { if (addr.compair(Ab.get(new int[]{i,indexJ}),best) == 1) { best = Ab.get(new int[]{i,indexJ}); ret = i; } } return ret; } }
之前的增广矩阵内部元素的处理类改成LinearSystemFunc类
/** * 线性系统 * @param <T> */ public final class LinearSystemFunc<T extends Number> extends LinearSystem<T>{ public LinearSystemFunc(Ranks<T> A, Attribute<T> b) { super(A,b); } public LinearSystemFunc(Ranks<T> A, Ranks<T> b) { super(A,b); } /** * 前向 * @param addr */ @Override @SuppressWarnings("unchecked") protected void forward(Addr<T> addr) { // int n = matrixRow; int i = 0; int k = 0; // for (int i = 0; i < n; i++) { while (i < matrixRow && k < matrixCol) { //看Ab[i][k]位置是否可以是主元 int maxRow = maxRow(i, k, matrixRow, addr); T[] temp = (T[]) new Number[Ab.colNum()]; //每一行无论是否有主元,都与主元最大值所在行进行交换 for (int j = 0; j < Ab.colNum(); j++) { temp[j] = Ab.get(new int[]{i, j}); Ab.set(new int[]{i, j}, Ab.get(new int[]{maxRow, j})); Ab.set(new int[]{maxRow, j}, temp[j]); } if (addr.equals(Ab.get(new int[]{i,k}),addr.zero())) { k++; }else { T u = Ab.get(new int[]{i, k}); //将主元归为一 for (int j = 0; j < Ab.colNum(); j++) { Ab.set(new int[]{i, j}, addr.div(Ab.get(new int[]{i, j}), u)); } //将主元所在行以下的行的所有非主元数据全部变成0 for (int m = i + 1; m < matrixRow; m++) { T u1 = Ab.get(new int[]{m, k}); for (int l = 0; l < Ab.colNum(); l++) { Ab.set(new int[]{m, l}, addr.sub(Ab.get(new int[]{m, l}), addr.mul(u1, Ab.get(new int[]{i, l})))); } } pivots.add(k); i++; } } } /** * 后向 */ @Override protected void backword(Addr<T> addr) { int n = pivots.size(); for (int i = n - 1; i >= 0; i--) { int k = pivots.get(i); //Ab[i][k]为主元 for (int j = i - 1; j >= 0; j--) { T u = Ab.get(new int[]{j, k}); for (int l = 0; l < Ab.colNum(); l++) { Ab.set(new int[]{j,l},addr.sub(Ab.get(new int[]{j,l}), addr.mul(u,Ab.get(new int[]{i,l})))); } } } } }
新增加我们的初等矩阵变换的线形系统类LinearSystemMatrix
/** * 初等矩阵变换线性系统 * @param <T> */ public final class LinearSystemMatrix<T extends Number> extends LinearSystem<T> { public LinearSystemMatrix(Ranks<T> A,Attribute<T> b) { super(A,b); } public LinearSystemMatrix(Ranks<T> A,Ranks<T> b) { super(A,b); } @Override protected void forward(Addr<T> addr) { int i = 0; int k = 0; while (i < matrixRow && k < matrixCol) { int maxRow = maxRow(i, k, matrixRow, addr); Ab = changeMatrix(i,maxRow,addr).dot(Ab,addr); if (addr.equals(Ab.get(new int[]{i,k}),addr.zero())) { k++; }else { T u = Ab.get(new int[]{i, k}); Ab = mulMatrix(i,addr.div(addr.one(),u),addr).dot(Ab,addr); for (int j = i + 1; j < matrixRow; j++) { T u1 = Ab.get(new int[]{j, k}); Ab = addPMatrix(j,i,addr.neg(u1),addr).dot(Ab,addr); } pivots.add(k); i++; } } } @Override protected void backword(Addr<T> addr) { int n = pivots.size(); for (int i = n - 1; i >= 0; i--) { int k = pivots.get(i); //Ab[i][k]为主元 for (int j = i - 1; j >= 0; j--) { T u = Ab.get(new int[]{j, k}); Ab = addPMatrix(j,i,addr.neg(u),addr).dot(Ab,addr); } } } /** * 交换初等矩阵 * @param i 原行 * @param j 交换行 * @param addr * @return */ private Ranks<T> changeMatrix(int i,int j,Addr<T> addr) { Ranks<T> identity = Matrix.identity(matrixRow,addr); identity.set(new int[]{i,i},addr.zero()); identity.set(new int[]{j,i},addr.one()); identity.set(new int[]{j,j},addr.zero()); identity.set(new int[]{i,j},addr.one()); return identity; } /** * 乘除初等矩阵 * @param i 需要处理的矩阵的行 * @param t 乘的系数 * @param addr * @return */ private Ranks<T> mulMatrix(int i,T t,Addr<T> addr) { Ranks<T> identity = Matrix.identity(matrixRow,addr); identity.set(new int[]{i,i},t); return identity; } /** * 加减初等矩阵 * @param i 需要加减的行 * @param j 被加减的行 * @param t 加减的倍数 * @param addr * @return */ private Ranks<T> addPMatrix(int i,int j,T t,Addr<T> addr) { Ranks<T> identity = Matrix.identity(matrixRow,addr); identity.set(new int[]{i,j},t); return identity; } }
将Ranks接口的求矩阵的逆改成两个方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 设置矩阵中某个位置的元素 * @param pos * @param value */ void set(int[] pos,T value); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); /** * 转置矩阵 * @return */ Ranks<T> trans(); /** * 矩阵的逆 * @param addr * @return */ Ranks<T> invFunc(Addr<T> addr); /** * 矩阵的逆 * @param addr * @return */ Ranks<T> invMatrix(Addr<T> addr); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } /** * 创建一个单位矩阵 * @param n * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> identity(int n,Addr<T> addr) { T[][] values = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(values[i],addr.zero()); } for (int i = 0; i < n; i++) { values[i][i] = addr.one(); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public void set(int[] pos, T value) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } values[pos[0]][pos[1]] = value; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> invFunc(Addr<T> addr) { if (this.rowNum() != this.colNum()) { return null; } int n = this.rowNum(); FuncGroup<T> ls = new LinearSystemFunc<>(this,Matrix.identity(n,addr)); if (!ls.gaussJordanElimination(addr)) { return null; } T[][] values = (T[][])new Number[this.rowNum()][((LinearSystemFunc)ls).getAb().colNum() - this.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < ((LinearSystemFunc)ls).getAb().colNum() - this.colNum(); j++) { values[i][j] = (T)((LinearSystemFunc)ls).getAb().get(new int[]{i,((LinearSystemFunc)ls).getAb().colNum() - this.colNum() + j}); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> invMatrix(Addr<T> addr) { if (this.rowNum() != this.colNum()) { return null; } int n = this.rowNum(); FuncGroup<T> ls = new LinearSystemMatrix<>(this,Matrix.identity(n,addr)); if (!ls.gaussJordanElimination(addr)) { return null; } T[][] values = (T[][])new Number[this.rowNum()][((LinearSystemMatrix)ls).getAb().colNum() - this.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < ((LinearSystemMatrix)ls).getAb().colNum() - this.colNum(); j++) { values[i][j] = (T)((LinearSystemMatrix)ls).getAb().get(new int[]{i,((LinearSystemMatrix)ls).getAb().colNum() - this.colNum() + j}); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Matrix clone() throws CloneNotSupportedException { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = this.values[i][j]; } } return new Matrix(values); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer one() { return 1; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public Integer div(Integer hs, Integer nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Integer lhs, Integer rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Integer lhs, Integer rhs) { return lhs == rhs; } @Override public Integer neg(Integer hs) { return -hs; } }; System.out.println(matrix.trans()); Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{9,33,97}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); Ranks<Integer> I = Matrix.identity(4, addr); System.out.println(I); Matrix<Integer> idenTest = new Matrix<>(new Integer[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}); System.out.println(idenTest.dot(I,addr)); System.out.println(I.dot(idenTest,addr)); Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double one() { return 1.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double sub(Double lhs, Double rhs) { return lhs - rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public Double div(Double hs, Double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Double lhs, Double rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Double lhs, Double rhs) { double dis = 1e-8; if (Math.abs(lhs - rhs) < dis) { return true; } return false; } @Override public Double neg(Double hs) { return -hs; } }; Ranks<Double> A = new Matrix<>(new Double[][]{{1.0,2.0},{3.0,4.0}}); System.out.println(A.invFunc(addr1)); System.out.println(A.invMatrix(addr1)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[1, 3],[2, 4],[3, 5]]}
Vector{values=[366, 644]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Matrix{values=[[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[-1.9999999999999996, 0.9999999999999998],[1.4999999999999998, -0.49999999999999994]]}
Matrix{values=[[-1.9999999999999996, 0.9999999999999998],[1.4999999999999998, -0.4999999999999999]]}
Python代码
将原线性系统类修改为LinearSystemFunc
from .Vector import Vector from .Matrix import Matrix from ._global import is_zero class LinearSystemFunc: def __init__(self, A, b): assert A.row_num() == len(b), "矩阵的行数必须等于向量的长度" self._matrix_row = A.row_num() self._matrix_col = A.col_num() if isinstance(b, Vector): self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]]) for i in range(self._matrix_row)] if isinstance(b, Matrix): self.Ab = [Vector(A.row_vector(i).underlying_list() + b.row_vector(i).underlying_list()) for i in range(self._matrix_row)] self.pivots = [] def _max_row(self, index_i, index_j, n): # 主元最大值所在的行 best, ret = self.Ab[index_i][index_j], index_i for i in range(index_i + 1, n): if self.Ab[i][index_j] > best: best, ret = self.Ab[i][index_j], i return ret def _forward(self): # 前向 i, k = 0, 0 while i < self._matrix_row and k < self._matrix_col: # 看Ab[i][k]位置是否可以为主元 max_row = self._max_row(i, k, self._matrix_row) self.Ab[i], self.Ab[max_row] = self.Ab[max_row], self.Ab[i] if is_zero(self.Ab[i][k]): k += 1 else: # 将主元归为一 self.Ab[i] = self.Ab[i] / self.Ab[i][k] for j in range(i + 1, self._matrix_row): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] self.pivots.append(k) i += 1 def _backward(self): # 后向 n = len(self.pivots) for i in range(n - 1, -1, -1): k = self.pivots[i] # Ab[i][k]为主元 for j in range(i - 1, -1, -1): self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i] def gauss_jordan_elimination(self): # 高斯-约旦消元法,如果有解,返回True;如果没有解,返回False self._forward() self._backward() for i in range(len(self.pivots), self._matrix_row): if not is_zero(self.Ab[i][-1]): return False return True def fancy_print(self): # 打印增广矩阵 for i in range(self._matrix_row): print(" ".join(str(self.Ab[i][j]) for j in range(self._matrix_col)), end=" ") print("|", self.Ab[i][-1])
新增加一个初等矩阵变换线性系统LinearSystemMatrix
from .Vector import Vector from .Matrix import Matrix from ._global import is_zero class LinearSystemMatrix: def __init__(self, A, b): assert A.row_num() == len(b), "矩阵的行数必须等于向量的长度" self._matrix_row = A.row_num() self._matrix_col = A.col_num() if isinstance(b, Vector): self.Ab = Matrix([Vector(A.row_vector(i).underlying_list() + [b[i]]) for i in range(self._matrix_row)]) if isinstance(b, Matrix): self.Ab = Matrix([Vector(A.row_vector(i).underlying_list() + b.row_vector(i).underlying_list()) for i in range(self._matrix_row)]) self.pivots = [] def _max_row(self, index_i, index_j, n): # 主元最大值所在的行 best, ret = self.Ab[(index_i, index_j)], index_i for i in range(index_i + 1, n): if self.Ab[(i, index_j)] > best: best, ret = self.Ab[(i, index_j)], i return ret def _forward(self): # 前向 i, k = 0, 0 while i < self._matrix_row and k < self._matrix_col: # 看Ab[i][k]位置是否可以为主元 max_row = self._max_row(i, k, self._matrix_row) self.Ab = self._change_matrix(i, max_row).dot(self.Ab) if is_zero(self.Ab[(i, k)]): k += 1 else: # 将主元归为一 self.Ab = self._mul_matrix(i, 1 / self.Ab[(i, k)]).dot(self.Ab) for j in range(i + 1, self._matrix_row): self.Ab = self._add_p_matrix(j, i, -self.Ab[(j, k)]).dot(self.Ab) self.pivots.append(k) i += 1 def _backward(self): # 后向 n = len(self.pivots) for i in range(n - 1, -1, -1): k = self.pivots[i] # Ab[i][k]为主元 for j in range(i - 1, -1, -1): self.Ab = self._add_p_matrix(j, i, -self.Ab[(j, k)]).dot(self.Ab) def _change_matrix(self, i, j): # 交换初等矩阵 identity = Matrix.identity(self._matrix_row) identity[(i, i)] = 0 identity[(j, i)] = 1 identity[(j, j)] = 0 identity[(i, j)] = 1 return identity def _mul_matrix(self, i, value): # 乘除初等矩阵 identity = Matrix.identity(self._matrix_row) identity[(i, i)] = value return identity def _add_p_matrix(self, i, j, value): # 加减初等矩阵 identity = Matrix.identity(self._matrix_row) identity[(i, j)] = value return identity def gauss_jordan_elimination(self): # 高斯-约旦消元法,如果有解,返回True;如果没有解,返回False self._forward() self._backward() for i in range(len(self.pivots), self._matrix_row): if not is_zero(self.Ab[(i, -1)]): return False return True def fancy_print(self): # 打印增广矩阵 for i in range(self._matrix_row): print(" ".join(str(self.Ab[(i, j)]) for j in range(self._matrix_col)), end=" ") print("|", self.Ab[(i, -1)])
矩阵类的inv方法也变成两个
from .Vector import Vector class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) @classmethod def identity(cls, n): # 返回一个n行n列的单位矩阵 m = [[0] * n for _ in range(n)] for i in range(n): m[i][i] = 1 return cls(m) def trans(self): # 返回矩阵的转置矩阵 return Matrix([[e for e in self.col_vector(i)] for i in range(self.col_num())]) def inv_func(self): # 返回矩阵的逆 from .LinearSystemFunc import LinearSystemFunc if self.row_num() != self.col_num(): return None n = self.row_num() ls = LinearSystemFunc(self, Matrix.identity(n)) if not ls.gauss_jordan_elimination(): return None invA = [[row[i] for i in range(n, 2 * n)] for row in ls.Ab] return Matrix(invA) def inv_matrix(self): # 返回矩阵的逆 from .LinearSystemMatrix import LinearSystemMatrix if self.row_num() != self.col_num(): return None n = self.row_num() ls = LinearSystemMatrix(self, Matrix.identity(n)) if not ls.gauss_jordan_elimination(): return None invA = [[row[i] for i in range(n, 2 * n)] for row in ls.Ab._values] return Matrix(invA) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def __setitem__(self, pos, value): # 设置pos位置的元素 r, c = pos self._values[r][c] = value def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3))) print("matrix.trans = {}".format(matrix.trans())) I = Matrix.identity(4) print(I) idenTest = Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) print(idenTest.dot(I)) print(I.dot(idenTest)) A = Matrix([[1, 2], [3, 4]]) print(A.inv_func()) print(A.inv_matrix())
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
matrix.trans = Matrix([[1, 3], [2, 4], [3, 5]])
Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]])
Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]])
初等矩阵到矩阵的逆
初等矩阵一定可逆,因为初等变换是可逆的,所以初等矩阵是可逆的。初等矩阵的逆矩阵也是一个初等矩阵。
比方说,将矩阵的第一行都乘以一个倍数k的初等矩阵
要将变换后的矩阵反其道而行之,只需要将k变成1/k即可
虽然这么说并不严谨,但我们可以使用矩阵逆的定义来说明。
如果需要变换的矩阵本身就是单位矩阵,则该单位矩阵变换后,就是先被变成了
的样子,再被
变回了单位矩阵原来的样子。同理
,从而满足矩阵逆的定义,存在一个矩阵
,左乘
也好,右乘
也好,都等于单位矩阵
,则
和
互为逆矩阵。
而对于第二行减去p倍的第三行的初等矩阵
则它的逆矩阵就是第二行加上p倍的第三行
而对于交换第二行和第三行的初等矩阵
则它的逆矩阵就是再次交换第二行和第三行
即为它本身
现在我们来看一下一般的可逆矩阵,通过高斯-约旦消元法把矩阵化为行最简形式的过程,寻找一系列初等矩阵E,使得
根据之前逆矩阵的求解过程,,如果有解(无解是系数矩阵化为行最简形式时有0行),则系数矩阵的行最简形式为单位矩阵,则上面的式子就变成了
则,因为我们知道A可逆,则等式的左右两边都乘以A的逆矩阵
根据矩阵乘法的结合律,我们可以先计算A乘以A的逆,得到
我们通过该式子可以看出A的逆矩阵其实就是对单位矩阵进行一系列的初等变换,其实就是把A化成行最简形式后进行一系列初等变换。
就相当于我们之前的一个过程,就是把左侧的A化为了I,而右侧的I化为了A的逆
矩阵逆的重要性
对于线性系统Ax = b,其中A是系数矩阵,b是常数列向量,x是未知数列向量
如果系数矩阵A是可逆的,则有,从而得到
其实通过这种使用A的逆的方式来求解线性系统跟使用高斯-约旦消元法的时间复杂度是差不多的,有的时候操作会更多一些,但在某些情况下是更好的,那就是如果A不变,b会变化的条件下,大大加快计算速度。
我们之前在介绍线性系统的时候,有一个经济系统的例子
国家对不同行业的投资,会有一定的调整,即常数向量会发生改变,此时,我们先求出系数矩阵的逆,则会加快未知数向量的求解。
更关键的是:矩阵的逆和很多重要的命题连接在了一起
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
通过上一小节,我们知道,1可以推导出3,而在3中化成了行最简形式I,由于有n个未知数,n个方程,则只可能有一个解,而齐次线性方程组只有一个解的话,则只能是0解,所以3可以推导出2。而经过3中化成行最简形式I,其实就是经过了一系列初等变换和一系列初等矩阵相乘的结果,所以3可以推导出4.
其实这些命题是等价的,也就是说这四个命题可以两两互相推导的。
- 我们先由1来推导2
,由于A是可逆的,我们在等式的两边都左乘A的逆
,则可以得到
,最终得到
- 再来由2推导3
假设A为n*n的矩阵,Ax=0有唯一解,则有n个未知数,且rref(A)有n个非零行,根据行最简形式定义:rref(A)=I
- 3推导4
现在我们知道一个矩阵在高斯-约旦消元法中就是进行一系列的初等变换,就是乘以一系列的初等矩阵E,又由之前知道,矩阵的行最简形式就是I
我们知道初等矩阵都是可逆的,我们在等式的两边乘以这些初等矩阵的逆矩阵
根据矩阵乘法的结合律,从中间开始结合,得到
最终得到
由于初等矩阵的逆矩阵还是初等矩阵,所以4成立。
- 4推导1
,A是一系列初等矩阵的乘积
我们来构造一个这些初等矩阵逆矩阵乘积的B
则
而对于比较麻烦一点,我们先来看一下
的成立
只要在等式的两边同时乘以XY,可得I=I,同理
,最终可得
,则可以得出1成立
以上面四个证明为桥梁,我们可以两两证明这四个命题是等价的。
- Ax=b只有唯一解
也与上面的四个命题是等价的。这五个命题是等价的,同时也是说这五个命题的否命题也是等价的
比如矩阵A可逆(A是非奇异矩阵)可得出rref(A)=I
则矩阵A不可逆(A是奇异矩阵),可得出,A的行最简形式存在零行
矩阵的LU分解
数的分解 66 = 2 * 3 * 11 这是一个质因数分解
一个矩阵也可以分解成几个矩阵乘积的形式,矩阵的不同分解有不同的目的,矩阵的LU分解是提高计算效率为目的,它是初等矩阵的一个很好的应用
将矩阵A分解成两个矩阵,一个叫L,另一个叫U
L:Lower Triangle Matrix ,下三角矩阵
以方阵为例,矩阵对角线上面的元素都为0,矩阵的主要元素都在矩阵的下三角部分,称为下三角矩阵
如果矩阵的中间部分的元素都为1,还可以称为单位下三角矩阵
U:Upper Triangle Matrix, 上三角矩阵
以方阵为例,矩阵对角线下面的元素都为0,矩阵的主要元素都在矩阵的上三角部分,称为上三角矩阵
矩阵的LU分解就是将一个矩阵分解成这样两个样子的矩阵,一般在LU分解中,会分解成一个单位下三角矩阵和一个上三角矩阵,这个上三角矩阵不一定是一个单位上三角矩阵。
以方阵为例,我们在对一个矩阵进行高斯消元法的时候,就是将主元下面的元素都化成0
高斯消元法的过程,通过初等变换,把一个矩阵变成了上三角矩阵。
等式的两边同时乘以这些单位矩阵的逆
可得
如果一切顺利的话,则可化为
现在我们以一个例子来说明
首先我们将第二个行向量减去第一个行向量乘以4,得
很明显,这一步初等变换的初等矩阵的逆矩阵为
我们再将第三个行向量减去第一个行向量乘以3,得
这一步初等变换的初等矩阵的逆矩阵再乘上上一步的逆矩阵,就变为
现在我们第一行主元下面的元素都变成了0,我们再来看第二行,一般我们会将第二行的主元归一,但从计算效率的角度考虑,我们就不把-3变成1了,而直接将其下面的元素变成0.此时我们将第三个行向量减去第二个行向量乘以3,得
这一步初等变换的初等矩阵的逆矩阵再乘上之前的逆矩阵,就变为
至此,我们的高斯消元的过程就结束了,为方便计算,主元位置不归一。我们可以看到高斯消元法得到的是一个上三角矩阵
,而整个逆过来的逆矩阵是一个单位下三角矩阵
,因为主元的位置并不归一,所以主对角线上的元素都为1
这样就将该矩阵分解成了两个矩阵
矩阵可以进行LU分解的条件,对A进行高斯消元的过程,不需要交换两行的位置
矩阵LU分解的意义
在解一个线性系统Ax=b的过程中,对A进行LU分解的时间复杂度:O(0.5n^3)
LU分解后就变成了
设Ux=y 则
求出y的时间复杂度O(n^2)
求出y后,求出x的时间复杂度O(n^2),所以整体解出线性系统的时间复杂度为O(0.5n^3) + 2O(n^2)
对比求解矩阵的逆的方法,求逆的时间复杂度为O(2*n^3)
然后再用矩阵的逆乘以的时间复杂度为O(n^2),则使用矩阵的逆来求解线性系统整体的时间复杂度为O(2*n^3) + O(n^2)
新增一个元组类
/** * 元组 * @param <T0> * @param <T1> */ @AllArgsConstructor public class Tuple<T0, T1> implements Cloneable { private T0 f0; private T1 f1; public int getArity() { return 2; } @SuppressWarnings("unchecked") public <T> T getField(int pos) { switch(pos) { case 0: return (T) this.f0; case 1: return (T) this.f1; default: throw new IndexOutOfBoundsException(String.valueOf(pos)); } } @SuppressWarnings("unchecked") public <T> void setField(T value, int pos) { switch(pos) { case 0: this.f0 = (T0) value; break; case 1: this.f1 = (T1) value; break; default: throw new IndexOutOfBoundsException(String.valueOf(pos)); } } public void setFields(T0 value0, T1 value1) { this.f0 = value0; this.f1 = value1; } public Tuple<T1, T0> swap() { return new Tuple<>(f1, f0); } @Override public String toString() { return "(" + f0 + "," + f1 + ")"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Tuple)) { return false; } @SuppressWarnings("rawtypes") Tuple tuple = (Tuple) o; if (f0 != null ? !f0.equals(tuple.f0) : tuple.f0 != null) { return false; } if (f1 != null ? !f1.equals(tuple.f1) : tuple.f1 != null) { return false; } return true; } @Override public int hashCode() { int result = f0 != null ? f0.hashCode() : 0; result = 31 * result + (f1 != null ? f1.hashCode() : 0); return result; } @Override protected Tuple<T0, T1> clone() throws CloneNotSupportedException { return new Tuple<>(this.f0, this.f1); } public static <T0, T1> Tuple<T0, T1> of(T0 value0, T1 value1) { return new Tuple<>(value0, value1); } }
Ranks接口添加矩阵的LU分解的方法
public interface Ranks<T extends Number> { /** * 矩阵的行列数 * @return */ int[] shape(); /** * 矩阵的行数 * @return */ int rowNum(); /** * 矩阵的列数 * @return */ int colNum(); /** * 矩阵的元素个数 * @return */ int size(); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int pos); /** * 获取矩阵中的一个元素 * @param pos * @return */ T get(int[] pos); /** * 设置矩阵中某个位置的元素 * @param pos * @param value */ void set(int[] pos,T value); /** * 获取一个行向量 * @param index * @return */ Attribute<T> rowVector(int index); /** * 获取一个列向量 * @param index * @return */ Attribute<T> colVector(int index); /** * 矩阵相加 * @param another * @return */ Ranks<T> add(Ranks<T> another,Addr<T> addr); /** * 矩阵相减 * @param another * @param addr * @return */ Ranks<T> sub(Ranks<T> another,Addr<T> addr); /** * 矩阵乘以一个数 * @param k * @param addr * @return */ Ranks<T> mul(T k,Addr<T> addr); /** * 矩阵除以一个数 * @param k * @param addr * @return */ Ranks<Double> div(double k,Addr<T> addr); /** * 矩阵取正的结果 * @return */ Ranks<T> pos(Addr<T> addr,T one); /** * 矩阵取负的结果 * @return */ Ranks<T> neg(Addr<T> addr,T negOne); /** * 矩阵与向量的点乘 * @param another * @param addr * @return */ Attribute<T> dot(Attribute<T> another,Addr<T> addr); /** * 矩阵与矩阵的点乘 * @param another * @param addr * @return */ Ranks<T> dot(Ranks<T> another,Addr<T> addr); /** * 转置矩阵 * @return */ Ranks<T> trans(); /** * 矩阵的逆 * @param addr * @return */ Ranks<T> invFunc(Addr<T> addr); /** * 矩阵的逆 * @param addr * @return */ Ranks<T> invMatrix(Addr<T> addr); /** * 矩阵的LU分解 * @param addr * @return */ Tuple<Ranks<T>,Ranks<T>> lu(Addr<T> addr); }
矩阵实现类
public class Matrix<T extends Number> implements Ranks<T>,Cloneable { private T[][] values; public Matrix(T[][] values) { this.values = values; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < values.length - 1; i++) { builder.append(Arrays.toString(values[i]) + ","); } builder.append(Arrays.toString(values[values.length - 1])); return "Matrix{" + "values=" + "[" + builder.toString() + "]" + '}'; } /** * 创建一个零矩阵 * @param row * @param col * @param addr * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> zero(int row,int col,Addr<T> addr) { T[][] values = (T[][])new Number[row][col]; for (int i = 0; i < row; i++) { Arrays.fill(values[i],addr.zero()); } return new Matrix<>(values); } /** * 创建一个单位矩阵 * @param n * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T extends Number> Ranks<T> identity(int n,Addr<T> addr) { T[][] values = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { Arrays.fill(values[i],addr.zero()); } for (int i = 0; i < n; i++) { values[i][i] = addr.one(); } return new Matrix<>(values); } @Override public int[] shape() { int[] res = new int[2]; res[0] = values.length; res[1] = values[0].length; return res; } @Override public int rowNum() { return shape()[0]; } @Override public int colNum() { return shape()[1]; } @Override public int size() { int row = rowNum(); int col = colNum(); return row * col; } @Override public T get(int pos) { if (pos >= size() || pos < 0) { throw new IllegalArgumentException("位置超出索引范围"); } int row = pos / colNum(); int col = pos % colNum(); return values[row][col]; } @Override public T get(int[] pos) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } return values[pos[0]][pos[1]]; } @Override public void set(int[] pos, T value) { if (pos.length != 2) { throw new IllegalArgumentException("位置长度超出范围"); } if (pos[0] >= rowNum() || pos[0] < 0 || pos[1] >= colNum() || pos[1] < 0) { throw new IllegalArgumentException("位置超出索引范围"); } values[pos[0]][pos[1]] = value; } @Override public Attribute<T> rowVector(int index) { if (index >= rowNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } return new Vector<>(values[index]); } @Override @SuppressWarnings("unchecked") public Attribute<T> colVector(int index) { if (index >= colNum() || index < 0) { throw new IllegalArgumentException("索引超出范围"); } T[] values = (T[])new Number[rowNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = this.values[i][index]; } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> add(Ranks<T> another,Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("加法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.add(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> sub(Ranks<T> another, Addr<T> addr) { if (!Arrays.equals(this.shape(),another.shape())) { throw new IllegalArgumentException("减法错误,两个矩阵的形状必须相同"); } T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.sub(this.get(new int[]{i,j}),another.get(new int[]{i,j})); } } this.values = values; return this; } @Override @SuppressWarnings("unchecked") public Ranks<T> mul(T k, Addr<T> addr) { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.mul(k,this.get(new int[]{i,j})); } } this.values = values; return this; } @Override public Ranks<Double> div(double k, Addr<T> addr) { Double[][] values = new Double[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { for (int j = 0; j < colNum(); j++) { values[i][j] = addr.div(this.get(new int[]{i,j}),k); } } return new Matrix<>(values); } @Override public Ranks<T> pos(Addr<T> addr,T one) { return mul(one,addr); } @Override public Ranks<T> neg(Addr<T> addr,T negOne) { return mul(negOne,addr); } @Override @SuppressWarnings("unchecked") public Attribute<T> dot(Attribute<T> another, Addr<T> addr) { if (this.colNum() != another.len()) { throw new IllegalArgumentException("矩阵列数需要与向量的长度相等"); } T[] values = (T[])new Number[this.rowNum()]; for (int i = 0; i < this.rowNum(); i++) { values[i] = this.rowVector(i).dot(another,addr); } return new Vector<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> dot(Ranks<T> another, Addr<T> addr) { if (this.colNum() != another.rowNum()) { throw new IllegalArgumentException("当前矩阵列数必须等于点乘矩阵行数"); } T[][] values = (T[][]) new Number[this.rowNum()][another.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < another.colNum(); j++) { values[i][j] = this.rowVector(i).dot(another.colVector(j),addr); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> trans() { T[][] values = (T[][])new Number[this.colNum()][this.rowNum()]; for (int i = 0; i < this.colNum(); i++) { for (int j = 0; j < this.rowNum(); j++) { values[i][j] = this.values[j][i]; } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> invFunc(Addr<T> addr) { if (this.rowNum() != this.colNum()) { return null; } int n = this.rowNum(); FuncGroup<T> ls = new LinearSystemFunc<>(this,Matrix.identity(n,addr)); if (!ls.gaussJordanElimination(addr)) { return null; } T[][] values = (T[][])new Number[this.rowNum()][((LinearSystemFunc)ls).getAb().colNum() - this.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < ((LinearSystemFunc)ls).getAb().colNum() - this.colNum(); j++) { values[i][j] = (T)((LinearSystemFunc)ls).getAb().get(new int[]{i,((LinearSystemFunc)ls).getAb().colNum() - this.colNum() + j}); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Ranks<T> invMatrix(Addr<T> addr) { if (this.rowNum() != this.colNum()) { return null; } int n = this.rowNum(); FuncGroup<T> ls = new LinearSystemMatrix<>(this,Matrix.identity(n,addr)); if (!ls.gaussJordanElimination(addr)) { return null; } T[][] values = (T[][])new Number[this.rowNum()][((LinearSystemMatrix)ls).getAb().colNum() - this.colNum()]; for (int i = 0; i < this.rowNum(); i++) { for (int j = 0; j < ((LinearSystemMatrix)ls).getAb().colNum() - this.colNum(); j++) { values[i][j] = (T)((LinearSystemMatrix)ls).getAb().get(new int[]{i,((LinearSystemMatrix)ls).getAb().colNum() - this.colNum() + j}); } } return new Matrix<>(values); } @Override @SuppressWarnings("unchecked") public Tuple<Ranks<T>,Ranks<T>> lu(Addr<T> addr) { // if (this.rowNum() != this.colNum()) { // throw new IllegalArgumentException("矩阵必须为方阵"); // } int n = this.rowNum(); int l = this.colNum(); if (l > n) { T[][] A = (T[][])new Number[n][l]; //此处不能直接把this.values赋给A,否则会改变原矩阵的values for (int i = 0; i < n; i++) { A[i] = Arrays.copyOf(this.values[i],n); } } T[][] A = (T[][])new Number[n][n]; //此处不能直接把this.values赋给A,否则会改变原矩阵的values for (int i = 0; i < n; i++) { A[i] = Arrays.copyOf(this.values[i],n); } //构建一个下三角矩阵的二维数组,先将其初始为一个单位矩阵 T[][] L = (T[][])new Number[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) { L[i][j] = addr.one(); }else { L[i][j] = addr.zero(); } } } for (int i = 0; i < n; i++) { //主元不能为0 if (addr.equals(A[i][i],addr.zero())) { return null; }else { //将主元下面的元素变为0 for (int j = i + 1; j < n; j++) { //获取初等矩阵逆矩阵的系数 T p = addr.div(A[j][i],A[i][i]); for (int k = 0; k < n; k++) { T u = A[i][k]; A[j][k] = addr.sub(A[j][k],addr.mul(p,u)); } //将初等矩阵逆矩阵的系数填充下三角矩阵 L[j][i] = p; } } } return new Tuple<>(new Matrix(L),new Matrix(A)); } @Override @SuppressWarnings("unchecked") public Matrix clone() throws CloneNotSupportedException { T[][] values = (T[][])new Number[this.rowNum()][this.colNum()]; for (int i = 0; i < rowNum(); i++) { values[i] = Arrays.copyOf(this.values[i],this.colNum()); } return new Matrix(values); } @SuppressWarnings("unchecked") public static void main(String[] args) throws CloneNotSupportedException { Ranks<Integer> matrix = new Matrix<>(new Integer[][]{{1,2,3},{3,4,5}}); System.out.println(matrix); System.out.println(Arrays.toString(matrix.shape())); System.out.println(matrix.rowNum()); System.out.println(matrix.colNum()); System.out.println(matrix.size()); System.out.println(matrix.get(4)); System.out.println(matrix.get(new int[]{0,0})); System.out.println(matrix.rowVector(1)); System.out.println(matrix.colVector(2)); Ranks<Integer> matrix1 = new Matrix<>(new Integer[][]{{7,8,9},{10,20,30}}); Addr<Integer> addr = new Addr<Integer>() { @Override public Integer zero() { return 0; } @Override public Integer one() { return 1; } @Override public Integer add(Integer lhs, Integer rhs) { return lhs + rhs; } @Override public Integer sub(Integer lhs, Integer rhs) { return lhs - rhs; } @Override public Integer mul(Integer k, Integer hs) { return k * hs; } @Override public Integer square(Integer hs) { return hs * hs; } @Override public double prescription(Integer hs) { return Math.sqrt(hs); } @Override public double div(Integer hs, double nhs) { return hs / nhs; } @Override public Integer div(Integer hs, Integer nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Integer lhs, Integer rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Integer lhs, Integer rhs) { return lhs == rhs; } @Override public Integer neg(Integer hs) { return -hs; } }; System.out.println(matrix.trans()); Ranks<Integer> copy = ((Matrix)matrix).clone(); Attribute<Integer> vector = new Vector<>(new Integer[]{9,33,97}); Ranks<Integer> matrix2 = new Matrix<>(new Integer[][]{{7,9},{11,33},{42,97}}); //[(1,2,3),(3,4,5)]*(23,65,79) System.out.println(matrix.dot(vector,addr)); // [(1,2,3),(3,4,5)]*[(7,9),(11,33),(42,97)] System.out.println(matrix.dot(matrix2,addr)); Ranks<Integer> copy1 = ((Matrix)matrix).clone(); System.out.println(matrix.add(matrix1,addr)); System.out.println(matrix1.sub(copy,addr)); System.out.println(copy.div(2,addr)); System.out.println(copy.pos(addr,1)); System.out.println(copy.neg(addr,-1)); System.out.println(copy1.mul(3,addr)); System.out.println(Matrix.zero(2,3,addr)); Ranks<Integer> I = Matrix.identity(4, addr); System.out.println(I); Matrix<Integer> idenTest = new Matrix<>(new Integer[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}); System.out.println(idenTest.dot(I,addr)); System.out.println(I.dot(idenTest,addr)); Addr<Double> addr1 = new Addr<Double>() { @Override public Double zero() { return 0.0; } @Override public Double one() { return 1.0; } @Override public Double add(Double lhs, Double rhs) { return lhs + rhs; } @Override public Double sub(Double lhs, Double rhs) { return lhs - rhs; } @Override public Double mul(Double k, Double hs) { return k * hs; } @Override public Double square(Double hs) { return hs * hs; } @Override public double prescription(Double hs) { return Math.sqrt(hs); } @Override public double div(Double hs, double nhs) { return hs / nhs; } @Override public Double div(Double hs, Double nhs) { return hs / nhs; } @Override public double acos(double hs) { return Math.acos(hs); } @Override public double toDegree(double hs) { return Math.toDegrees(hs); } @Override public int compair(Double lhs, Double rhs) { if (lhs - rhs > 0) { return 1; }else if (lhs - rhs < 0) { return -1; }else { return 0; } } @Override public boolean equals(Double lhs, Double rhs) { double dis = 1e-8; if (Math.abs(lhs - rhs) < dis) { return true; } return false; } @Override public Double neg(Double hs) { return -hs; } }; Ranks<Double> A = new Matrix<>(new Double[][]{{1.0,2.0},{3.0,4.0}}); System.out.println(A.invFunc(addr1)); System.out.println(A.invMatrix(addr1)); Ranks<Integer> B = new Matrix<>(new Integer[][]{{1,2,3},{4,5,6},{3,-3,5}}); Tuple<Ranks<Integer>,Ranks<Integer>> lu = B.lu(addr); Ranks<Integer> L = lu.getField(0); System.out.println(L); Ranks<Integer> U = lu.getField(1); System.out.println(U); System.out.println(L.dot(U,addr)); } }
运行结果
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
[2, 3]
2
3
6
4
1
Vector{values=[3, 4, 5]}
Vector{values=[3, 5]}
Matrix{values=[[1, 3],[2, 4],[3, 5]]}
Vector{values=[366, 644]}
Matrix{values=[[155, 366],[275, 644]]}
Matrix{values=[[8, 10, 12],[13, 24, 35]]}
Matrix{values=[[6, 6, 6],[7, 16, 25]]}
Matrix{values=[[0.5, 1.0, 1.5],[1.5, 2.0, 2.5]]}
Matrix{values=[[1, 2, 3],[3, 4, 5]]}
Matrix{values=[[-1, -2, -3],[-3, -4, -5]]}
Matrix{values=[[3, 6, 9],[9, 12, 15]]}
Matrix{values=[[0, 0, 0],[0, 0, 0]]}
Matrix{values=[[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]}
Matrix{values=[[-1.9999999999999996, 0.9999999999999998],[1.4999999999999998, -0.49999999999999994]]}
Matrix{values=[[-1.9999999999999996, 0.9999999999999998],[1.4999999999999998, -0.4999999999999999]]}
Matrix{values=[[1, 0, 0],[4, 1, 0],[3, 3, 1]]}
Matrix{values=[[1, 2, 3],[0, -3, -6],[0, 0, 14]]}
Matrix{values=[[1, 2, 3],[4, 5, 6],[3, -3, 5]]}
Python代码
from .Vector import Vector from ._global import is_zero class Matrix: def __init__(self, list2d): self._values = [row[:] for row in list2d] @classmethod def zero(cls, r, c): # 返回一个r行c列的零矩阵 return cls([[0] * c for _ in range(r)]) @classmethod def identity(cls, n): # 返回一个n行n列的单位矩阵 m = [[0] * n for _ in range(n)] for i in range(n): m[i][i] = 1 return cls(m) def trans(self): # 返回矩阵的转置矩阵 return Matrix([[e for e in self.col_vector(i)] for i in range(self.col_num())]) def inv_func(self): # 返回矩阵的逆 from .LinearSystemFunc import LinearSystemFunc if self.row_num() != self.col_num(): return None n = self.row_num() ls = LinearSystemFunc(self, Matrix.identity(n)) if not ls.gauss_jordan_elimination(): return None invA = [[row[i] for i in range(n, 2 * n)] for row in ls.Ab] return Matrix(invA) def inv_matrix(self): # 返回矩阵的逆 from .LinearSystemMatrix import LinearSystemMatrix if self.row_num() != self.col_num(): return None n = self.row_num() ls = LinearSystemMatrix(self, Matrix.identity(n)) if not ls.gauss_jordan_elimination(): return None invA = [[row[i] for i in range(n, 2 * n)] for row in ls.Ab._values] return Matrix(invA) def lu(self): # 方阵的LU分解 assert self.row_num() == self.col_num(), "矩阵必须为方阵" n = self.row_num() A = [self.row_vector(i) for i in range(n)] L = [[1 if i == j else 0 for i in range(n)] for j in range(n)] for i in range(n): # 看A[i][i]位置是否可以为主元 if is_zero(A[i][i]): return None, None else: for j in range(i + 1, n): p = A[j][i] / A[i][i] A[j] = A[j] - p * A[i] L[j][i] = p return Matrix(L), Matrix([A[i].underlying_list() for i in range(n)]) def __add__(self, another): # 返回两个矩阵的加法结果 assert self.shape() == another.shape(), \ "加法错误,两个矩阵的形状必须相同" return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def __sub__(self, another): # 返回两个矩阵的减法结果 assert self.shape() == another.shape(), \ "减法错误,两个矩阵的形状必须相同" return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))] for i in range(self.row_num())]) def dot(self, another): if isinstance(another, Vector): # 矩阵和向量的乘法 assert self.col_num() == len(another), \ "矩阵列数需要与向量的长度相等" return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())]) if isinstance(another, Matrix): # 矩阵和矩阵的乘法 assert self.col_num() == another.row_num(), \ "当前矩阵列数必须等于点乘矩阵行数" return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())] for i in range(self.row_num())]) def __mul__(self, k): # 返回矩阵的数量乘结果: self * k return Matrix([[e * k for e in self.row_vector(i)] for i in range(self.row_num())]) def __rmul__(self, k): # 返回矩阵的数量乘结果: k * self return self * k def __truediv__(self, k): # 返回数量除法的结果矩阵: self / k return (1 / k) * self def __pos__(self): # 返回矩阵取正的结果 return 1 * self def __neg__(self): # 返回矩阵取负的结果 return -1 * self def row_vector(self, index): # 返回矩阵的第index个行向量 return Vector(self._values[index]) def col_vector(self, index): # 返回矩阵的第index个列向量 return Vector([row[index] for row in self._values]) def __getitem__(self, pos): # 返回矩阵pos位置的元素 r, c = pos return self._values[r][c] def __setitem__(self, pos, value): # 设置pos位置的元素 r, c = pos self._values[r][c] = value def size(self): # 返回矩阵中元素的个数 r, c = self.shape() return r * c def row_num(self): # 返回矩阵的行数 return self.shape()[0] __len__ = row_num def col_num(self): # 返回矩阵的列数 return self.shape()[1] def shape(self): # 返回矩阵的形状:(行数,列数) return len(self._values), len(self._values[0]) def __repr__(self): return "Matrix({})".format(self._values) __str__ = __repr__
from playLA.Matrix import Matrix from playLA.Vector import Vector if __name__ == "__main__": matrix = Matrix([[1, 2, 3], [3, 4, 5]]) print(matrix) print("matrix.shape = {}".format(matrix.shape())) print("matrix.size = {}".format(matrix.size())) print("len(matrix) = {}".format(len(matrix))) print("matrix[0][0] = {}".format(matrix[0, 0])) print(matrix.row_vector(1)) print(matrix.col_vector(0)) matrix2 = Matrix([[7, 8, 9], [10, 20, 30]]) print("add: {}".format(matrix + matrix2)) print("subtract: {}".format(matrix2 - matrix)) print("scalar-mul: {}".format(matrix * 3)) print("scalar-mul: {}".format(3 * matrix)) print("zero_2_3: {}".format(Matrix.zero(2, 3))) vec = Vector([23, 65, 79]) print("matrix.dot(vec) = {}".format(matrix.dot(vec))) matrix3 = Matrix([[7, 9], [11, 33], [42, 97]]) print("matrix.dot(matrix3) = {}".format(matrix.dot(matrix3))) print("matrix.trans = {}".format(matrix.trans())) I = Matrix.identity(4) print(I) idenTest = Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) print(idenTest.dot(I)) print(I.dot(idenTest)) A = Matrix([[1, 2], [3, 4]]) print(A.inv_func()) print(A.inv_matrix()) B = Matrix([[1, 2, 3], [4, 5, 6], [3, -3, 5]]) L, U = B.lu() print(L) print(U) print(L.dot(U))
运行结果
Matrix([[1, 2, 3], [3, 4, 5]])
matrix.shape = (2, 3)
matrix.size = 6
len(matrix) = 2
matrix[0][0] = 1
(3, 4, 5)
(1, 3)
add: Matrix([[8, 10, 12], [13, 24, 35]])
subtract: Matrix([[6, 6, 6], [7, 16, 25]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
scalar-mul: Matrix([[3, 6, 9], [9, 12, 15]])
zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
matrix.dot(vec) = (390, 724)
matrix.dot(matrix3) = Matrix([[155, 366], [275, 644]])
matrix.trans = Matrix([[1, 3], [2, 4], [3, 5]])
Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]])
Matrix([[-1.9999999999999996, 0.9999999999999998], [1.4999999999999998, -0.4999999999999999]])
Matrix([[1, 0, 0], [4.0, 1, 0], [3.0, 3.0, 1]])
Matrix([[1, 2, 3], [0.0, -3.0, -6.0], [0.0, 0.0, 14.0]])
Matrix([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [3.0, -3.0, 5.0]])
线性相关和线性无关
线性组合
对于若干个n维向量,每一个向量乘以一个标量的和称为线性组合
称为这些向量的的一个线性组合
比如三维空间中,有三个标准单位向量
则在三维空间中的任何一个向量都可以看成这样的线性组合
在矩阵与向量的乘法中,由于矩阵可以看成是向量的批处理,从列视角来看,它同样可以看成是向量与系数乘法的和,则也是一个线性组合
将矩阵看成一个新的空间坐标系,那么矩阵乘以向量的结果为新空间坐标系中的向量的坐标对应的原直角坐标系中的坐标
这个视角在这里将会非常有意义
它就好比将(4,1),(2,3)当成直角坐标系的(1,0),(0,1)一样
在高斯消元法中,经过变换,得到的一个变换方式,同样是一种线性组合
线性相关和线性无关
对于若干个n维向量,存在一组k不全为0,使得
,则称
线性相关
如果线性相关
其中一个向量可以写成其他向量的线性组合
证明:因为线性相关,存在不全为0的
设不为0
根据线性组合的定义,此命题正方向成立。
根据等式的变换,我们可以看到为-1,不为0,此命题反方向成立。
-------------------------------------------------------------------------------------------------------------
对于若干个n维向量 ,只有k全部为0时,才有
,则称
线性无关
意味着任何一个向量都不可以表示成其他向量的线性组合
证明线性无关,其中e向量是标准单位向量,在三维空间中的三个标准单位向量如下
反证法:假设线性相关,存在一组不全为0的k:
假设
由于本身表示的是第i个元素为1,其他元素为0,而上面的等式第i个元素为0,则等式明显不成立,所以
是线性无关的。
在线代中,我们更加关注的是线性无关。
线性相关的重要性质
m个n维向量,若m > n,则
线性相关
比如100个三维向量一定线性相关,4个三维向量一定线性相关,但3个三维向量就要具体分析了!
我们先来看2个二维向量
是否线性相关?
根据线性相关的概念,是否存在不全为0,满足
该式可以转换成一个矩阵和向量的乘法是否只有唯一0解的问题。很明显这是一个齐次线性方程组,我们将其系数矩阵
化成行最简形式
,由于系数矩阵非零行1行 < 未知数个数2,第二列为无主元的自由列,所以它有无数个解(不只有0解),则这两个二维向量u和v一定是线性相关的
证明:m个n维向量,若m > n,则
线性相关
根据线性相关的概念,是否存在不全为0,满足
转换成矩阵与向量的乘法
这里系数矩阵中行数是向量的维度n,列数是未知数的个数m,当我们对系数矩阵化成行最简形式的时候,它的非零行一定小于未知数的个数,因为行总数是小于列数的,而非零行最大也就是行总数,必然小于列数——未知数的个数,所以它是有无数解的,而不仅仅有唯一的零解,所以命题成立。
那么m个n维向量,何时线性无关呢?
同样我们化成
只有唯一零解的时候,即系数矩阵非零行 = 未知数的个数,即列数
在我们说矩阵的逆和很多命题连接在一起的时候,有五个等价命题:
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
- Ax=b只有唯一解
由第2条看到,它是一种唯一零解的情况,说明要想达到线性无关,需要满足m=n,系数矩阵是一个方阵,且这个系数矩阵可逆
n * n的方阵A,其列向量由组成,即
,则A可逆
线性无关
由此对于方阵A的等价命题可以添加第6条
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
- Ax=b只有唯一解
- 方阵A的列向量线性无关
直观理解线性相关
如果线性相关
其中一个向量可以写成其他向量的线性组合
在二维平面中,随便取两个向量、
,再随便取第三个向量
那么一定可以表示成
、
的线性组合,无论这个
的方向是怎样的。
它们的不同,只不过是k的正负罢了
如果、
共线,则
无法表示成
、
的线性组合,但在没有
的情况下,
、
本身就是线性相关的,因为
= k
无论是还是
,我们都可以表述成下面的形式
,只不过
为0罢了
若向量中存在零向量,则
线性相关
证明:假设,我们构建一组数据,除了
,其他都为0
,由于
,而其他系数都为0,则
正因为不等于0,满足线性相关的定义,所以命题成立。
在二维平面中,如果两向量不共线,则这两个向量线性无关(因为没有一个向量可以写成另外一个向量乘以一个标量)
在三维空间中,三个向量不共面,则这三个向量线性无关
如果线性无关
没有一个向量可以写成其他向量的线性组合
生成空间
二维空间中的任何向量,都可以表示为u和v的线形组合(u、v不共线),我们说u和v可以生成整个二维空间。
u、v、w也可以生成整个二维空间
若空间中的所有向量,都可以被表示成的线形组合,则称:这些向量可以生成这个空间
对于一个n维空间,至少需要n个向量才能够生成
证明:反证法 假设m个向量可以生成n维空间。且m < n
根据定义,则n维空间中的任何一个向量,都可以表示成这m个向量的线性组合,我们把这个任意向量定义为u,则有
它等价于
这就是一个非齐次线性系统,而行数>列数(因为n > m),则将系数矩阵化为行最简形式后,一定存在零行
上面的非齐次线性系统,用增广矩阵来表示如下
进行高斯-约旦消元法后,得到的结果大致如下
由于存在零行,由于我们的u是任意取的,我们无法保证在系数矩阵零行的位置,它的u的值是0,可能是非0的,所以这个线性系统是无解的,矛盾,命题得证。
那么n个向量一定可以生成n维空间吗?答案是否定的
比如在二维空间中,两个向量共线
那么,n个向量何时可以生成整个空间?
可以肯定的是
该线性系统一定是有解的。我们将系数矩阵化为行最简形式,如下所示的时候
该线性系统才一定是有解的,由于该系数矩阵是一个方阵,非零行等于未知数个数,如果有解,它只可能有唯一解
在我们之前说的方阵A的等价命题中
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
- Ax=b只有唯一解
- 方阵A的列向量线性无关
根据5可知,只有系数矩阵可逆时,可以生成整个空间,由此方阵A的等价命题又可以增加一条
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
- Ax=b只有唯一解
- 方阵A的列向量线性无关
- 方阵A的列向量可以生成n维空间
空间的基
若m个向量生成n维空间,m最小为n
m个n维向量,若m > n,则
线性相关
若m个n维向量线性无关,m最大为n
若一组向量可以生成整个n维空间,且线性无关,这组向量一定有n个,则称这组向量为这个n维空间的一组基
n个n维向量,若他们是这个n维空间的基
生成整个n维空间
线性无关
一个空间有无数组基
比如在二维空间中,随便取两个不共线的向量u、v,那么这两个向量就是这个二维空间的基
我们最常用的那组基就是标准单位向量和
是二维空间的基;u、v也是二维空间的基。它们应该具有同样的性质。首先它们都能生成空间,其次它们都是线性无关的!
在二维空间中,任何一个向量(或者是点)都可以表示成的线性组合
在二维空间中,任何一个向量(或者是点)都可以表示成u和v的线性组合!
我们可以把看成是一个二维空间的坐标系(直角坐标系),u、v同样是二维空间的坐标系。我们可以在
坐标系中观察到二维空间的任意一个点,在u、v坐标系中也同样可以观察到二维空间的任意一个点。当然,转换坐标系后,同样一个点的表示方法不同,虽然表示方法不同,但是每一个点都可以通过空间的基的线性组合的方式来表示出来。这个结论在n维空间中也是一样的,且在一组基下的表示方法是唯一的。
在n维空间中,任何一个向量(或者是点)都可以表示成的线性组合,且表示方法唯一
证明: 对n维空间中的任何一个向量u,看是否存在一组k,使得:
等价于
转换为
我们现在要看的是这个线性系统是否有解,还是否有唯一解,这是十分明显的,系数矩阵就是一个单位矩阵,此命题得证!
-----------------------------------------------------------------------------
在n维空间,如果给定一组基,任何一个向量(或者是点)都可以表示成这组基的线性组合,且表示方法唯一
证明: 对n维空间中的任何一个向量u,看是否一定存在一组k,使得:
等价于
由于这组v是n维空间的基,意味着这组v可生成整个空间,且线性无关
根据之前方阵A的等价命题
- 对于方阵A,矩阵A可逆,A是非奇异矩阵
- 线性系统Ax=0(齐次线性方程组)只有唯一解,x=0
- 矩阵的行最简形式rref(A)=I
- A可以表示成一系列初等矩阵的乘积
- Ax=b只有唯一解
- 方阵A的列向量线性无关
- 方阵A的列向量可以生成n维空间
中的第5条可知,该线性系统只有唯一解,此命题得证
之前我们说过矩阵表示空间
其实就是说直角坐标系中(12,8)这个点在u、v这组向量所组成的一组基中,表示成了(2,2)
即
空间的基的性质
n维空间中,任意n个线性无关的向量,一定是这个n维空间的基。
n维空间中,如果n个向量可以生成整个空间,则这n个向量,是这个n维空间的基。
这是三维空间中的两个向量,它们是线性无关的,但不可生成整个空间
这是三维空间中的三个向量,它们是线性无关的,可以生成整个空间
这是三维空间中的四个向量,它们是线性相关的,可以生成整个空间
如果n维空间的p个向量线性无关,则:p <= n
如果n维空间的p个向量线性相关,则:p > n
如果p个向量可以生成n维空间,则:p >= n
如果p个向量是n维空间的基,则:p = n
如果一组向量可以生成n维空间,如果其中的一个向量,是其他向量的线性组合,则删除这个向量,剩下的向量仍可以生成整个n维空间
证明: (其中的一个向量,是其他向量的线性组合)
(
可以生成n维空间,
是这个n维空间中任意一个向量)
将第一个式子代入第二个式子,消除,得到的结果依然是
中除
外其他的向量组合依然为这个n维空间中的任意向量
,此题得证。
如果一组向量可以生成n维空间,则这组向量的一个子集,是n维空间的一组基。
空间,向量空间和欧几里得空间
空间是一个集合
欧几里得空间是有序实数元组的集合,如(6,66)属于二维欧几里得空间,记作;(3.14,0,sqrt(2))属于三维欧几里得空间,记作
;而n维欧几里得空间记作
;又或者说欧几里得空间是点集;是起点为原点的向量集合。其实除了欧几里得空间,还有很多不同的空间,只不过欧几里得空间是我们最常用的空间罢了。
而向量空间表示空间中的元素都是"向量"
向量必须要能进行两大运算操作——向量加法、向量数量乘法
向量不能随便定义,必须满足十条性质
对于一个向量空间V
- 如果u,v都属于V,则u + v属于V
- 如果u属于V,k是一个实数,则ku属于V
这两条性质在数学上,被称为封闭(closure)
和加法相关的性质
- 加法交换律:u + v = v + u
- 加法结合律: (u + v) + w = u + (v + w)
- 存在O(零向量)属于向量空间,使得u + O = u
- 对于每一个u存在-u,使得u + (-u) = O
和数量乘法相关的性质
- 数量乘结合律: (kc)u = k(cu)
- 基于向量的数量乘分配律: k(u + v) = ku + kv
- 基于实数的数量乘分配律: (k + c)u = ku + cu
- 1 * u = u
欧几里得空间是向量空间,因为它满足向量的加法和数量乘法,且满足这十条性质
除了加法和数量乘法外,存在零向量,存在正负的向量等等......
子空间
假设V是一个向量空间,如果S是V的子集,且S还是一个向量空间,则称S是V的一个子空间。
比如所有2阶方阵,形成一个向量空间V。所有如下形式的矩阵,形成一个向量空间
S,S是V的子空间
再比如如上形式的矩阵,形成一个向量空间V
所有如下形式的矩阵S,不形成一个向量空间,因为它不满足向量加法,数量乘法依然属于这个空间
所以S不是V的子空间,但是是V的子集
所以又可以说,假设V是一个向量空间,如果S是V的子集,且S对加法和数量乘法封闭,则称S是V的一个子空间。
这里可以说如果S对加法和数量乘法封闭,则一定满足向量的十大性质。
向量空间的性质
若V是一个向量空间,则满足
- 存在O(零向量)属于向量空间,使得u + O = u
- 如果u属于V,k是一个实数,则ku属于V
若V是一个向量空间,则0u = O
证明: 0 = 0 + 0则0u = (0 + 0)u,根据数量乘法分配律,则有0u = 0u + 0u
因为向量空间V中的每一个向量u,都存在-u,使得u + (-u) = O
假设对于0u,存在-0u,使得0u + (-0u) = O
0u + (-0u) = 0u + 0u + (-0u)
O = 0u + O
由于u + O = u,则0u + O = 0u,最终得0u = O,得证
- 对于每一个u存在-u,使得u + (-u) = O
- 如果u属于V,k是一个实数,则ku属于V
若V是一个向量空间,则: -u = -1u
证明: 根据十大性质的第十条,1u = u,则u + (-1u) = 1u + (-1u) = (1 - 1)u = 0u = O
因为u + (-u) = O,则-u = -1u,得证
- 存在O(零向量)属于向量空间,使得u + O = u
- 对于每一个u存在-u属于S,使得u + (-u) = O
- 0u属于S,即O属于S
- -1u属于S,即-u属于S
直观理解欧几里得空间的子空间
在二维的欧几里得空间中,我们来看一下零向量O和互为相反的向量u和-u
我们画一条通过原点的直线,这条直线就是二维欧几里得空间的子空间。在这条直线上的向量满足那十条性质,对加法和数量乘法封闭
但是一旦我们将这条直线进行平移,不再通过原点,我们来看一下它是否还是二维欧几里得空间的子空间
首先我们可以看到,它不包含零向量,这条直线上的向量相加后的向量不在这条直线上,而且某条向量的反向量也不在这条直线上,所以我们可以断定这条直线不是二维欧几里得空间的子空间。
我们来看通过原点的直线是否一定是二维欧几里得空间的子空间,我们通过原点画出一条射线
由图中可以看到,虽然它满足对向量加法封闭,但是它不满足对数量乘法封闭,因为数量乘法的实数可以是一个负数,对于在这个直线上的向量乘以一个负数,很明显不在这条直线上,所以它不是二维欧几里得空间的子空间。
对于三维欧几里得空间来说,过原点的一个平面,是三维欧几里得空间的一个子空间。过原点的一个直线,是三维欧几里得空间的一个子空间。原点本身,是三维欧几里得空间的一个子空间。
对于n维欧几里得空间来说,过原点的一个m维空间(m < n),是n维欧几里得空间的一个子空间。
维度
之前我们在说空间的基的时候知道,它是一组向量
- 这组向量可以生成空间
- 它们是线性无关的
一个空间的基中,向量的个数,称为维度
二维欧几里得空间的维度为2,它的标准正交基是,正好是2个向量
记作
三维欧几里得空间的维度为3,它的标准正交基是,正好是3个向量
记作
n维欧几里得空间的维度为n。记作
一个欧几里得空间的任何一组基,其中的向量个数是相同的。
子空间和维度
之前我们知道,这条直线是二维欧几里得空间的子空间。任何一个这条直线上的非零向量都可以生成这条直线(子空间)上的所有向量,因为这条直线上的所有向量都可以表示成任何一个非零向量的线性组合。这个非零向量,就是这个子空间的一组基。这个子空间的维度为1.
虽然对于三维欧几里得空间来说,但是
- 过原点的一个平面,是三维欧几里得空间的一个子空间
,
- 过原点的一个直线,是三维欧几里得空间的一个子空间
,
- 原点本身,是三维欧几里得空间的一个子空间
,
。跟其他空间不同的是,0维空间没有无数组基,它只有零向量O这一个基。对于这种情况,我们认为它是没有任何自由度的,它被压缩在了O这一个位置。
一个欧几里得空间中的每一个有序实数元组包含n个元素,这个空间的维度一定为n吗?因为有子空间的存在,这个命题肯定是错误的。我们来看一个例子
被向量u=(2,0,0);v=(-1,0,0);w=(0,0,1)生成的空间,维度是多少?
之前我们提到过如果一组向量可以生成n维空间,则这组向量的一个子集,是n维空间的一组基。
在这里如果u、v、w三个向量线性无关,则可以生成一个三维空间。但很明显,u和v是线性相关的,u = -2v或者v = -1/2u,所以可以删除u(或v),而u(或v)和w是线性无关的,所以这个生成空间的维度为2,它所代表的空间样式如下,它只能生成一个平面
一组n维向量的生成空间,是n维空间的子空间。比如像上面的u、v、w是三维向量,其生成空间是三维空间的子空间-->二维空间
一组向量属于空间V,则它们的生成空间是V的子空间。
行空间和矩阵的行秩
上面的题目中,我们可以一眼看出维度是2,但是并不是所有的一组向量都是这么容易被看出来的
高斯-约旦消元法的结果的每一行,是原来矩阵各行的一个线性组合,如
以最后一行为例,由于,可得
,可见它的行向量
是
和
的线性组合,这就意味着第三个行向量跟其他所有的行向量是线性相关的,也就是说
是可以被删除的那一个。
给出一组n维向量,其生成空间的维度是多少?
找到这组向量有多少和其他向量线性相关。
将这组向量按照行排列成一个矩阵,将这个矩阵执行高斯-约旦消元法,化为行最简形式(RREF),零行表示它的向量可以表示其他向量的线性组合即线性相关,可以把这些向量删除。其中的非零行可以表示这一组向量生成空间的基,非零行的个数即为其生成空间的维度。
对于一个矩阵
行向量生成的空间,称为行空间(Row Space)
列向量生成的空间,称为列空间(Column Space)
对于一个非方阵,如
它的行空间是4维空间的子集,列空间是3维空间的子集。
对于一个m行n列的矩阵,行空间是n维空间的子集,行空间的维度<=n;对于一个m行n列的矩阵,列空间是m维空间的子集,列空间的维度<=m.
一个矩阵的行最简形式的非零行数量称为矩阵的行秩(Row Rank),行空间的维度,为矩阵的行秩。(这里需要区分的是,空间是没有行秩的,矩阵是没有维度的)
求出行空间的一组基?其实通过高斯-约旦消元法化为行最简形式,非零行的行向量就是行空间的一组基。因为在原始矩阵行空间中的任何一个向量都跟行最简形式的非零行的行向量线性相关。
列空间
被向量u=(2,0,0);v=(-1,0,0);w=(0,0,1)生成的空间,维度是多少?
之前我们是以行的形式来看待的,现在我们以列的形式来看待这个问题
但无论是行的形式还是列的形式,依然是看这组向量是否线性无关。现在我们看这组方程组是否只有唯一的解。很明显这是一个齐次线性方程组,我们来看它的系数矩阵
将其化为行最简形式,我们可以看到有2个主元列,1个自由列。自由列上的元素意味着可以随便取值,当然可以取非零值。将行最简形式化为方程组为
k1 = 0.5k2
k3 = 0
由该方程组可知,k1和k2是线性相关的,而k1(或者k2)跟k3是线性无关的。
这就意味着自由列的个数,为可以表示为其他向量线性组合的向量个数;主元列的个数,为线性无关的向量个数。主元列的个数,为维度!主元列的个数,为列秩(Column Rank),所以这里列空间的维度以及列秩为2。
对于一个矩阵,列向量生成的空间,称为列空间(Column Space)。对于一个m行n列的矩阵,列空间是m维空间的子空间。一个矩阵的行最简形式的主元列数量称为矩阵的列秩。列空间的维度,为矩阵的列秩。
求列空间的一组基?原矩阵化为行最简形式的主元列所对应的原矩阵的列,是列空间的一组基。如
这里的(2,0,0)和(0,0,1)是原矩阵中的列向量,这两个向量对应着行最简形式主元列的位置,它们就是列空间的一组基。
这里需要注意的是和行空间的区别,行空间的行最简形式中的非零行,是行空间的一组基。而列空间的一组基在原矩阵中,而不在行最简形式中。