想象一下屁使,如果你手里有一塊形狀不規(guī)則的土地(實際上我沒有,窮...)荚藻,要測量它的面積屋灌,怎么辦呢?
拿尺子量应狱,不知如何下手共郭,突然感覺高中幾何解決不了,得祭出本科的高等數(shù)學(xué)才行疾呻。所以除嘹,慣例我們應(yīng)該發(fā)揚拿來主義,比如 “國際上岸蜗,如何如何...”:
一個叫黎曼的德國數(shù)學(xué)家(Bernhard Riemann, 1826-1866)尉咕,他想了個辦法:將這不規(guī)則圖形切成一條條的小長條兒,然后將這個長條近似的看成一個矩形璃岳,再分別測量出這些小矩形的長度年缎,再計算出它們的面積,把所有矩型面積加起來就是這塊不規(guī)則地的面積铃慷。這就是著名的“黎曼和”单芜。小長條寬度趨于0時,即為面積微分犁柜,各個面積求和取極限即為定積分洲鸠。雖然牛頓時代就給出了定積分的定義,但是定積分的現(xiàn)代數(shù)學(xué)定義卻是用黎曼和的極限給出。
好吧扒腕,大致意思是理解了绢淀,光說不行,得練習(xí)瘾腰。于是我先造出來一塊地來當(dāng)?shù)刂鹘缘模彤?dāng)下圖綠色部分是我的地吧
誰家能把土地劃成這樣啊,好吧居灯,承認(rèn)是為了一會切割小矩形的時候祭务,方便計算高而特意用了正弦曲線,否則要是一點曲線規(guī)律都沒有怪嫌,那真的要用尺子去一個個量小矩形的高了,累死柳沙。
計算正弦曲線封閉區(qū)間和y軸相封存的面積
言歸正傳岩灭,說一下應(yīng)用題的要求吧。
如上圖的曲線是一個sin正弦函數(shù)赂鲤,公式 y=sin(x)噪径,我們要求的是這個函數(shù)與x軸閉區(qū)間[-0.5,1.0]所夾的部分面積,也就是綠色部分数初。
按照定積分的分割方法,我們把這片面積分割成n個小矩形,挨個計算面積匕荸,累加求和就是大約綠色部分的總面積了粟誓。分割的n越多,越接近于真實面積仑鸥,但是計算量也會增大吮播;反之分割的越少越省事,但是精度就下降了眼俊。大致如下圖這般:
用python畫了個示意圖意狠,以表明積分的原理
#include <stdio.h>
#include <math.h>
// 求曲線面積(定積分)。參數(shù)為疮胖,函數(shù)f(x)环戈,x軸上區(qū)間[begin,end]兩個點的值
// @param begin x軸區(qū)間開始位置
// @param end x軸區(qū)間結(jié)束位置
float integration(float begin, float end)
{
float sum = 0; //所有矩形的面積累加總和
float n = 1000; //將函數(shù)曲線下方劃為n個矩形,n值越大澎灸,精確值越高
float width = (end - begin) / n; //單個矩形寬度院塞,函數(shù)總長度除以矩形數(shù)量得到
for(float i=1 ; i <= n ; i++) //計算每個矩形的面積
{
float x = begin + width * i; //通過i的遞增,得出每個矩形底部x點的值
float y = fabs(sin(x));
float area = y * width; //求x點對應(yīng)的y值击孩,取絕對值后迫悠,得到高度;再用底寬度乘高得出單個矩形面積
sum += area; //累加矩形面積
}
return sum;
}
int main(){
printf("sin(x), x range is: [-0.5 , 1.0], area is: %f\n", integration(-0.5 , 1));
return 0;
}
編譯執(zhí)行
$ g++ test.cpp
$ ./a.out
sin(x), x range is: [-0.5 , 1.0], area is: 0.582387
得出面積大概是0.582387巩梢。
到這里又開始有疑問了创泄,如何驗證正確率艺玲?
那么,我該祭出不定積分公式了鞠抑,面積
則對公式進行推導(dǎo)出其原函數(shù)饭聚,然后限定[a,b]的區(qū)間,就是定積分了搁拙,求面積秒梳。
按上圖,積分函數(shù)f(x)=sin(x)箕速,公式為酪碘。
把原函數(shù)找出來
上限b減去下限a兴垦,展開后常數(shù)C抵消了,最終成了
設(shè)置定積分下限a=-0.5字柠,上線b=1.0探越。但是結(jié)果卻錯了,算下來0.3多窑业,和我們上面的0.5多相差太大钦幔。原來看圖,就明白了常柄,圖是兩塊啊鲤氢,分布在y軸兩側(cè),不能直接這么算拐纱,應(yīng)該拆分一下铜异,變成[-0.5,0]和[0,1]兩個區(qū)間,分別運用上面的公式計算秸架,并取絕對值
怎么樣揍庄,我們用代碼堆疊小方塊算出來的0.582387,是不是很接近用公式計算出來的結(jié)果0.582114东抹?只相差了0.000273蚂子。
另外一個驗證的方式,用高大上的在線定積分計算器 https://zh.numberempire.com/definiteintegralcalculator.php 核對下缭黔,輸入函數(shù)是sin(x)食茎,自變量x從-0.5到0,結(jié)果是 ?0.122417馏谨,取絕對值后是 0.122417别渔;然后自變量x再輸入0到1,結(jié)果是 0.459697,兩者相加得到 0.582114哎媚。
計算圓的面積
為了更好直觀的說明原理喇伯,我又換了一個容易計算的圖,如下
這次拨与,圓的函數(shù)方程式為 可以用這個來計算x和y的關(guān)系稻据,a、b是圓心坐標(biāo)买喧。
對于面積捻悯,我們有公式 就可以推算出,再除以2就是半圓的面積淤毛。計算下來今缚,大概是6.2831852。
接下來低淡,我們繼續(xù)用上文的求定積分的方式荚斯,結(jié)合圓的函數(shù)方程式,計算出半圓面積查牌。
#include <stdio.h>
#include <math.h>
// 求曲線面積(定積分)。參數(shù)為滥壕,函數(shù)f(x)纸颜,x軸上區(qū)間[begin,end]兩個點的值
// @param begin x軸區(qū)間開始位置
// @param end x軸區(qū)間結(jié)束位置
float integration(float begin, float end)
{
float r = 2;
float a = 0;
float b = 0;
float sum = 0; //所有矩形的面積累加總和
float n = 1000; //將函數(shù)曲線下方劃為n個矩形,n值越大绎橘,精確值越高
float width = (end - begin) / n; //單個矩形寬度胁孙,函數(shù)總長度除以矩形數(shù)量得到
for(float i=1 ; i <= n ; i++) //計算每個矩形的面積
{
float x = begin + width * i; //通過i的遞增,得出每個矩形底部x點的值
float y = sqrt(r*r - (x-a)*(x-a)) + b;
float area = y * width; //求x點對應(yīng)的y值称鳞,取絕對值后涮较,得到高度;再用底寬度乘高得出單個矩形面積
sum += area; //累加矩形面積
}
return sum;
}
int main(){
printf("circle, x range is: [-2.0 , 2.0], area is: %f\n", integration(-2.0 , 2.0));
return 0;
}
編譯運行得到結(jié)果
circle, x range is: [-2.0 , 2.0], area is: 6.282976
這和我們用公式計算出來的結(jié)果6.2831852冈止,只相差了0.0002092狂票,萬分之2的差距,精度還可以熙暴。
接下來我們調(diào)高精度n闺属,設(shè)置成10000,計算后的結(jié)果是 6.283169周霉,相差了0.0000162掂器,這次是10萬分之一。
我們再調(diào)低精度n俱箱,到100国瓮,計算后的結(jié)果是 6.276536,這次相差0.0066492,差距拉大到千分之六了乃摹。
附錄
上文中的幾個圖像禁漓,我都是用python繪制出來,奉上python畫圖的源碼峡懈。
1. 正弦函數(shù)的圖
import matplotlib.pyplot as plt
import numpy as np
import mpl_toolkits.axisartist as axisartist
#創(chuàng)建畫布
fig = plt.figure(figsize=(8, 8))
#使用axisartist.Subplot方法創(chuàng)建一個繪圖區(qū)對象ax
ax = axisartist.Subplot(fig, 111)
#將繪圖區(qū)對象添加到畫布中
fig.add_axes(ax)
# 通過set_visible方法設(shè)置繪圖區(qū)所有坐標(biāo)軸隱藏
ax.axis[:].set_visible(False)
# 添加x坐標(biāo)軸璃饱,且加上箭頭
ax.new_floating_axis
ax.axis["x"] = ax.new_floating_axis(0,0)
ax.axis["x"].set_axisline_style("->", size = 1.0)
# 添加y坐標(biāo)軸,且加上箭頭
ax.axis["y"] = ax.new_floating_axis(1,0)
ax.axis["y"].set_axisline_style("-|>", size = 1.0)
# 設(shè)置x肪康、y軸上刻度顯示方向
ax.axis["x"].set_axis_direction("top")
ax.axis["y"].set_axis_direction("right")
#設(shè)置x荚恶、y坐標(biāo)軸的范圍
plt.xlim(-2,2)
plt.ylim(-1.5,1.5)
#plt.grid() # 網(wǎng)格線
plt.legend(loc="upper left") # 圖例說明,loc指定位置
#生成x步長為0.1的列表數(shù)據(jù)
x = np.linspace(-4, 4, 800)
y = np.sin(x)
#x - array( length N) 定義曲線的 x 坐標(biāo)
#y - array( length N ) 定義曲線的 y 坐標(biāo)
#如果數(shù)據(jù)點比較少的情況下磷支,會有縫隙出現(xiàn)谒撼,使用interpolate可以填充縫隙
plt.fill_between(x, y, where=(-0.5<=x) & (x<=1), facecolor='green', alpha=0.3, interpolate=True)
# 繪制填充紅色的矩形方塊,以展示積分的直觀圖
qujian = x[np.where((-0.5<=x) & (x<=1))]
i = 5
for xi in qujian:
if i > 5 :
rect = plt.Rectangle((xi,0),0.04,np.sin(xi)+0.02, color='red') # 之所以給加了個+0.02雾狈,是對畫出來的圖微微調(diào)整廓潜,更好看些。
ax.add_patch(rect)
i = 0
i = i + 1
#繪制圖形
plt.plot(x,y, c='b')
plt.show()
注意上面的“繪制填充紅色的矩形方塊”部分的代碼善榛,如果不想繪制方塊辩蛋,只看原圖的話,把這部分代碼注釋掉就行移盆。
2. 圓形圖
這里上下文和上文繪制正弦函數(shù)是一樣的悼院,只把核心畫圓部分貼出來,覆蓋之前畫正弦函數(shù)的部分就行了
......
plt.legend(loc="upper left") # 圖例說明咒循,loc指定位置
# 圓的基本信息
# 1.圓半徑
r = 2.0
# 2.圓心坐標(biāo)
a, b = (0., 0.)
theta = np.arange(0, 2*np.pi, 0.01)
x = a + r * np.cos(theta)
y = b + r * np.sin(theta)
plt.plot(x, y) # 畫圓
plt.axis('equal')
#x - array( length N) 定義曲線的 x 坐標(biāo)
#y - array( length N ) 定義曲線的 y 坐標(biāo)
#如果數(shù)據(jù)點比較少的情況下据途,會有縫隙出現(xiàn),使用interpolate可以填充縫隙
plt.fill_between(x, y, where=(-r<=x) & (x<=r) & (y>=0), facecolor='green', alpha=0.3, interpolate=True)
# 繪制填充紅色的矩形方塊叙甸,以展示積分的直觀圖
qujian = np.linspace(-r, r, 400)
i = 5
for xi in qujian:
if i > 5 :
y = np.sqrt([(r*r - (xi-a)*(xi-a))]) + b # 根據(jù)圓的公式 r^2 = (x-a)^2 + (y-b)^2 推算出y
rect = plt.Rectangle((xi-0.02,0), 0.04, y, color='red') # 畫矩形的時候有點偏差颖医,所以往左移了0.02。
ax.add_patch(rect)
i = 0
i = i + 1
plt.show()
3. 除了正弦函數(shù)裆蒸,我還寫了余弦熔萧、指數(shù)等函數(shù)的面積計算
#include <stdio.h>
#include <math.h>
// 求曲線面積(定積分)。參數(shù)為光戈,函數(shù)f(x)哪痰,x軸上區(qū)間[begin,end]兩個點的值
// @param f(float) 這個參數(shù)是'函數(shù)指針'傳值,傳遞的是一個函數(shù)的地址久妆;這個函數(shù)用來求x軸上某一點對應(yīng)的y值晌杰。
// @param begin x軸區(qū)間開始位置
// @param end x軸區(qū)間結(jié)束位置
float integration(float f(float), float begin, float end)
{
float sum = 0; //所有矩形的面積累加總和
float n = 1000; //將函數(shù)曲線下方劃為n個矩形,n值越大筷弦,精確值越高
float width = (end - begin) / n; //單個矩形寬度肋演,函數(shù)總長度除以矩形數(shù)量得到
for(float i=1 ; i <= n ; i++) //計算每個矩形的面積
{
float x = begin + width * i; //通過i的遞增抑诸,得出每個矩形底部x點的值
float area = fabs(f(x)) * width; //求x點對應(yīng)的y值,取絕對值后爹殊,得到高度蜕乡;再用底寬度乘高得出單個矩形面積
sum += area; //累加矩形面積
}
return sum;
}
// 第一個例子,函數(shù)f(x)為正弦曲線
float function1(float x){
return sin(x);
}
// 第二個例子梗夸,函數(shù)f(x)為余弦曲線
float function2(float x){
return cos(x);
}
// 第三個例子层玲,函數(shù)f(x)為指數(shù)曲線
float function3(float x){
return exp(x);
}
int main(){
printf("sin(x), x range is: [-0.5 , 1.0], area is: %f\n", integration(function1, -0.5 , 1));
printf("cos(x), x range is: [-1.0 , 1.0], area is: %f\n", integration(function2, -1 , 1));
printf("exp(x), x range is: [ 0.0 , 2.0], area is: %f\n", integration(function3, 0 , 2));
return 0;
}
編譯執(zhí)行
$ g++ test.cpp
$ ./a.out
sin(x), x range is: [-0.5 , 1.0], area is: 0.582387
cos(x), x range is: [-1.0 , 1.0], area is: 1.682942
exp(x), x range is: [ 0.0 , 2.0], area is: 6.395446
始于 2019-11-01,北京反症;更于 2019-11-02辛块,北京。
該文章在以下平臺同步