python实现克莱姆法则

拟墨画扇 提交于 2020-03-05 20:25:55


注:本文对numpy对象使用append方法时均使用了深拷贝deepcopy,因为python中对象的赋值是按引用传递的,如果不使用深拷贝在append时会改变原有对象从而覆盖原先的值

首先完成python模拟行列式运算

a11a12a1na21a22a2nan1an2ann=(1)ta1p1a2p2anpn \begin{vmatrix} {a_{11}}&{a_{12}}&{\cdots}&{a_1n}\\ {a_{21}}&{a_{22}}&{\cdots}&{a_2n}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {a_{n1}}&{a_{n2}}&{\cdots}&{a_{nn}}\\ \end{vmatrix}= \sum{(-1)^{t}}{a_{1p_{1}}a_{2p_{2}}}{\cdots}{a_{np_{n}}}

标注:t: p1p2p3pn{p_1}{p_2}{p_3}{\cdots}{p_n} 的逆序数

公式分析

p1p2p3pn{p_1}{p_2}{p_3}{\cdots}{p_n}从下标来看可以看作n个数的全排列,而t则是后面下标序列的逆序数,所以我们可以将他分成三个模块来实现,首先是求的我们所需的全排列序列,然后对该序列求逆序数,最后累加求和

模块分析与实现

环境

Anaconda 3 + Python 3.6.5 + Jupyter

模块导入

import numpy as np
import copy

全排列

  • 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。

    公式:全排列数f(n)=n!(定义0!=1)

在这里插入图片描述

(深度优先搜索)采用递归的思路:先确定一个节点,然后后面的节点进行交换,在确定下一个节点,一层层递归,递归回溯时应将顺序交换回来,以便向其他子节点方向递归

# 全排列
def permutations(n):
    arr = [x for x in range(n)]
    result =[]
    dfs(arr,0,len(arr),result)
    return result

def dfs(arr, begin, end,result):
    if begin == end:
        result.append(copy.deepcopy(arr))
        pass
    else:
        for i in range(begin,end):
            arr[i],arr[begin] = arr[begin],arr[i]
            dfs(arr,begin+1,end,result)
            arr[i],arr[begin] = arr[begin],arr[i]
            pass
        pass

逆序数

  • 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数
# 逆序数
def inverse(arr):
    count = 0
    for i in range(len(arr)):
        for j in range(i):
            if arr[j] > arr[i]:
                count+=1
                pass
            pass
        pass    
    return count

方阵计算

  • 根据公式进行累加计算
def det(matrix):
    if matrix.shape[0]!=matrix.shape[1]:
        print('此矩阵不是方阵!')
        return
    permutation = permutations(matrix.shape[0])
    permutation = np.array(permutation)
    result = 0
    for i in range(len(permutation)):
        a = 1
        for j in range(matrix.shape[0]):
            a*=matrix[j][permutation[i][j]]
        result += pow(-1,inverse(permutation[i]))*a
    return result

克莱姆法则 Cramer’s rule

对以下方程组:
{a11x1+a12x2++a1nxn=b1a21x1+a22x2++a2nxn=b2an1x1+an2x2++annxn=bn \begin{cases} a_{11}x_1 + a_{12}x_2 + {\cdots} + a_{1n}x_n = b_1\\ a_{21}x_1 + a_{22}x_2 + {\cdots} + a_{2n}x_n = b_2\\ {\cdots}{\cdots}\\ a_{n1}x_1 + a_{n2}x_2 + {\cdots} + a_{nn}x_n = b_n\\ \end{cases}
如果线性方程组的系数行列式D不为零,即:
D=a11a1nan1ann0 D=\begin{vmatrix} {a_{11}}&{\cdots}&{a_{1n}}\\ {\vdots}&{\ddots}&{\vdots}\\ {a_{n1}}&{\cdots}&{a_{nn}}\\ \end{vmatrix} {\neq} 0
那么方程组有唯一解:
x1=D1D,x2=D2D,,xn=DnD x_1 = \frac{D_1}{D} , x_2 = \frac{D_2}{D} , {\cdots} , x_n = \frac{D_n}{D}
其中 DjD_j(j=1,2,\cdots,n) 是把行列式D中第j列元素用方程组右端的常数项代替后所得到的n阶行列式,即:
Dj=a11a1j1b1a1nan1anj1b1ann D_j=\begin{vmatrix} {a_{11}}&{\cdots}&a_{1,j-1}&b_1&{\cdots}&a_{1n}\\ {\vdots}&&{\vdots}&{\vdots}&&{\vdots}\\ {a_{n1}}&{\cdots}&a_{n,j-1}&b_1&{\cdots}&a_{nn}\\ \end{vmatrix}

对此我们做出如下约定:

  1. d为系数矩阵作为函数的第一个参数
  2. b为方程组右端常数项组成的列向量作为函数的第二个参数
  3. d_i为公式中的 DjD_j(j=1,2,\cdots,n) 组成的三维矩阵
  4. 返回由结果 xix_i(j=i,2,\cdots,n) 组成的向量

实现代码:

def cramer(d,b):
    if d.shape[0]!=d.shape[1]:
        print('此矩阵不是方阵!')
        return
    if det(d) == 0:
        print('系数方阵为0')
        return
    d_i = []
    for i in range(b.shape[0]):
        d_i.append(copy.deepcopy(d))
        d_i[i][:,i] = b
        pass
    x = []
    for i in range(b.shape[0]):
        x.append(det(d_i[i]) / det(d))
    print(x)

测试结果:

测试以下方程组:
{2x+y+z=15x+2y+z=16x+y+2z=17 \begin{cases} 2x + y + z = 15\\ x + 2y + z =16\\ x + y + 2z =17\\ \end{cases}
即:
[211151211611217] \left[ \begin{array}{ccc|c} 2&1&1&15\\ 1&2&1&16\\ 1&1&2&17 \end{array} \right]
索引函数的输入为:
d=211121112                            b=151617 d=\begin{vmatrix} 2&1&1\\ 1&2&1\\ 1&1&2 \end{vmatrix} \;\;\;\;\;\;\;\;\;\;\;\;\;\; b = \begin{vmatrix} 15\\16\\17 \end{vmatrix}
预测结果:
x=345 x = \begin{vmatrix} 3&4&5 \end{vmatrix}
运行结果:

d = np.array([[2,1,1],[1,2,1],[1,1,2]])
b = np.array([15,16,17])
cramer(d,b)
[3.0, 4.0, 5.0]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!