3 使用 SymPy 做數(shù)學(xué)研究基礎(chǔ)

本章導(dǎo)航:

  1. 介紹 SymPy 的基礎(chǔ)語法窗市。
  2. 舉了兩個(gè)實(shí)例介紹如何使用 SymPy 做數(shù)學(xué)研究先慷。

9.1 Python 中的數(shù)學(xué):符號(hào)計(jì)算

Python 有許多優(yōu)秀的處理數(shù)學(xué)的工具,本章僅僅介紹符號(hào)計(jì)算庫:SymPy咨察。

9.1.1 使用 SymPy 表示數(shù)

Sympy 庫完全由 Python 寫成论熙,支持符號(hào)計(jì)算、高精度計(jì)算摄狱、模式匹配脓诡、繪圖、解方程媒役、微積分祝谚、組合數(shù)學(xué)、離散 數(shù)學(xué)酣衷、幾何學(xué)交惯、概率與統(tǒng)計(jì)、物理學(xué)等方面的功能穿仪。

復(fù)數(shù)一般使用 a + bi 的形式進(jìn)行表示席爽。其中 a, b \in \mathbb{R},而 i 是虛數(shù)的單位啊片。在 SymPy 中針對這些特定的“數(shù)”拳昌,有專門的符號(hào)表示。

使用 I 表示 i钠龙,使用 Rational 表示分?jǐn)?shù)。

from sympy import I, Rational, sqrt, pi, E, oo
3 + 4I, Rational(1, 3), sqrt(8), sqrt(-1), pi, E**2, oo

輸出的結(jié)果是:

3+4i, \frac{1}{3}, 2\sqrt{2}, i, \pi, e^2, \infty

可以看出,SymPy 可以完美的表示我們熟悉的各種數(shù)學(xué)中定義的“數(shù)”碴里。

9.1.2 使用 SymPy 提升您的數(shù)學(xué)計(jì)算能力

為什么要使用 SymPy 呢沈矿?直接使用 Python 的 math 庫不就可以了嗎?您也許會(huì)有諸如此類的疑問咬腋。在解釋原因之前羹膳,我們來看一個(gè)例子:

import math
math.sqrt(8)

輸出的結(jié)果是:

2.8284271247461903

2.8284271247461903?您沒有看出根竿,由于計(jì)算機(jī)是以二進(jìn)制方式進(jìn)行數(shù)學(xué)運(yùn)算的陵像,故而計(jì)算虛數(shù)的值總是不精確的,而 SymPy 的計(jì)算結(jié)果便是精確的寇壳。

import math, sympy
math.sqrt(8) ** 2, sympy.sqrt(8) ** 2

輸出結(jié)果是:

(8.000000000000002, 8)

9.1.2.1 對數(shù)學(xué)表達(dá)式進(jìn)行簡化與展開

在 SymPy 中使用 symbols 定義數(shù)學(xué)中一個(gè)名詞:變量醒颖。使用 symbols 創(chuàng)建多個(gè)變量名時(shí),請使用空格或者逗號(hào)進(jìn)行隔開壳炎,即 symbols('x y') 或者 symbols('x,y') 的形式泞歉。

from sympy import symbols
x, y = symbols('x y')
expr = x + 2*y
expr

輸出結(jié)果:

x + 2y

這樣,我們便可以將 expr 當(dāng)作數(shù)學(xué)中的表達(dá)式了:

expr + 1, expr - x

輸出結(jié)果:

x+2y+1, 2y

既然談到數(shù)學(xué)表達(dá)式匿辩,總會(huì)涉及的其的展開與化簡腰耙。比如 x(x+2y) = x^2 +2xy,可以這樣:

from sympy import expand, factor
expanded_expr = expand(x*expr)
expanded_expr

輸出結(jié)果是:

x^2 + 2xy

我們還可以將其展開式轉(zhuǎn)換為因子的乘積的形式:

factor(expanded_expr)

輸出的結(jié)果是:

x(x + 2y)

SymPy 不僅僅如此铲球,它還可以支持 LATEX 公式:

alpha, beta, nu = symbols('alpha beta nu')
alpha, beta, nu

輸出結(jié)果是:

\alpha, \beta, \nu

需要注意的是 symbols 定義的變量 x, y 等不再是字符串挺庞,您將其看作是數(shù)學(xué)中的變量即可。

9.1.2.2 求導(dǎo)稼病,微分选侨,定積分,不定積分溯饵,極限

先載入一些會(huì)用到的函數(shù)侵俗,方法,符號(hào):

from sympy import symbols, sin, cos, exp, oo
from sympy import expand, factor, diff, integrate, limit
x, y, t = symbols('x y t')

在 SymPy 中使用 diff 對數(shù)學(xué)表達(dá)式求導(dǎo)丰刊,比如:(\sin(x)e^x)' = e^x \sin(x) + e^x \cos(x)隘谣,使用 SymPy,則有:

diff(sin(x)*exp(x), x)

輸出結(jié)果是:

e^x \sin(x) + e^x \cos(x)

在 SymPy 中使用 integrate 求解不定積分與定積分啄巧。比如寻歧,\int e^x\sin(x) + e^x\cos(x) \textlnkkcjh x = \sin(x)e^x,即:

integrate(exp(x)*sin(x) + exp(x)*cos(x), x)

輸出結(jié)果:

\sin(x)e^x

定積分 \int_{-\infty}^{\infty} \sin(x^2)\textccz2zux x = \frac{\sqrt{2 \pi}}{2}

integrate(sin(x**2), (x, -oo, oo))

輸出結(jié)果:

\frac{\sqrt{2} \sqrt{\pi}}{2}

在 SymPy 中使用 limit 求極限秩仆,比如 \lim_{x \to 0} \frac{\sin(x)}{x} = 1码泛,即:

limit(sin(x)/x, x, 0)

輸出結(jié)果:

1

9.1.2.3 解方程

from sympy import solve, dsolve, Eq, Function

可以使用 SymPy 的 solve 求解 f(x) = 0 這種類型的方程。比如澄耍,求解 x^2 - 2 = 0

solve(x**2 - 2, x)

輸出結(jié)果是:

[-\sqrt{2}, \sqrt{2}]

您還可以使用 SymPy 的 dsolve 函數(shù)求解微分方程:

y = Function('y')
dsolve(Eq(y(t).diff(t, t) - y(t), exp(t)), y(t))

輸出 y'' - y = e^t 的結(jié)果:

C_2 e^{-t} +(C_1 + \frac{t}{2})e^t

小貼士:您可以使用 sympy.latex 將您的運(yùn)算結(jié)果使用 LATEX 的形式打印出來:

from sympy import latex
latex(Integral(cos(x)**2, (x, 0, pi)))

輸出結(jié)果:

\int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}\, dx

9.1.3 使用 SymPy 求值

在 9.4.2 中我們已經(jīng)介紹了 SymPy 的大多數(shù)符號(hào)運(yùn)算機(jī)制噪珊,接下來將討論如何通過“賦值”(數(shù)學(xué)中的變量賦值)來獲取表達(dá)式的值晌缘。如果您想要為表達(dá)式求值,可以使用 subs 函數(shù)(Substitution痢站,即置換):

x = symbols('x')
expr = x + 1
expr.subs(x, 2)

輸出的結(jié)果為:

3

正是我們想要得到的預(yù)期結(jié)果磷箕。

也可以使用 subs 函數(shù)作為參數(shù)置換,比如:

在 9.1.2.3 中出現(xiàn)的符號(hào) Eq 是用來表示兩個(gè)數(shù)學(xué)表達(dá)式的相等關(guān)系阵难。比如岳枷, x + 1 = 4,可以這樣:

Eq(x+1, 4)

輸出結(jié)果:

x+1=4

又比如呜叫,(x+1)^2 = x^2 + 2x + 1空繁,可以這樣:

Eq((x + 1)**2, x**2 + 2*x + 1)

輸出結(jié)果:

(x+1)^2 = x^2 + 2x + 1

如果您想要判斷兩個(gè)數(shù)學(xué)表達(dá)式是否相等,您有兩種方式朱庆。下面我們來判斷 \cos^2(x) - \sin^2(x) = \cos(2x)

方式一:使用 equals盛泡。

a = cos(x)**2 - sin(x)**2
b = cos(2*x)

a.equals(b)

輸出結(jié)果為 True 符合預(yù)期。

方式二:使用 simplify 化簡等式:

simplify(a - b)

輸出結(jié)果為 0 也符合預(yù)期椎工。

當(dāng)然饭于,也可以作圖:

9.1.4 使用 SymPy 做向量運(yùn)算

在 SymPy 中分別使用 PointSegment 來表示數(shù)學(xué)中的點(diǎn)線段實(shí)體(entity)维蒙。

from sympy import Segment, Point
from sympy.abc import x
a = Point(1, 2, 3)
b = Point([2, 3])
c = Point(0, x)
a, b, c

輸出結(jié)果是:

(Point3D(1, 2, 3), Point2D(2, 3), Point2D(0, x))

Point 用來表示數(shù)學(xué)中的點(diǎn)(向量):x=(x_1,\cdots,x_n) \in \mathbb{R}^{n}掰吕,其中 x_j \in \mathbb{R},\, 1\leq j \leq n。對于 Segment(x1,x2)中的參數(shù) x1颅痊,x2 可以是 Point 實(shí)例殖熟,也可以是元組或者列表,array 等斑响。但是 x1x2 代表的點(diǎn)的維度必須一樣菱属。Segment(x1,x2) 表示一個(gè)有向的線段,即矩形的對角線方向?yàn)?x_2 - x_1舰罚。比如纽门,定義下圖9.4.1的實(shí)體:

x1 = Point(1,1)
x2 = Point(2,2)
s = Segment(x1,x2)
圖9.1.1 Segment 示意圖

圖中的有向?qū)蔷€(向量)可以通過 s.direction 進(jìn)行計(jì)算:

s.direction

輸出結(jié)果為:

??????????2??(1,1)

即向量 x_2 - x_1。在許多領(lǐng)域中营罢,一般將水平向右作為 X 軸赏陵,水平向下作為 Y 軸。左上角作為原點(diǎn) (0,0)饲漾。

有了對角線的方向蝙搔,便可以計(jì)算矩形框的寬與高:

w, h = s.direction

由于 Point 指代數(shù)學(xué)中的向量,故而其存在加法考传、減法吃型、數(shù)乘以及內(nèi)積運(yùn)算,它們均被重載為 Python 的運(yùn)算符僚楞。比如勤晚,我們定義向量 a,b,c枉层,且 a+b=c,可有:

a = Point(0,1)
b = Point(1,0)
c = Point(1,1)

下面我們對它們進(jìn)行加法運(yùn)算:

a + b == c

輸出結(jié)果為 True 符合預(yù)期赐写。同樣有:

a - b, c * 2, a.dot(c)

輸出結(jié)果為:

(Point2D(-1, 1), Point2D(2, 2), 1)

所有結(jié)果均符合預(yù)期返干。下面我們再來看看 Segment

ab = Segment(a, b)
ac = Segment(a, c)
ab.direction, ac.direction

輸出的結(jié)果是:

(Point2D(1, -1), Point2D(1, 0))

我們可以計(jì)算 abac 的方向向量的夾角余弦:

ab.direction.dot(ac.direction) / (ab.length * ac.length)

輸出結(jié)果為 \frac{\sqrt{2}}{2},即 \cos(\theta) = \frac{a \cdot b}{|a||b|}血淌。很容易求得 \theta = \frac{\pi}{4}。更方便的是财剖,Segment 提供了函數(shù)直接計(jì)算夾角:

ab.angle_between(ac)

輸出結(jié)果也是 \frac{\pi}{4}悠夯。是不是很方便?如果您想要獲取 Segment 實(shí)例的全部點(diǎn)可以調(diào)用 ab.points躺坟,分別調(diào)取各點(diǎn)則可以使用 ab.p1ab.p2沦补。

9.2 一個(gè)案例:實(shí)現(xiàn)圖像局部 resize 保持高寬比不變

下面我們利用 9.4 節(jié)學(xué)習(xí)的東西構(gòu)造一個(gè)實(shí)現(xiàn)圖像局部 resize 保持高寬比不變的模型。

  • 題設(shè):假設(shè)有一張圖片 I 的尺寸為 (W_I, H_I)咪橙,I 存在一個(gè)目標(biāo)物 O夕膀,O 的尺寸為 (W, H)
  • 目標(biāo):將 O resize 為尺寸是 (w,h)美侦,而約束條件是保證 resize 之后的邊界為 w_m,h_m(注意:這里的 h_m 表示下邊界長度)产舞。需要盡可能保持 resize 之后的圖片塊寬高比不變,即滿足:

\frac{W}{H} = \frac{w - 2w_m}{h - h_m - h_p} \;\; (\text{公式 9.5.1})

其中 h_p 指的是上邊界的長度菠剩。這里只有 h_p 的未知量易猫,我們可以直接求出:

h_p = h - h_m - \frac{W - 2w_m}{W} H \;\; (\text{公式 9.5.2})

為了保證 resize 的一致性,需要在原圖補(bǔ)邊 H_m, W_m, H_p 對應(yīng)于 h_m, w_m, h_p具壮。下面給出它們的計(jì)算公式:

\frac{w}{h} = \frac{W + 2W_m}{H + H_m + H_p} \;\; (\text{公式 9.5.3})

同時(shí)還需要保證 resize 前后的寬高與邊界的比例不變:

\frac{W}{W_m} = \frac{w}{w_m} \;\; (\text{公式 9.5.4})\\ \frac{H}{H_m} = \frac{h}{h_m} \;\; (\text{公式 9.5.5})

聯(lián)合上述公式准颓,便可以求出所有未知量。至此棺妓,模型建立完畢攘已。下面考慮使用 Python 實(shí)現(xiàn)。

from sympy import Point, Segment, symbols, Eq, solve
from dataclasses import dataclass

@dataclass
class Resize:
    W: int
    H: int
    w: int
    h: int
    w_m: int
    h_m: int

    def model(self):
        H_m, W_m, H_p, h_p = symbols("H_m, W_m, H_p, h_p")
        eq1 = Eq(self.W/self.H, (self.w - 2 * self.w_m)/(self.h - self.h_m - h_p))
        eq2 = Eq(self.w/self.h, (self.W + 2 * W_m)/(self.H + H_m + H_p))
        eq3 = Eq(self.W/W_m, self.w/self.w_m)
        eq4 = Eq(self.H/H_m, self.h/self.h_m)
        R =  solve([eq1, eq2, eq3, eq4], [H_m, W_m, H_p, h_p])
        return R

# 傳入已知量
W, H = 100, 100
w, h = 32, 32
w_m, h_m = 5, 5
# 模型建立
r = Resize(W, H, w, h, w_m, h_m)
# 模型求解
R = r.model()
# 查看求解結(jié)果
R

輸出結(jié)果為:

{H_m: 15.6250000000000,
 W_m: 15.6250000000000,
 H_p: 15.6250000000000,
 h_p: 5.00000000000000}

這樣我們完成了設(shè)定的目標(biāo)怜跑。為了更直觀样勃,從網(wǎng)上找一張貓的圖片作為例子。為了可視化的方便妆艘,我們需要定義一個(gè)畫邊界框的函數(shù):

def bbox_to_rect(bbox, color):
    # 將邊界框(左上x, 左上y, 右下x, 右下y)格式轉(zhuǎn)換成matplotlib格式:
    # ((左上x, 左上y), 寬, 高)
    return plt.Rectangle(
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=1)

該函數(shù)的參數(shù) bbox 取值為 (x_{\text{左上}}, y_{\text{左上}}, x_{\text{右下}}, y_{\text{右下}})彤灶,color 指定框的顏色。

from matplotlib import pyplot as plt
%matplotlib inline
# 讀取圖片
img = plt.imread("cat.jpg")
bbox = [65, 25, 235, 175] # 框的坐標(biāo)
fig = plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(bbox, 'blue'))
plt.show()

輸出結(jié)果為:


圖9.2.1 一張貓的圖片

數(shù)組 img 指代 I批旺,而圖中的“貓”(即 O)我們可以使用切片的方式獲取幌陕,即 img[bbox[1]:bbox[3]+1,bbox[0]:bbox[2]+1](這里需要注意,切片的第一維度為高)汽煮,即:

patch = img[bbox[1]:bbox[3]+1,bbox[0]:bbox[2]+1]
plt.imshow(patch)
plt.show()

輸出結(jié)果為:

圖9.2.2 貓的切片搏熄,簡稱**貓片**棚唆,即圖像塊

我們可以直接求得貓片的寬和高:h, w = patch.shape[:2],但是心例,patch 是由 bbox 獲取的宵凌,故而,我們也可以直接求得:

def get_aspect(bbox):
    '''
    計(jì)算 bbox 的寬和高
    '''
    # 加 1 是因?yàn)橄袼攸c(diǎn)是的尺寸為 1x1
    w = bbox[2] - bbox[0] + 1
    h = bbox[3] - bbox[1] + 1
    return w, h
# 獲取寬高
W, H = get_aspect(bbox)

我們想要將貓片 resize 為 (480, 480)止后,則可以設(shè)定:

w, h = 480, 480
w_m, h_m = 5, 5

接著瞎惫,計(jì)算邊界:

r = Resize(W, H, w, h, w_m, h_m)
R = r.model()
H_m, W_m, H_p, h_p = R.values()

由于這里獲取的值是浮點(diǎn)數(shù),我們需要將其轉(zhuǎn)換為整數(shù):

def float2int(*args):
    return [int(arg)+1 for arg in args]
# 獲取整數(shù)型數(shù)據(jù)
H_m, W_m, H_p, h_p = float2int(*R.values())

最后译株,我們原圖與 resize 之后的圖片展示如下:

import cv2
patch = img[bbox[1]-H_p:bbox[3]+1+H_m,bbox[0]-W_m:bbox[2]+1+W_m]
resize = cv2.resize(patch, (w, h))
plt.imshow(patch)
plt.title("origin")
plt.show()
plt.imshow(resize)
plt.title("resize")
plt.show()

輸出結(jié)果為:


圖9.2.3 原圖
圖9.2.4 resize 之后的圖片

本章的代碼邏輯并不是很嚴(yán)謹(jǐn)瓜喇,主要是提供如何一個(gè)創(chuàng)建計(jì)算機(jī)視覺軟件的思路,您仔細(xì)研究本章的軟件設(shè)計(jì)思路將會(huì)收獲很多的歉糜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載乘寒,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末匪补,一起剝皮案震驚了整個(gè)濱河市伞辛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夯缺,老刑警劉巖蚤氏,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異喳逛,居然都是意外死亡瞧捌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門润文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姐呐,“玉大人,你說我怎么就攤上這事典蝌∈锷埃” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵骏掀,是天一觀的道長鸠澈。 經(jīng)常有香客問我,道長截驮,這世上最難降的妖魔是什么笑陈? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮葵袭,結(jié)果婚禮上涵妥,老公的妹妹穿的比我還像新娘。我一直安慰自己坡锡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鲸拥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吵取。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天锯厢,我揣著相機(jī)與錄音皮官,去河邊找鬼。 笑死实辑,一個(gè)胖子當(dāng)著我的面吹牛臣疑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徙菠,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼郁岩!你這毒婦竟也來了婿奔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤问慎,失蹤者是張志新(化名)和其女友劉穎萍摊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體如叼,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冰木,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笼恰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踊沸。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖社证,靈堂內(nèi)的尸體忽然破棺而出逼龟,到底是詐尸還是另有隱情,我是刑警寧澤追葡,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布腺律,位于F島的核電站,受9級特大地震影響宜肉,放射性物質(zhì)發(fā)生泄漏匀钧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一谬返、第九天 我趴在偏房一處隱蔽的房頂上張望之斯。 院中可真熱鬧,春花似錦朱浴、人聲如沸吊圾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽项乒。三九已至啰劲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間檀何,已是汗流浹背蝇裤。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留频鉴,地道東北人栓辜。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像垛孔,于是被迫代替她去往敵國和親藕甩。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容