當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式微處理器
[導(dǎo)讀][導(dǎo)讀] 在嵌入式產(chǎn)品開發(fā)中,有時會需要利用一些數(shù)學(xué)統(tǒng)計(jì)的一些知識,并利用代碼的方式實(shí)施在產(chǎn)品的應(yīng)用中。有人會說均值有啥好聊的,不就是加起來除一下嘛?不妨來讀一讀。 本文目的不是數(shù)學(xué),而在于分享如何進(jìn)行工程應(yīng)用實(shí)現(xiàn)。 什么是均值? 對于離散數(shù)據(jù)集

[導(dǎo)讀] 在嵌入式產(chǎn)品開發(fā)中,有時會需要利用一些數(shù)學(xué)統(tǒng)計(jì)的一些知識,并利用代碼的方式實(shí)施在產(chǎn)品的應(yīng)用中。有人會說均值有啥好聊的,不就是加起來除一下嘛?不妨來讀一讀。

本文目的不是數(shù)學(xué),而在于分享如何進(jìn)行工程應(yīng)用實(shí)現(xiàn)。

什么是均值?

對于離散數(shù)據(jù)集,算術(shù)平均值也稱為期望值或簡稱為平均值,是離散數(shù)據(jù)集合的中心值。假設(shè)有這樣的數(shù)據(jù)序列: ,其均值由下面的式子計(jì)算:
對,你說的沒錯就是加起來求平均。有盆友或許會問,為什么均值有的地方寫的是 ,而這里寫成 ,這其實(shí)是有緣由的:
  • 樣本均值(sample mean):某類隨機(jī)變量有限樣本的算術(shù)平均值。
  • 總體均值(population mean):從隨機(jī)變量概率分布的角度對隨機(jī)變量趨勢的度量,所以從這個角度而言,下面的公式正是描述了這個概念:
弄這樣兩個概念有什么必要呢?總體均值反應(yīng)的是事務(wù)的總體規(guī)律,實(shí)際研究中,往往很難得到所有的數(shù)據(jù),比如產(chǎn)品的某項(xiàng)指標(biāo)規(guī)律,如果每一個產(chǎn)品都去測,代價可能極其高昂,實(shí)際往往是對產(chǎn)品進(jìn)行抽樣檢測。(公式中多寫了個X)
大數(shù)定律指出樣本數(shù)量越大,樣本均值越接近總體均值
如此一來這就有實(shí)用意義了。
均值除了上面這種算術(shù)平均值之外,還有幾何平均值、諧波均值、功率均值、加權(quán)均值、截?cái)嗑?、函?shù)泛化均值等,有興趣的可以去了解一下。

如何計(jì)算均值?

這里主要討論對于嵌入式電子系統(tǒng)編程中,樣本均值的計(jì)算方法以及C代碼。分享直接法和遞推法計(jì)算均值,重點(diǎn)介紹遞推法。

直接法

按照公式定義,加和求平均。這個編代碼很容易:

   
float mean(float *pSample,int size)
{
    if(pSample== NULL || size<= 0)
        return NAN;
  
    float sum =  0;
    for( int i= 0;i<size;i++)
   {
     sum += *pSample;
     pSample++;
   }

    return (sum/size);
}
該方法簡單直接,但是缺點(diǎn)是在內(nèi)存比較小的單片機(jī)系統(tǒng)中如需要計(jì)算大樣本集的場合,就捉襟見肘了。比如幾萬樣本時,內(nèi)存可能就不夠了!

遞推法

因?yàn)闃颖揪涤?jì)算公式如下:
那么前 個樣本的均值為:
不難得出:
從而
這樣就可以編代碼了:

   
float recursive_mean(float xn,int size)
{
   static  int index =  0;
   static  float last_mean =  0.0f;
   float mean =  0.0f;

   if(index<size -1)
  {
    index++;
    mean = last_mean+(xn-last_mean)/index;
  }
   else
  {
    mean = last_mean+(xn-last_mean)/size;
    index     =  0;
    last_mean =  0.0f;
  }

  last_mean = mean;
   return mean;
}
這個代碼很容易理解:
  • 在樣本窗未滿時,按實(shí)際傳入樣本大小遞推。
  • 在樣本窗滿后,按實(shí)際傳入樣本大小遞推,并復(fù)位索引。
函數(shù)內(nèi)靜態(tài)變量不推薦使用,但這里函數(shù)使用了內(nèi)部靜態(tài)變量,為什么使用靜態(tài)變量呢?因?yàn)樗鶎?shí)現(xiàn)的需求對外部不可見,這種需求本身的作用域就在函數(shù)本體內(nèi)部。這樣寫個人理解會更好一些。
關(guān)于static的用法,前面寫過兩篇文章,有興趣的可以去點(diǎn)進(jìn)去看看:
  • 實(shí)例分析如何遠(yuǎn)離漫天飛舞的全局變量

  • 由static來談?wù)勀K封裝

在嵌入式應(yīng)用中,如果所需要統(tǒng)計(jì)的樣本非常大時,這種算法將非常有實(shí)用價值,只需要極小的內(nèi)存開銷。 尤其在一些傳感器測量應(yīng)用中,該方法非常有價值。

測試一下


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

float mean(float *pSample,int size)
{
    if(pSample== NULL || size<= 0)
        return NAN;
    float sum =  0;
    for( int i= 0;i<size;i++)
   {
     sum += *pSample;
     pSample++;
   }

    return (sum/size);
}

float recursive_mean(float xn,int size)
{
   static  int index =  0;
   static  float last_mean =  0.0f;
   float mean =  0.0f;

   if(index<size -1)
  {
    index++;
    mean = last_mean+(xn-last_mean)/index;
  }
   else
  {
    mean = last_mean+(xn-last_mean)/size;
    index     =  0;
    last_mean =  0.0f;
  }

  last_mean = mean;
   return mean;
}
#define N            (1000)
#define SAMPLE_SIZE  (100)
int main(int argc, char *argv[])
{
     float sim[N];
     float out[N/SAMPLE_SIZE];

     for( int i= 0;i<N;i++)
    {
        sim[i]=i* 5+rand()% 10;
    }
     printf( "\n\n");
     int j= 0;
     for( int i= 0;i<N;i=i+SAMPLE_SIZE)
    {
        out[j] = mean(&sim[i],SAMPLE_SIZE);
        j++;
    }

     for(j= 0;j<N/SAMPLE_SIZE;j++)
    {
         printf( "%.2f,",out[j]);
    }
     printf( "\n");
    j =  0;
     for( int i= 0;i<N;i++)
    {
        out[j]=recursive_mean(sim[i],SAMPLE_SIZE);

         if((i+ 1)%SAMPLE_SIZE== 0)
            j++;
    }
     for(j= 0;j<N/SAMPLE_SIZE;j++)
    {
         printf( "%.2f,",out[j]);
    }
     printf( "\n");
     return  0;
}
看一下結(jié)果:

   
252.14, 752.38, 1251.85, 1751.82, 2252.57, 2752.25, 3251.78, 3751.58, 4252.06, 4752.02,
252.14, 752.38, 1251.85, 1751.82, 2252.57, 2752.25, 3251.78, 3751.58, 4252.06, 4752.02,
兩種計(jì)算方法效果一樣,但是第二種方法消耗極小的內(nèi)存。當(dāng)然這增加了函數(shù)調(diào)用次數(shù),但是在大樣本計(jì)算時非常有利。

總結(jié)一下

在實(shí)際應(yīng)用中,常常需要求取測量的均值,或者依據(jù)均值做相應(yīng)的應(yīng)用,而且均值是計(jì)算信號序列或者樣本集其他數(shù)學(xué)統(tǒng)計(jì)規(guī)律的基礎(chǔ)計(jì)算,比如要計(jì)算方差、協(xié)方差等等。那么實(shí)際應(yīng)用中學(xué)會如何計(jì)算均值并進(jìn)行編碼實(shí)現(xiàn)時很必要的。尤其在一些內(nèi)存受限的場景,學(xué)會利用遞推規(guī)律進(jìn)行計(jì)算很有學(xué)習(xí)掌握的價值。


-END-


本文授權(quán)轉(zhuǎn)載自嵌入式客棧,作者:逸珺




推薦閱讀



【01】C語言內(nèi)存泄露很嚴(yán)重,如何應(yīng)對?
【02】編譯C語言程序,使用 gcc 指令,而C++程序則推薦使用 g++指令!
【03】C語言:優(yōu)雅的字符串函數(shù)庫
【04】在C 語言中,請一定記得初始化局部變量!
【05】嵌入式編程是否應(yīng)該用C++替代C語言


免責(zé)聲明:整理文章為傳播相關(guān)技術(shù),版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

嵌入式ARM

掃描二維碼,關(guān)注更多精彩內(nèi)容

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉