반응형

그래프 데이타에서 특정 Y 값에 대응하는 X 값을 찾을 경우가 있습니다. 

이런경우 X 데이타 배열에서 설정된 X 과 같은값을 찾아서 대응하는 Y 값을 리턴하면 되지만 X값이 데이타 배열상의 값과 일치하지 않고 중간값에 해당하는 값일 경우가 있습니다. 

 

이때 이 Y 값에 대응하는 X값을 찾는 알고리즘입니다. 

알고리즘은 Y에 대응하는 가장 근사값을 먼저찾고 (Y1) 그 다음값(Y2)를 이용하여 X1, X2를 찾습니다. 이렇게 찾은 두점에 대한 직선의 방적식을 계산한 후 그 직선의 방정식에서 설정한 Y 값을 대입하여 X값을 찾는 방식입니다. 

public static double Ratio_Method_Value(in double[] xArray, in double[] yArray, double yValue)
{
    /************************************************************************
    function : y 값에 대응해는 x 값을 찾는 함수 
    param-1 : x 축의 배열 데이타 
    param-2 : y 축의 배열 데이타 
    param-3 : 설정 y 값 
    ************************************************************************/
    double xValue = 0;

    if (yValue > yArray.Min() && yValue < yArray.Max())    
    {
        for (int i = 0; i < yArray.Length - 1; i++)
        {
            if ((yValue >= yArray[i] && yValue <= yArray[i + 1]) || (yValue <= yArray[i] && yValue >= yArray[i + 1]))
            {
                double x1 = xArray[i];
                double x2 = xArray[i + 1];
                double y1 = yArray[i];
                double y2 = yArray[i + 1];

                xValue = x1 + (x2 - x1) * (yValue - y1) / (y2 - y1);
                break;
            }
        }
    }

    return xValue;
}
반응형
반응형

아래 이미지와 같은 그래프가 있을때 최고 peak 값 1개를 찾는 것은 쉽다. 

그런데 아래 그림에서 각각의 peak 점을 모두 찾아야 할 경우가 있다. 이처럼 peak 가 여럿인 그래프에서 모든 peak 점을 찾는 알고리즘을 만들어 보았다. 

private List<int> PeakDetect(in double[] data, int count, double threshold)
{
    List<int> peakPos = new List<int>();
    List<int> peaks = new List<int>();
    double[] imsi = new double[count + 1];
    bool peak;

    for(int index = 0 ; index < data.Length - count ; index++)
    {
        Array.Copy(data, index, imsi, 0, count + 1);  // 영역 데이타의 상승 경향을 보기위한 값들을 복사한다. (선택한 카운트 + 1)
        var beforeValue = imsi[0];
        peak = true;

        for(int i = 1 ; i < count ; i++)
        {
            if(beforeValue <= imsi[i]) beforeValue = imsi[i];  // 데이타가 계속 상승하는 곡선인지 확인 
            else
            {
                peak = false;  // 중간에 값이 하강하면 Peak 가 아니다.
                break;
            }
        }

        if(peak == true)  // 영역의 데이타가 모두 상승 경향일 경우 
        {
            // 영역데이타가 마지막 값 이후에 하락하는 경우 && threshold 값 이상일 경우 peak 일 수 있다. 
            if(beforeValue > imsi[count] && beforeValue > threshold) peakPos.Add(index + count - 1);  
        }
    }

    foreach(int pos in peakPos)  // 앞단에서 찾은 Peak 가능 값들을 다시 확인한다. 
    {
        Array.Copy(data, pos, imsi, 0, count);  // peak 예상 위치에서 카운트 만큼 데이타를 취한다. 
        var beforeValue = imsi[0];
        peak = true;

        for(int i = 1 ; i < count ; i++)
        {
            if(beforeValue > imsi[i]) beforeValue = imsi[i];  // 데이타가 계속 하강경향인지 확인한다. 
            else
            {
                peak = false;   // 중간에 값이 상승하면 peak가 아니다. 
                break;
            }
        }

        if(peak == true) peaks.Add(pos);
    }

    return peaks;
}

 

 

 

List<int> PeakDetect(in double[] data, int count, double threshold)

 

return : searching 된 모든 peak 위치값

param-1 : 데이타 

param-2 : 영역데이타의 카운트 ( 연속적인 상승 및 하강에 대한 정보를 확인하기 위한 데이타 영역 카운트)

                 카운트가 작으면 peak 의 작은 변화도 peak로 감지해 버린다. 

param-3 : searching 된 Peak 의 값이 threshold 이하인 경우 peak로 찾지 않고 제외한다. 

반응형
반응형

장비제어 프로그램을 하다보면 작업 프로세스가 순차적으로 이루어 지는 경우가 많이 있습니다. 

또한 각각의 순차적인 작업 내에서 에러 발생이나 상황에 따른 작업 Skip 이 필요한 경우도 발생을 합니다. 

코드를 순차적으로 짜면 되겠지만 그렇게 되면 조건문도 많이 들어가고 나중에 코드를 보거나 수정이 필요할 때 많이 피곤해 집니다. 

 

저는 이런 상황에서 while 문과 switch 문을 사용하여 처리를 합니다. 

이렇게 코딩을 하면 보기도 편하고 수정도 편하고 작업의 스킵이나 오류 발생시 처리도 상당히 요긴합니다. 

아래에 작업에 대한 코드 예를 들었습니다. 

 

int WorkFunction()
{
	int error = 0 ;
	int index = 0 ;
	bool NextSkip = false ;
    
	while(true)
	{
		switch(index)
		{
			case 0 : error = function1() ;    	break ;
			case 1 : error = function2(ref NextSkip) ; 		
					 if(NextSkip == true) index ++ ;
					 break ;
			case 2 : error = function3() ; 		break ;
			case 3 : error = function4() ;		break ;
		}
        
		if(error != 0) break ;
		if(++ index > 3) break ;
	}
    
	return error ;
}
반응형
반응형

예전 어느 일본업체에서 사용하는 스무징 알고리즘을 구하게 되어서 제가 사용하고 있습니다. 

상당히 스무징이 깨끗하게 잘 됩니다. 그러다 보니 사용하는 데이타의 종류에 따라 스무징 알고리즘을 다르게 사용해야 합니다. 

 

아래 스무징도 아무 데이타에 사용은 못하고 제가 꼭 필요로 하는 데이타의 스무징 종류에 따라 구분해 사용합니다. 

 

smoothing
Smoothing 결과

void Smoothing(double fltData[], short siMaxMeasCount, short siSCount, short siSStep)
{
    double	fltSmooth[1000*2];

	int	iLstep,iRstep;
	int	iStart_p,iEnd_p;
	int	iCnt;
	double	fltSum;

	if(siSStep < 2)	return;

	if(!(siSStep % 2))	siSStep++;
	iLstep = siSStep / 2;
	iRstep = iLstep;

	for (int i = 0; i < siSCount; i++)
	{
		memcpy(fltSmooth,fltData,sizeof(double) * siMaxMeasCount);

		for(int j = 1; j < siMaxMeasCount; j++)
		{
			iStart_p = j - iLstep;
			iEnd_p = j + iRstep;
			if(iStart_p < 0)
			{
				iStart_p = 0;
				iEnd_p = j + j;
			}
			else if(iEnd_p >=  siMaxMeasCount)
			{
				iStart_p = j - (siMaxMeasCount - j - 1);
				iEnd_p = siMaxMeasCount-1;
			}

			if(iEnd_p > siMaxMeasCount) break;
			iCnt = 0;
			fltSum = 0.0f;
			for(int k = iStart_p; k <= iEnd_p;k++)
			{
				fltSum += fltSmooth[k];
				iCnt++;
			}
			if(iCnt <= 1)
			{
				fltData[j] = fltSmooth[j];
			}
			else
			{
				fltData[j] = fltSum / (float)iCnt;
			}
		}
	}
}
반응형
반응형

데이타 스무징 기법 중에 하나인 Savitzky Golay 필터 입니다. 제가 수학 출신이 아니라서 수학적 이론은 잘 모릅니다. 

그래서 모르는 내용을 어딘가에서 주워 담아 쓸수도 있겠지만 그런 내용은 더 잘 설명해놓은 분들이 있을테니 저는 그냥 제가 사용하는 코드만 올려 놓습니다. 

 

1. Savitzky Golay 를 적용한 결과 그래프 입니다. 

  * 파란색원 원본 데이타

  * 붉은색이 Savitzky Golay 데이타 입니다.

Savitzky Golay
Savitzky Golay

2. 구현을 위한 코드는 아래와 같습니다. 

void Savitzky_Golay_smoothing(double* y_series, double* destY, int point_number)
{
//        int A[] = { -2, 3, 6, 7, 6, 3, -2 };
//        int A[] = { -21,14,39,54,59,54,39,14,-21};
	int A[] = {-36,9,44,69,84,89,84,69,44,9,-36};
//        int A[] = {-1,9,44,69,84,89,84,69,44,9,-1};

	int n = 5;
	int k = 0;

	for(k = 0; k < n; k++)
	{
		destY[k] = y_series[k] ;
	}

	for(k = n; k < point_number-n; k++)
	{
		int i = 0;
		double nominator = 0;
		double denominator = 0;

		for(i = -n; i <= n; i++)
		{
			nominator += (A[n+i]*y_series[k+i]);
			denominator += A[n+i];
		}
		double y = nominator / denominator;

		if(destY != NULL)  destY[k] = y ;
	}

	for(k = point_number-n ; k < point_number ; k++)
	{
		destY[k] = y_series[k] ;
	}
}
반응형
반응형

가우시안 피팅을 위한 함수 입니다. 

아래 그림에서 붉은색은 측정된 데이타이고 녹색은 가우시안 피팅 데이타 입니다. 

Gaussian Fitting
Gaussian Fitting 그래프

#include <algorithm>
using namespace std;

int __fastcall GaussDeviation(double *buf, double *gbuf, int len,
        double &peak, double &pkpos, double &pdiam, double &pdev, double base, double cut_level)
{
    int i, p1, p2, gbuf_len ;
    double pix=0, maxi=0 ;
    double sumix = 0, sumi = 0 ;                 //long type에서 double로 변경, sumi가 0이 되어 gaussianfit하지 않음.
    double thresh,thresh1, maxinten ;
    double radius=0, center, r2=0., oldr2=0 ;
    double *temp ;

    temp = max_element(&buf[0], &buf[len]) ;
    maxi = *temp ;

//------------ 추가 코드 --------------------
    temp = min_element(&buf[0], &buf[len]) ;
    base = *temp ;
//-------------------------------------------   

    maxinten = maxi - base ;
    thresh = maxinten * cut_level ;

    peak = 0 ;
    pkpos = 0 ;
    pdiam = 0 ;
    pdev = 0 ;

    if(len == 0) return 0 ;

    for(i=1 ; i<len-1 ; i++)
    {
        if(buf[i] < thresh) continue ;
        pix = buf[i] - thresh ;
        sumi += pix ;
        sumix += i * pix ;
    }

    for(p1=1 ; buf[p1]<=thresh && p1<len-1 ; p1++) ; // find first edge
    for(p2=len-1 ; buf[p2]<=thresh && p2>p1 ; p2--) ; // find second edge

    if(fabs(buf[p1] - thresh) > fabs(buf[p1+1] - thresh)) p1 = p1 + 1 ;
    if(fabs(buf[p2] - thresh) > fabs(buf[p2-1] - thresh)) p2 = p2 - 1 ;

    center = p1 + (p2 - p1) / 2.0 ;
    radius = (p2 - p1 + 1) / 2.0 ;

    memset(gbuf, 0, sizeof(double)*len) ;

    if(sumi < 1)                                                                  // If no points, just give center
    {
        sumix = len / 2 ;
        sumi = 1 ;
        radius = 0 ;
    }

    if(sumi >= 1 && radius > 1)
    {
        double a=0, b=0 ;
        if(cut_level > 0)
        {
            for(i=0 ; i<50 ; i++)
            {
                GaussianFit(buf,gbuf,len,base,maxinten,radius,center,&a,&b,&r2) ;
                maxinten += a ;
                radius += b ;
                if(r2 < 0.) r2=0. ;                                             // On horrible curves, r2 can go negative!
                if(r2<0.005|| (i>1 && a<0.001 && b<0.001)) break ;
                if(i>1 && fabs(oldr2-r2)/r2 < .0002) break ;                    // r2 didn't change
                oldr2 = r2 ;
            }
        }
        else
        {
            for(i=0 ; i<5 ; i++)
            {
                GaussianFit(buf,gbuf,len,base,maxinten,radius,center,&a,&b,&r2) ;
                maxinten += a ;
                radius += b ;
                if(r2<0.) r2=0. ;                                               // On horrible curves, r2 can go negative!
                if(r2<0.005 || (i>1 && a<0.001 && b<0.001)) break ;
                if(i>1 && fabs(oldr2-r2)/r2 < .0002) break ;                    // r2 didn't change
                oldr2 = r2 ;
            }
        }
    }

//    radius = (p2-p1) / 2. ;                                                     // Use the image's radius, not the gaussian's
    pdiam = p1 ;                                                                //radius*2; 0808..
    pdev = r2 ;
    peak = maxinten ;

    for(i=0 ; i<len ; i++)
    {
        if(gbuf[i] >= maxinten) pkpos = i ;
    }

    gbuf_len = p2 - p1 + 1 ;
    return gbuf_len ;
}
//---------------------------------------------------------------------------
void __fastcall GaussianFit(double *pix, double *gbuf,int npix, double mininten,
        double A, double B, double T, double *a, double *b, double *r2)
{
    /* pix is the row of pixel data
       gbuf is output of fit gaussian
       npix is the number of pixels in the row
       A is the current amplitude estimate (0 < A < 256)
       B is the current width estimate (in pixels)
       T is the centroid (assumed to be accurate/constant; 0 < T < npix)
       a is the calculated correction for A
       b is the calculated correction for B
       r2 is the r?goodness-of-fit for the revised estimators
    */
    /* note that it would be intuitive for this function to return r2 */

    /* SUMMATION VARIABLES */
    /* for each pixel : */
    double x; /* the distance of the pixel from the centroid = i-T */
    double u; /* the partial derivative of the Gaussian estimate w.r.t. A */
    double v; /* the partial derivative of the Gaussian estimate w.r.t. B */
    double f; /* the current Gaussian estimate */
    double F; /* the deviation of the pixel value from the estimate */
    /* overall: */
    double C=0; /* the sum of u?*/
    double D=0; /* the sum of uv */
    double E=0; /* the sum of Fu */
    double G=0; /* the sum of v?*/
    double H=0; /* the sum of Fv */
    double J=0; /* the sum of F?*/
    /* additional variables from Coherents formulas (without names) */
    double sumf=0; /* the sum of revised estimates */
    double sumF=0; /* the sum of errors in the revised estimates */
    double avgf; /* the average new estimate */
    double var; /* the variance between a pixel value and the average */
    double sumvar2=0; /* the sum of the squared variances */

    int i ;
    for(i=0 ; i<npix ; i++)                                                     /* this is the slowest part of this function */
    {
        x = (double)(i-(int)T) ;
        f = A * exp(-2.*((x*x)/(B*B))) ;                                        /* (1) */
        F = (double)(pix[i]-mininten)-f ;                                       /* (5) */
        if(A==0) A = 1 ;
        u = f / A ;                                                             /* (3) */
        if(B==0) B = 1 ;
        v = 4.* f *(x*x)/(B*B*B) ;                                              /* (4) */
        C += u*u ;
        D += u*v ;
        E += F*u ;
        G += v*v ;
        H += F*v ;
        J += F*F ;
    }
    /* new coefficient estimates */

    double temp ;
    temp = (D*D-C*G) ;
    if(temp==0) temp = 1 ;

    *b = (E*D-C*H)/ temp ;        /* (8) */
    *a = -(E*G-D*H)/temp ;       /* (9) */
    A += *a;
    B += *b;

    /* analyze estimates using new coefficients */
    J = 0 ;
    for(i=0 ; i<npix ; i++)
    {
        x = (double)(i-(int)T) ;
        if(B==0) B = 1 ;
        f = A*exp(-2.*((x*x)/(B*B))) ;
        F = (double)(pix[i]-mininten) - f ;
        gbuf[i] = mininten + f ;                                                /* PLOT IDEAL GAUSSIAN */
        sumf += f ;
        sumF += F ;
        J += F*F ;
    }
    if(npix == 0) npix = 1 ;
    avgf = sumf / npix ;

    for(i=0 ; i<npix ; i++)
    {
        var = (double)(pix[i]-mininten) - avgf ;
        sumvar2 += var * var ;
    }
    if(sumvar2==0) sumvar2 = 1 ;
    *r2 = 1. - sqrt(J / sumvar2) ;
}
반응형
반응형

생물의 진화과정 인 자연선택(natural selection)과 유전법칙을 모방한 확률적 탐색기법을 이용하여 최적의 길찾기를 C# 으로 구현해 보았습니다. 

 

1. 소스 프로그램 :

Genetic_Algorithm.zip
0.28MB

2. 알고리즘 순서는 아래와 같습니다. 

 

 (1) 노드 생성 ( 2차원 좌표상의 특정위치 )

 (2) 각각의 노드들을 랜덤하게 연결하여 하나의 유전자를 생성 

 (3) 여러개의 유전자(프로그램에서는 10개)를 모아서 하나의 유전자 그룹을 생성 

 (4) 유전자 그룹의 각각의 유전자마다 Fitness 값을 계산한 후 최적의 Fitness 값을 가진 유전자를 선정

 (5) 유전자 그룹의  각 유전자 Fitness 값을 이용해서 각각의 유전자의 가중치를 적용하여 룰렛휠을 생성 

 (6) 룰렛휠의 돌려서 2개의 유전자를 선택하여 Crossover하여 하나의 새로운 유전자 생성 

 (7) 새로 생성된 유전자에 돌연변이 생성 ( 1% 확율 )

 (8) ( 6,7)번을 10회 반복하여 새로운 유전자 그룹을 만들고 기존 부모 유전자 그룹은 파기 

 (9) 유전자 그룹의 각각의 유전자마다 Fitness 값을 계산한 후 최적의 Fitness 값을 가진 유전자를 선정

 (10) 기존보다 최적의 유전자가 없을 경우 최악의 유전자를 기존 최적의 유전자와 교체 

 (11) 5번부터 다시 반복 

 

3. 실행 결과 프로그램 

 (1) 빨간색 점이 시작 위치이고 모든 노드들을 경유하여 다시 제자리로 돌아오게 되는 경로를 찾게 됩니다. 

 (2) reset : 노드 재 생성 

 (3) Operate : 수동 세대 전환 

 (4) start : 자동 연속 세대 전환

 (5) stop : 자동 전환 멈춤

반응형

'Mathematics Algorithm' 카테고리의 다른 글

Data Smoothing #1 알고리즘 C++  (0) 2023.03.30
Data Smoothing #2 (Savitzky Golay) C++  (0) 2023.03.30
Gaussian Fitting 함수 C++  (0) 2023.03.23
Least Square(최소 자승법)  (2) 2023.02.26
Data Smoothing #3 ( Exponential ) C++  (0) 2023.02.06
반응형

실험적 데이타를 추출했을 경우 데이타의 경향성을 확인하기 위해 수학적으로 사용하는 직선근사를 C# 코드로 구현한 것입니다.

아래 코드는 추출된 X,Y 좌표 상의 데이타들을 Y축의 상한값과 하한값을 범위로 하여 일차 방정식으로 식을 추출하는 코드 입니다.  

Least Square

public static (double slope, double yIntercept) GetLinearLeastSquaresFit(double[] dataX, double[] dataY, double lowYValue, double highYValue)
{
    double slope=0, yIntercept=0;
    int index = 0 ;
    double count = 0 ;
    double xSummary = 0;
    double ySummary = 0;
    double xxSummary = 0; 
    double xySummary = 0;

    foreach(var Yvalue in dataY)
    {
        if(Yvalue > highYValue) break ;
        if(Yvalue > lowYValue)
        {
            xSummary += dataX[index]; 
            ySummary += Yvalue;
            xxSummary += dataX[index] * dataX[index];
            xySummary += dataX[index] * Yvalue;

            count += 1 ;
        }

        index ++ ;
    }

    if(count > 2)  // 최소한 2개 이상의 점이 있어야 계산이 된다. 
    {
        try
        {
            slope = (xySummary * count - xSummary * ySummary) / (xxSummary * count - xSummary * xSummary); 
            yIntercept = (xySummary * xSummary - ySummary * xxSummary) / (xSummary * xSummary - count * xxSummary); 
        }
        catch
        {
            slope = yIntercept = 0;
        }
    }

    return (slope, yIntercept);  // 기울기와 y 절편값을 리턴합니다. 
}
반응형

+ Recent posts