程序?qū)崿F(xiàn)黎曼和(定積分)

想象一下,如果你手里有一塊形狀不規(guī)則的土地(實(shí)際上我沒有,窮...),要測(cè)量它的面積,怎么辦呢?

拿尺子量,不知如何下手,突然感覺高中幾何解決不了,得祭出本科的高等數(shù)學(xué)才行。所以,慣例我們應(yīng)該發(fā)揚(yáng)拿來主義,比如 “國際上,如何如何...”:

一個(gè)叫黎曼的德國數(shù)學(xué)家(Bernhard Riemann, 1826-1866),他想了個(gè)辦法:將這不規(guī)則圖形切成一條條的小長(zhǎng)條兒,然后將這個(gè)長(zhǎng)條近似的看成一個(gè)矩形,再分別測(cè)量出這些小矩形的長(zhǎng)度,再計(jì)算出它們的面積,把所有矩型面積加起來就是這塊不規(guī)則地的面積。這就是著名的“黎曼和”。小長(zhǎng)條寬度趨于0時(shí),即為面積微分,各個(gè)面積求和取極限即為定積分。雖然牛頓時(shí)代就給出了定積分的定義,但是定積分的現(xiàn)代數(shù)學(xué)定義卻是用黎曼和的極限給出。

好吧,大致意思是理解了,光說不行,得練習(xí)。于是我先造出來一塊地來當(dāng)?shù)刂?,就?dāng)下圖綠色部分是我的地吧


image.png

誰家能把土地劃成這樣啊,好吧,承認(rèn)是為了一會(huì)切割小矩形的時(shí)候,方便計(jì)算高而特意用了正弦曲線,否則要是一點(diǎn)曲線規(guī)律都沒有,那真的要用尺子去一個(gè)個(gè)量小矩形的高了,累死。

計(jì)算正弦曲線封閉區(qū)間和y軸相封存的面積

言歸正傳,說一下應(yīng)用題的要求吧。

如上圖的曲線是一個(gè)sin正弦函數(shù),公式 y=sin(x),我們要求的是這個(gè)函數(shù)與x軸閉區(qū)間[-0.5,1.0]所夾的部分面積,也就是綠色部分。

按照定積分的分割方法,我們把這片面積分割成n個(gè)小矩形,挨個(gè)計(jì)算面積,累加求和就是大約綠色部分的總面積了。分割的n越多,越接近于真實(shí)面積,但是計(jì)算量也會(huì)增大;反之分割的越少越省事,但是精度就下降了。大致如下圖這般:

用python畫了個(gè)示意圖,以表明積分的原理


image.png
#include <stdio.h>
#include <math.h>

// 求曲線面積(定積分)。參數(shù)為,函數(shù)f(x),x軸上區(qū)間[begin,end]兩個(gè)點(diǎn)的值
// @param begin    x軸區(qū)間開始位置
// @param end      x軸區(qū)間結(jié)束位置
float integration(float begin, float end)
{
  float sum = 0;         //所有矩形的面積累加總和
  float n = 1000;        //將函數(shù)曲線下方劃為n個(gè)矩形,n值越大,精確值越高
  float width = (end - begin) / n;  //單個(gè)矩形寬度,函數(shù)總長(zhǎng)度除以矩形數(shù)量得到
  for(float i=1 ; i <= n ; i++)     //計(jì)算每個(gè)矩形的面積
  {
    float x = begin + width * i;     //通過i的遞增,得出每個(gè)矩形底部x點(diǎn)的值
    float y = fabs(sin(x));
    float area = y * width; //求x點(diǎn)對(duì)應(yīng)的y值,取絕對(duì)值后,得到高度;再用底寬度乘高得出單個(gè)矩形面積
    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。

到這里又開始有疑問了,如何驗(yàn)證正確率?

那么,我該祭出不定積分公式了,面積

A = \int_a^b f(x)dx

則對(duì)公式進(jìn)行推導(dǎo)出其原函數(shù),然后限定[a,b]的區(qū)間,就是定積分了,求面積。

按上圖,積分函數(shù)f(x)=sin(x),公式為。
A = \int_{a}^ sin(x)dx
把原函數(shù)找出來
A = (-cos(x) + C) |_{a}^
上限b減去下限a,展開后常數(shù)C抵消了,最終成了
A = cos(a) - cos(b)

設(shè)置定積分下限a=-0.5,上線b=1.0。但是結(jié)果卻錯(cuò)了,算下來0.3多,和我們上面的0.5多相差太大。原來看圖,就明白了,圖是兩塊啊,分布在y軸兩側(cè),不能直接這么算,應(yīng)該拆分一下,變成[-0.5,0]和[0,1]兩個(gè)區(qū)間,分別運(yùn)用上面的公式計(jì)算,并取絕對(duì)值

A = |cos(-0.5) - cos(0)| + |cos(0) - cos(1)| = 0.582114

怎么樣,我們用代碼堆疊小方塊算出來的0.582387,是不是很接近用公式計(jì)算出來的結(jié)果0.582114?只相差了0.000273。

另外一個(gè)驗(yàn)證的方式,用高大上的在線定積分計(jì)算器 https://zh.numberempire.com/definiteintegralcalculator.php 核對(duì)下,輸入函數(shù)是sin(x),自變量x從-0.5到0,結(jié)果是 ?0.122417,取絕對(duì)值后是 0.122417;然后自變量x再輸入0到1,結(jié)果是 0.459697,兩者相加得到 0.582114。

計(jì)算圓的面積

為了更好直觀的說明原理,我又換了一個(gè)容易計(jì)算的圖,如下

image.png

這次,圓的函數(shù)方程式為 r^2 = (x-a)^2 + (y-b)^2 可以用這個(gè)來計(jì)算x和y的關(guān)系,a、b是圓心坐標(biāo)。

對(duì)于面積,我們有公式 s=πr^2 就可以推算出,再除以2就是半圓的面積。計(jì)算下來,大概是6.2831852。

接下來,我們繼續(xù)用上文的求定積分的方式,結(jié)合圓的函數(shù)方程式,計(jì)算出半圓面積。

image.png
#include <stdio.h>
#include <math.h>

// 求曲線面積(定積分)。參數(shù)為,函數(shù)f(x),x軸上區(qū)間[begin,end]兩個(gè)點(diǎn)的值
// @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個(gè)矩形,n值越大,精確值越高
  float width = (end - begin) / n;  //單個(gè)矩形寬度,函數(shù)總長(zhǎng)度除以矩形數(shù)量得到
  for(float i=1 ; i <= n ; i++)     //計(jì)算每個(gè)矩形的面積
  {
    float x = begin + width * i;     //通過i的遞增,得出每個(gè)矩形底部x點(diǎn)的值
    float y = sqrt(r*r - (x-a)*(x-a)) + b;
    float area = y * width; //求x點(diǎn)對(duì)應(yīng)的y值,取絕對(duì)值后,得到高度;再用底寬度乘高得出單個(gè)矩形面積
    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;
}

編譯運(yùn)行得到結(jié)果

circle, x range is: [-2.0 , 2.0], area is: 6.282976

這和我們用公式計(jì)算出來的結(jié)果6.2831852,只相差了0.0002092,萬分之2的差距,精度還可以。

接下來我們調(diào)高精度n,設(shè)置成10000,計(jì)算后的結(jié)果是 6.283169,相差了0.0000162,這次是10萬分之一。

我們?cè)僬{(diào)低精度n,到100,計(jì)算后的結(jié)果是 6.276536,這次相差0.0066492,差距拉大到千分之六了。

附錄

上文中的幾個(gè)圖像,我都是用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)建一個(gè)繪圖區(qū)對(duì)象ax
ax = axisartist.Subplot(fig, 111)
#將繪圖區(qū)對(duì)象添加到畫布中
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步長(zhǎng)為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ù)點(diǎn)比較少的情況下,會(huì)有縫隙出現(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') # 之所以給加了個(gè)+0.02,是對(duì)畫出來的圖微微調(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ù)點(diǎn)比較少的情況下,會(huì)有縫隙出現(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') # 畫矩形的時(shí)候有點(diǎn)偏差,所以往左移了0.02。
    ax.add_patch(rect)
    i = 0
  i = i + 1

plt.show()

3. 除了正弦函數(shù),我還寫了余弦、指數(shù)等函數(shù)的面積計(jì)算

#include <stdio.h>
#include <math.h>

// 求曲線面積(定積分)。參數(shù)為,函數(shù)f(x),x軸上區(qū)間[begin,end]兩個(gè)點(diǎn)的值
// @param f(float) 這個(gè)參數(shù)是'函數(shù)指針'傳值,傳遞的是一個(gè)函數(shù)的地址;這個(gè)函數(shù)用來求x軸上某一點(diǎn)對(duì)應(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個(gè)矩形,n值越大,精確值越高
  float width = (end - begin) / n;  //單個(gè)矩形寬度,函數(shù)總長(zhǎng)度除以矩形數(shù)量得到
  for(float i=1 ; i <= n ; i++)     //計(jì)算每個(gè)矩形的面積
  {
    float x = begin + width * i;     //通過i的遞增,得出每個(gè)矩形底部x點(diǎn)的值
    float area = fabs(f(x)) * width; //求x點(diǎn)對(duì)應(yīng)的y值,取絕對(duì)值后,得到高度;再用底寬度乘高得出單個(gè)矩形面積
    sum += area;         //累加矩形面積
  }
  return sum;
}

// 第一個(gè)例子,函數(shù)f(x)為正弦曲線
float function1(float x){
  return sin(x);
}

// 第二個(gè)例子,函數(shù)f(x)為余弦曲線
float function2(float x){
  return cos(x);
}

// 第三個(gè)例子,函數(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,北京。

該文章在以下平臺(tái)同步

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容