agd插值演算法 常見插值演算法研究

2021-10-13 05:44:35 字數 3687 閱讀 7479

最近在做心電相關的專案,由於微控制器的處理能力有限,在接收心電訊號之後需要對資料進行壓縮(其實是取一些特徵點),然後後期再進行顯示。但是在手持arm上進行顯示的時候,通過這些殘缺的資料繪出心電圖形是很困難的,這就要進行插值處理,所以進行了一些插值演算法相關的研究,常用的插值演算法是拉格郎日插值和牛頓插值演算法,切比雪夫插值演算法。

一. 拉格朗日插值

拉格郎日的原理,下面這篇帖子寫的比較好,感興趣可以看一下,對於應用型達人來講,不必對原理過於糾結。

複雜的公式:

其中每個

為拉格朗日基本多項式(或稱插值基函式),其表示式為:

拉格朗日基本多項式

的特點是在

上取值為1,在其它的點

上取值為0。

拉格郎日插值的本質就是通過一些離散的點**乙個函式,然後得到未知點所對應的函式值。很容易想到的就是n階多項式。

f(x) = a0*x^n+a1*x^(n-1)+……an

那我們求得係數就可以了。實際上,我們正是通過拉格朗日公式來解決這個問題,原理見上面貼子。

下面是使用該插值演算法時所遇到的問題:

1. 真實的心電訊號可能都是大於0的值。如果通過所有的離散點進行擬合,則從得到的擬合函式中所獲得的插值容易產生負值,一開始不理解,還以為是自己演算法問題,後來想想,為了使得到的函式穿過所有這些離散點,得到負值是完全有可能的。這就是常見的拉格朗日問題,當次數較高時,會出現數值不穩定的現象(龍格現象)。解決辦法就是分段低階插值。

2. 我改用了3點插值,3點插值也會出現負值,雖然頻率少了一些。因為3點其實構造的就算拋物線,當點的分布不均勻(2,3點離的很近)時,很容易會出現負值。如下:

3. 所以,最後只能採用兩點差值來實現趨勢的描繪,這是純折線的,而且就是線性插值,其實完全沒有必要用拉格朗日了。

……然後發現拉格朗日在實際中的應用很少,因為存在數值不穩定性的特性。

測量資料會有誤差,構造的函式還會有誤差,計算機計算浮點數也有誤差,所以,最終可能會造成很大的誤差。所以,雖然我們有了這個理論基礎,但是在實際應用的時候,仍然需要根據特殊的情況進行編寫,以適應我們的應用。比如可以讓距離間隔差不多的幾個點來進行3點插值,這樣拋物線會平滑,就不會產生負值了,當然,這只是針對我的這種情況而言。再比如,可以進行3點滑動插值,每次插值完成後,移除第乙個點,既每次插值都會保留上次所使用的插值點。

4. 由於拉格朗日插值演算法在插值點增加或者減少時,需要重新計算基本多項式,這樣效率會很低。為了解決這個問題,引入了牛頓插值法.

拉格朗日**實現:

//arrx[n],arry[n]分別存放的是已知插值節點(xi,yi)中的xi,yi,引數n為已知插值節點的個數,而引數x為待求解的插值節點的橫座標值

float lagrange(float arrx,float arry,int n, floatx)

float yresult = 0.0;

float lvalue[80];

intk,m;

floattemp1,temp2;

for(k=0;k

temp1 = 1.0;

temp2 = 1.0;

for(m=0;m

if(m==k)

continue;

temp1 *=(x-arrx[m]);

temp2 *=(arrx[k]-arrx[m]);

lvalue[k]=temp1/temp2;

for(int i=0;i

yresult+=arry[i]*lvalue[i];

returnyresult;

二. 牛頓插值演算法

牛頓插值法的公式如下:

其中,f[x]的求解如下:

牛頓插值演算法的關鍵在於獲得係數f(xk)。我們就以作者所取的3個點(0, 1), (2, 2), (3, 4)來**。根據公式,我們需要取得f(x1), f(x1,x2), f(x1,x2,x3).首先我們分別計算一下值,這是數學計算。

f(x1) = 1;

f(x1,x2) = (f(x2)- f(x1))/(x2- x1) = 1/2

f(x2,x3) = 2. 計算同上,雖然這不是有效係數,但是在進一步求係數的時候會用到。

f(x1,x2,x3) = (f(x2, x3)- f(x2, x3))/(x3- x1) = 1/2

所以,f(x) = 1+1/2*(x-0)+1/2(x-0)(x-2)

這就是理論學習中牛頓插值演算法所謂的差商原理。

這一步數學計算的目的可以加深大腦對計算過程的理解,推薦演算一遍。

下面是程式實現,僅僅有理論是不夠的,還得寫出**。

首先,我們需要兩個陣列,x[3],y[3],分別存放3個橫座標和對應的縱座標值。

x[3] = , y[3] = .

關鍵的問題還是求係數,作者給我們提供的思路是使用二維陣列來存放,我推演一遍。二維陣列a[i][j]的大小根據自己的需要來定。

先給出結果:

當j=0的時候,a[i][0] = y[i].

當i>=1,j>=1的時候,a[i][j] = (a[i][j-1]-a[i-1][j-1])/(x[i]-x[i-j])

自己歸納一下就可以得到上面的結論。

a[0][0] = y[0] = 1;

a[1][0] = y[1] = 2;  a[1][1] = (a[1][0]-a[0][0])/(x[1]-x[0]) = (2-1)/(2-0) = 1/2

a[2][0] = y[2] = 4;  a[2][1] = (a[2][0]-a[1][0])/(x[2]-x[1]) = (4-2)/(3-2) = 2

a[2][2] = (a[2][1]-a[1][1])/(x[2]-x[0]) = (2-1/2)/3 = 1/2

所以,f(x) = 1+1/2*(x-0)+1/2(x-0)(x-2)

有效的係數全部存在了a[i][i](i=j的時候)中。

有了上面的二維陣列,就可以很容易的實現牛頓插值演算法了。

牛頓**實現:

插值係數計算:

//求得係數,length為已知的插值點個數

for(i=0;i

for(j=0;j<=i;j++)

if(j==0)

a[i][j] =y[i];

continue;

a[i][j] = (a[i][j-1]-a[i-1][j-1])/(x[i]-x[i-j]);

//牛頓插值演算法

float newton(float arrx, float arry,float factor[40],int n,intx)

float result = arry[0];

float tmp = 1.0;

inti,j;

for(i=1;i

for(j=0;j<=i;j++)

if(j!=i)

continue;

tmp *= (x-arrx[i-1]);

result += factor[i][j]*tmp;

returnresult;

三.   切比雪夫插值

切比雪夫插值的提出是為了改善插值在差值區間上的最大誤差控制

關鍵:切比雪夫插值節點的選取

對於乙個插值區間 [a, b], 如果要在這個插值區間上選取n個點作為插值基點,使得上面的最大誤差

最小,則基點的選法如下:

對於 i = 1, 2, ...., n. 下面的不等式在區間 [a, b] 上滿足

切比雪夫的實現就是在牛頓插值中選取合適的基點,並且要用到cos函式。這個演算法感興趣的可以自己研究一下。

插值查詢演算法

插值查詢演算法是基於二分查詢演算法的,只是在查詢的過程中計算middle的方式有所改變,比如一組可以通過二分查詢演算法計算出的nmid索引的值,是這樣子計算的 nmid nlow nhigh 2,但是插值查詢演算法是根據比率算出的,nmid nlow key narr nlow narr nhigh...

插值查詢演算法

在均勻分布的有序數列中,會比二分查詢次數少很多 思想跟二分查詢一直,都是找到其中乙個值,小了往左找,大了往右找 區別是 也跟二分查詢差不多,就公式不同,不過因為公式複雜了一些,需要一些判斷條件來保證它的正確性 public static int binarysearch int arr,int ke...

Catmull Rom插值演算法

一 簡要介紹 catmull rom演算法保證兩點 1 點pi的一階導數等於pi 1 pi 1,即點pi的切向量和其相鄰兩點連線的切向量是平行的 2 穿過所有pi點。這是與貝塞爾曲線的最大區別,正因為這樣的特性,使得catmull rom演算法適於用作軌跡線演算法。點pi處的切線記作 pi 1 pi...