이동평균 필터

이동평균은 추세의 변동을 알 수 있도록 구간을 옮겨 가며 구하는 평균을 말한다.




실제로 이동평구현한 결과


< 이동평균 필터 >



< 이동평균 필터(순간변화) >


사용 결과 약간의 딜레이가 생기는거 같은데 FILTERSIZE를 줄이면 딜레이가 줄어들 것 같다. 


작업환경 : Codevision (Version : 2.05.0 Evaluation)

---------------------- mov_average.h -----------------------


#ifndef __AVERAGE_H__

#define __AVERAGE_H__


#define FILTERDATA 10

int avg (int x);


#endif // __AVERAGE_H__


-----------------------------------------------------



---------------------- mov_average.c ----------------------


#include <mega128.h>

#include "my_header.h"


int data[FILTERDATA];


int avg (int x)

{

    unsigned char i;

    int sum = 0, average;

    

    for (i = 0; i < FILTERDATA - 1; i++)

        data[i] = data[i+1];

        

    data[FILTERDATA - 1] = x;

        

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

        sum += data[i]; 

    

    average = sum / FILTERDATA;

    

    return average;

}  


-------------------------------------------------




이동평균필터 재귀식



< 배치식 >


유도 과정



식을 자세히 보면, 새로운 평균값은 이전 평균값에서 가장 오래된 데이터 값을 데이터 개수로 나눈 값을 빼주고 새로운 값을 데이터 개수로 나누 값을 더해준 결과이다. 즉, 오래된 데이터값 대신 새로운 데이터 값을 가중치를 곱해주어 더해준 값이다. 일반적인 평균에서 각 데이터의 가중치는 항상 1/n 으로 일정하다.



실행 결과







주황색이 이동평균필터 재귀식을 사용한 것이고 회색이 그냥 이동평균필터이다. 보면 거의 비슷한 것을 알 수 있다.



개발 환경 : Codevision (Version : 2.05.0 Evaluation)


---------------------- mov_average_recursioin.h -----------------------


#ifndef __MOVE_AVERAGE_FILTER__

#define __MOVE_AVERAGE_FILTER__


#define MOV_FILTERSIZE 4

#define CHANNEL     4



//int MovingAverage(int, unsigned char);

int MovingAverage(int input);

#endif //__MOVE_AVERAGE_FILTER__


-------------------------------------------------------------------



---------------------- mov_average_recursioin.c ----------------------


#include "my_header.h"


int MovingAverage(int input)

{

    static int data[MOV_FILTERSIZE];

    

    static int average = 0;

    unsigned char i;

    

    average = average + (input - data[0])/MOV_FILTERSIZE;

         

        

    for (i = 0; i < MOV_FILTERSIZE - 1; i++)

        data[i] = data[i+1];

        

    data[i] = input;

        

    return average;

}

------------------------------------------------------------------


---------------------- mov_average_recursioin.c (쉬프트 연산자로 하는 방법) ----------------------

int average = 0;


void MovAvgRecursion(int input)

{

    static int data[MOV_FILTERSIZE];

    unsigned char i;    

    int temp;

    

    

    temp = input - data[0];    

    

    if (temp < 0) temp += 3;

    average += temp >> 2;

    

    for (i = 0; i < MOV_FILTERSIZE - 1; i++)

        data[i] = data[i+1];

        

    data[i] = input;

}


----------------------------------------------------------------

Posted by 나무길 :

평균 필터


 데이터를 모두 모아서 한 번에 연산하는 과정을 배치식 (batch expression)이라고 한다. 데이터의 총합을 데이터 개수로 나눠주는 단순한 계산. 하지만 배치식은 앞서 계산했던 평균값은 전혀 이용하지 못하게 되어 불필요하게 시간 또는 자원(메모리)을 낭비하게 된다. 



평균필터 재귀식 설명

배치식과 달리 재귀식은 앞선 결과 값을 이용한다는 점에서 게산의 효율과 메모리의 효과적인 사용에 매우 좋다. 평균 필터의 재귀식은 아래와 같이 표현된다. (식이란 재귀함수로 코딩했다는 것이 아니다. 재귀함수와 재귀식은 다른 것. 재귀함수로 작성하게 되면 더 비효율적인 메모리와 시간 낭비가 발생한다.)



재귀식의 유도과정



식을 자세히 보면, 새로운 평균값은 이전 평균값에 (k - 1)/k 의 가중치[각주:1]가 곱해졌고 새로운 값은 1/k의 가중치가 곱해져 더해진 값으로 구성되었다. 예를 들어 이전에 값이 [4 6 8 6]이고 새로 얻은 값이 10이라고 하면 앞선 4개의 값의 평균인 6에 가중치 4/5를 곱하고 새로 얻은 값인 10에 가중치 1/5를 곱해 더하면 5개의 평균값이 나온다. (24 + 10) / 5 = 6.8이 평균이다. 5로 나눈 이유는 총 개수가 5개이므로 5로 나눈것. 앞서 구한 평균값에 데이터 개수 4개를 다시 곱해 다시 나눈다는 의미와 같다. 

이렇게 복잡하지만 재귀식을 사용하는 이유는 시간낭비를 줄이고 데이터 저장 공간을 효율적으로 사용하기 위함이다.




실제로 한번 해보았다.



잘된다.





개발 환경 : Codevision (Version : 2.05.0 Evaluation)


-------------------------------------- average_recursion.h ------------------------------------


#ifndef __AVERAGE_RECURSION_H__

#define __AVERAGE_RECURSION_H__


#define FILTERDATA 10



int average_recursion (int x);


#endif //__AVERAGE_RECURSION_H__


--------------------------------------------------------------------------------------------



---------------------------------------- average_recursion.c ------------------------------------

#include "my_header.h"




int average_recursion (int x)

{

    static int average = 0;

    

    average = (((FILTERDATA-1)*average)/FILTERDATA) + (x/FILTERDATA); 

    

    return average;

}


-------------------------------------------------------------------------------------------


  1. 비중을 서로 달리하는 여러 품목에 대한 하나의 평균치를 산출할 때, 단순한 산술평균만으로는 합리적인 수치를 뽑을 수가 없으므로 비중에 따라 각 개별품목에 알맞은 중요도를 결정하고 이를 적용시켜 평균치를 얻게 된다. 중요도를 결정하는 표준은 종합평균(지수)의 성질에 따라서 다르다. [본문으로]
Posted by 나무길 :

재귀함수

2015. 11. 2. 11:34 from Language/C언어

언젠가 한번 공부해야지 하고 밀어두다고 씨름로봇하면서 평균필터 재귀함수로 작성해야될 일이 생겨서 공부한다.





재귀함수, 순환함수, Recursion 이라고도 한다. 사용법은 간단하다. 팩토리얼 재귀함수를 예로 들어본다.


< 팩토리얼 재귀함수 >


재귀함수는 반드시 두부분으로 나뉘어야 한다.빨간색 부분은 이 재귀가 드디어 멈추는 구간이다. 반드시 멈출 수 있는 조건을 작성해 주어야 한다. return 부분에 초록색 동그라미는 바로 재귀호출을 하고 있는 문장이다.




구체적인 함수흐름


< 함수 흐름도 >


자기 자신을 호출한다고 하지만, 실제로 동작 순서는 위와 같다. 재귀함수를 이해할 때는 항상 위와 같은 그림을 연상하는게 정신건강에 이롭다. 



효율성


반복문과 재귀함수 효율은 어떨까? 아무리 찾아봐도 재귀함수가 효율이 좋다는 말은 없다. Stack Overflow 라는 것이 있는데 함수가 호출 될 때 스택 메모리를 사용한다. 재귀호출은 함수가 꼬리에 꼬리르 물고 실행되기 때문에 계속해서 메모리에 쌓이게 된다. 그렇다보니 시간과 메모리공간의 효율성이 떨어지는 단점이 있다. 특히 피보나치 수열같은 함수를 재귀함수로 사용하게되면 스택을 기하급수적으로 증가시키기 때문에 매우 비효율 적이다.


< 피보나치 수열 >


마지막에 return fibo(n-1) + fibo(n-2); 이부분에서 각 함수자체가 또 재귀를 또또또........ 호출한다.

'Language > C언어' 카테고리의 다른 글

C언어의 메모리 구조  (0) 2015.06.06
선언(declaration)과 정의(definition)의 차이  (0) 2013.11.08
C언어 따옴표의 의미  (0) 2013.11.06
반복문 for 의 함정  (1) 2013.10.23
강제 형 변환  (0) 2013.10.23
Posted by 나무길 :