这是一道小学数学的问题。
使用简单的二元一次方程组可以很容易的解出答案:
假设有x只鸡,y只兔子;
x + y = n; 1式
2x+4y = m; 2式
根据等式的基本原则:等式两边同乘以某一个非0的常数,等式仍然成立;
并且对于两个等式,左边的值分别相加,右边的值分别相加,等式仍然成立;
2式 - 2*1式 ==》 2y = m-2n;
==》 可以推导出 y = (m-2n)/2;
带回到1式 ==》 x=(4n-m)/2;
从而可以用以下的Python代码来实现:
def getChickRabbitNumber(n,m):
"""
n=14,m=32 ==> x,y 12,2
n=10,m=16 =x,y=> x,y No solution
"""
if n <= 0 or m <= 0 or m%2==1 or 4*n < m or m < 2*n: # 判决输入是否合法
return None,None
numberOfChick = (4*n-m)/2
numberOfRabbit = (m-2*n)/2
return numberOfChick,numberOfRabbit
这是一个显而易见的一元二次方程的解法。
但是由于问题本身的特殊性,每次我们需要对于一个实际的问题进行求解的过程。而这个n元一次方程组在求解时最容易想到的就是使用高斯消元的方法,当然,我们可以使用线性代数求解方程组的一整套方法论来解决这个问题。
在计算机求解的过程中实际上也可以使用线性代数中的概念来得到更广泛意义上的方法。
那么,上面这个解二元一次方程组的过程
我们看下面更一般解法:
def getChickRabbitNumber2(n,m):
"""
a+b = n
2a+4b = m
get a,b
n=14,m=32 ==> 12,2
n=10,m=16 ==> No solution
"""
# 一般解法:注意这里的一般是指不需要针对特殊问题采用特殊的函数方式,只需将这里的numpy数组替换即可推广
A = np.array([[1,1], #方程组 第一个方程的系数矩阵
[2,4]]) #方程组 第二个方程的系数矩阵
B = np.array([n, #方程组 第一个方程右边的值
m]) #方程组 第二个方程右边的值
x1,x2 = solve(A,B) # 求解这个方程组
if x1 < 0 or x2 < 0 or (int(x1) != x1) or (int(x2) != x2): # 判断解是否有意义
return None,None
return x1,x2
这是使用Python中的numpy矩阵和内置的矩阵求解的方法来处理。
方程的解可以是唯一的,可以是不存在的,也可以无穷多个(无穷多还有可能有不同的等级,因为无穷大也是有大小的。对,你没看错,无穷大有多种等级的!)
我们这里的问题有其特殊性,方程的个数是2个,未知数的个数也是2个;
如果我们让方程的个数为m个,未知数的个数为n个;则m,n之间的关系有:
m < n, m = n, 和 m > n三种情况:
通常来说
当 m=n 时,方程会有唯一解;
当 m<n 时,方程可能有无穷多解;
但是,当有一个方程出现一侧为0,另一侧不为0时,则方程组出现矛盾,此时无解;
当 m>n时,方程的解个数是不确定的,各种可能性都有可能;
这m个方程,有可能有些可以通过其他方程组合能得到的,也就是说,它表达的真实信息可能在其他方程中已有所表达了,这样并不是一个有效的信息,此时m的真实值应该减少(以后我们会具体分析),但减少到什么程度就得看情况了;
其实, 我们从几何直观上理解问题时,问题会显得更加的简单清晰。
下面我们通过三个线性方程例子的求解过程及几何直观表现来展示一下解的多样性:
当有唯一解的时候(事实上,这是一个苛刻的条件等式)
举个例子:
平面直角坐标系中有一个抛物线y = ax^2+bx+c,它过三个已知点(-3,20),(1,0),(2,10), 求这个抛物线方程。
根据三点条件,我们得到下面的三个方程式:
### 线性方程组
9a - 3b + c = 20
a + b + c = 0
4a + 2b + c = 10
可以很容易的使用高斯消元得到这组方程组的解(我们这里先忽略解的过程,使用Python来做解方程的过程),我们可以解出它是唯一的: a=3 b=1 c=-4
# Python的求解代码如下:
## 先确定常数项
A1 = [[9,-3,1],
[1,1,1],
[4,2,1]]
A = np.array(A1)
# 确定等式右边的值
B = np.array([20,
0,
10])
# 计算方程组的解
c = solve(A,B)
print(c) # [ 3. 1. -4.]
于是,抛物线方程为y = 3*x^2 + x -4
我们可以把其抛物线图像画出:
其实唯一解在这里也是表达了三维空间中得一个点,我们知道,上面的三个方程其实每一个都表达了一个平面;
那么当这个三个方程都满足时,其实质上就是求空间中的这三个平面相交的部分,这里实际上是一个点(3,1,-4)!
让我们来看看下面这张图,这是用Python的matplotlib做出的三个平面的相交部分图示:
当 m < n时,方程则可能有无穷多解。
这时,表达的可能就不是一个点,而是一条直线,或者一个平面,一个空间的概念。
比如我们再来看一个例子:
如解下面的线性方程组:
9a - 3b + c = 20
a + b + c = 0
-a + 2b + c = -5
==》 数学的方法可以推导出如下解的关系
# x = t
# y = -5 + 2t
# z = 5 - 3t
# 其中t是任意值,这里可以简单认为是实数;
下面是Python解该方程组的代码:
## 先确定常数项
A1 = [[9,-3,1],
[1,1,1],
[-1,2,1]]
A = np.array(A1)
## 确定等式右边的值
B = np.array([20,
0,
-5])
# 计算方程组的解
try:
C = solve(A,B)
print(C) # 其中一组解 [ -6.5 -18. 24.5],(思考:为什么这里Python给出了这组解,有没有方法得到其他解???)
except:
print("Matrix is singular.")
这里的t是可以自由变换的量,当t的值确定下来时,则y,z的值可以确定下来。
事实上,如果用空间坐标来刻画此时的图像,得到则是一个在x轴方向上可以无限延展的平面(x的值可以发生变换,y,z的值随着x的值发生变化),如下图:
确实,我们发现线性代数的问题如果用这样的几何的方法来理解,
用矩阵的代数方法来计算并没有想象的那么难。但事实上,用几何方法在未知数和方程超过三个的时候似乎也不好理解了,
因为涉及到超平面了。但是,至少对于我们尝试去理解其直观本质还是有一定帮助。
最后,我们再来看看无解的情况:
请看下面的式子:
### 线性方程组
x + y = 2 1式
x - y = 0 2式
x + 3y = 5 3式
==》 由1式,2式可以推出 x=1,y=1,但是带入3式矛盾;
下面是Python解该方程组的代码
## 先确定常数项
A1 = [[1, 1, 0],
[1,-1, 0],
[1, 3, 0]]
A = np.array(A1)
# 确定等式右边的值
B = np.array([2,
0,
5])
# 计算方程组的解
try:
c = solve(A,B)
print(c) # [ 3. 1. -4.]
except:
print("方程组无解.")
通过几何意义,我们可以很容易看出来,
这个方程组背后的含义是:
欧几里得平面中的三条直线不相交,所以没有公共点(这点并不奇怪!)
通过上面几个方程组的解及几何含义,我们直观了解了下方程组解的意义及在Python中解方程组的方法。
总结:
事实上未知数是一种不确定,方程是对未知数的限制,所以一般来说方程越多,空间中的点越少,因为限制越多。
关于线性代数更多直观性的解释及代码展示的细节,我们将会再之后的一系列专题文章中继续讨论。
参考:《线性代数》李尚志编著
《算法竞赛入门》 刘汝佳编著