解行列式程序 python代码实现
制作背景
大二上学期结束要放假时,线性代数有一个小作业:做一个阶行列式的程序。由于我此时最熟悉的语言是Python,所以就用python做的。我猜到网上应该有很多答案,但是为了锻炼,我没查资料,我尽可能自己造轮子。也没引用到任何库,就按上课老师讲的思路来的。
现功能
理论上能计算任意阶的行列式,但由于性能和算法原因,只能计算1~10阶。
主要思路
n阶行列式用二维数组表示
用户逐行输入行列式
实现计算行列式的方法
获取行列式的每一条路径,(路径的每个数的每行每列都不重复)
例如:
1 2 3
4 5 6
7 8 9
1->5->9
1->6->8
2->4->9
2->6->7
3->4->8
3->5->7
如果把每个路径上的数字改为对应的x坐标,就是:
0 1 2 3
.--------> x
1 | 1 2 3
2 | 4 5 6
3 | 7 8 9
y
1->2->3
1->3->2
2->1->3
2->3->1
3->1->2
3->2->1
这样,获取路径的数字可以通过获取二维数组的下标获得,二维数组的下标相当于上面转换后的路径。观察发现,获取n阶行列式的所有路径问题转化成了求n的全排列问题。
若获取n的全排列,可以先画(构建)个树结构
n=3:
start
/ | \
/ | \
1 2 3
/| /\ |\
2 3 1 3 1 2
| | | | | |
3 2 3 1 2 1
构建这样一棵树,从上到下的每条路径都为要获取的路径。
创建节点类
节点 {
data: 存放此节点数据
child: 存放子节点们的集合
father: 存放父节点
}
用递归法添加节点
当前节点为空节点
// 对应 add() 函数
递归函数 (当前节点 当前路径上已有的节点集合) {
遍历 n {
如果n 不在 当前路径上已有的节点集合 中 {
创建 新节点
新节点的 父 为当前节点
新节点的 子 添加n
添加 新节点 到当前路径上已有的节点集合
如果 当前路径上已有的节点集合长度 <= n {
递归函数 (新节点 当前路径上已有的节点集合)
}
}
}
}
获取每条路径
具体代码对应 getAllPath() 函数
递归的进入每个分支
归到了最深,则把当前节点一直到最祖先节点的路径反添加到记录
把当前节点一直到最祖先节点的路径反添加到记录的方法具体见 getPath() 递归函数
获取每条路径的所有x坐标位数字的逆序数
把每条路径的所有实际数据相乘,得到结果
如果逆序数为偶数,则该路径的结果为正,奇数则负
将所有结果先加,得到最终结果。
源代码
# -*- encoding: utf-8 -*-
"""
计算行列式的程序
2021年1月15日
by littlefean
"""
def main():
while True:
n = eval(input("请输入行列式的阶数:"))
if n > 0:
temp = []
array = []
for y in range(n):
line = input(f"请输入第{y + 1}行的所有数据,中间用空格隔开:").split()
temp.append(line)
for y in range(n):
numLine = []
for x in range(n):
numLine.append(int(temp[y][x]))
array.append(numLine)
detCalc(array)
else:
print("输入阶数不符合规范")
continue
def detCalc(det: list):
"""
计算行列式
det: 表示行列式的二维数组
注意:请保证二维数组的形状为正方形
return: 行列式的计算结果
"""
level = len(det) # 获得行列式的阶数
paths = fullPermutation(level)
d = 0 # 最终结果
pathsResults = []
printDet(det)
print("=", end=" ")
for path in paths:
y = 0
num = 1 # 每条路径的结果
if tao(path) % 2 == 0:
print("+(", end="")
else:
print("-(", end="")
for x in range(len(path)):
if x == len(path) - 1:
print(det[y][path[x]], end="")
else:
print(det[y][path[x]], end="x")
num *= det[y][path[x]]
y += 1
if tao(path) % 2 == 0:
d += num
pathsResults.append(num)
else:
d -= num
pathsResults.append(-num)
print(")", end="")
print()
print("=", end=" ")
for r in range(len(pathsResults)):
if r == 0 and pathsResults[r] >= 0:
print(f"{pathsResults[r]}", end=" ")
if pathsResults[r] >= 0:
print(f"+{pathsResults[r]}", end=" ")
else:
print(f"{pathsResults[r]}", end=" ")
print()
print(f"= {d}")
return d
def printDet(arr):
"""
传入二维数组,打印行列式
:param arr:
:return:
"""
maxStrLen = 1
for line in arr:
for x in line:
if len(str(x)) > maxStrLen:
maxStrLen = len(str(x))
for line in arr:
print("|", end=" ")
for x in line:
print(" " * (maxStrLen - len(str(x))) + str(x), end=" ")
print("|")
def tao(arr: list):
"""
传入数组,返回该数组的逆序数
arr: 所有元素都为int类型数据的数组
return: 正整数
"""
n = 0
for i in range(len(arr) - 1):
for j in range(i + 1, len(arr)):
if arr[i] > arr[j]:
n += 1
return n
def fullPermutation(n: int):
"""
输入一个正整数 n,返回 [0, n-1]组成的所有排列情况组成的二维列表
例如:
传入 n = 3
返回 [[0, 1, 2],[0, 2, 1],[1, 0, 2],[1, 2, 0],[2, 0, 1],[2, 1, 0]]
"""
class Joint:
"""节点类,用于构建树结构"""
def __init__(self, data, father):
"""
data: 表示本节点自身存储的数据
child: 表示存储子节点的列表,请自觉保证其类型均为 Joint
father: 表示节点的父节点,请自觉保证其类型同样为 Joint,如果没有父节点请写 None
"""
self.data = data
self.child = []
self.father = father
def show(self):
"""
在终端打印此节点以及它的所有子节点所组成的树结构
return: 无返回值
"""
def printSelf(jnt, deep):
tabStr = "\t" * deep
print(tabStr + str(jnt.data))
deep += 1
if len(jnt.child) != 0:
for jt in jnt.child:
printSelf(jt, deep)
printSelf(self, 1)
def getAllPath(self):
"""
获取此节点对象与所有子节点所组成的树结构 的所有路径 并将所有路径以数组的形式返回
return: 二维数组,该二维数组由很多 “路径数组”组成,路径数组 由该路径上所有节点的 item 属性值组成
"""
array = []
def getPath(jnt: Joint):
"""
传入节点 jnt
返回 祖先节点到当前节点 jnt 所组成的路径数组,路径数组由每个节点的 item 属性值组成
"""
arr = [jnt.data]
def goOut(jt):
"""递归的向父节点走"""
if jt.father.data is not None:
arr.append(jt.father.data)
goOut(jt.father)
else:
return
goOut(jnt)
return arr[::-1]
def goIn(jnt):
"""递归的深入每一个分支"""
for son in jnt.child:
if len(son.child) == 0:
arr = getPath(son)
array.append(arr)
else:
goIn(son)
goIn(self)
return array
root = Joint(None, None)
def add(r, arr):
"""
递归地添加节点,用于构造全排列的树结构
r: 用于表示递归中的当前节点
arr: 表示该节点到祖先节点的路径上已经存在的所有节点的 item 属性
"""
for x in range(n):
if x not in arr:
jSon = Joint(x, r)
r.child.append(jSon)
arrNew = arr + [x]
if len(arrNew) <= n:
add(jSon, arrNew)
add(root, [None])
# 把树的每一个路径都抽取出来
return root.getAllPath()
if __name__ == '__main__':
main()
不足之处
获取每条路径的方式很消耗时间和空间
此算法为一般方法,还可以更新有技巧的方法。