※요약

C언어나 C++ 등에서 소수점 특정 자릿수 반올림하는 방법입니다. 매크로 함수와 일반 함수 2가지로 구현되어 있으며, 다른 곳에서 사용할 땐 원하는 함수만 복사해서 쓰면 됩니다.


※예제

#include <stdio.h>
#include <cmath>

#define ROUNDING(x, dig)	( floor((x) * pow(float(10), dig) + 0.5f) / pow(float(10), dig) )

double Rounding( double x, int digit )
{
	return ( floor( (x) * pow( float(10), digit ) + 0.5f ) / pow( float(10), digit ) );
}

int main( )
{
	printf( "%g\n", ROUNDING( 9.3453456, 3 ) );
	printf( "%g\n", Rounding( 9.3453456, 3 ) );
	printf( "%g\n", ROUNDING( 9.3453456, 5 ) );
	printf( "%g\n", Rounding( 9.3453456, 5 ) );

	return 0;
}



※결과





※요약

어떤 수가 있을 때, 이 수가 2의 n승인지 판별하는 방법입니다.



※특징

비트 연산을 이용해 속도가 빠름



※함수 설명

2의 3승인 8을 2진수로 표현하면 아래와 같습니다.(편의상 8비트로 표현)

0000 1000

여기서 1을 빼주면

0000 0111이 되고, 0000 1000와 & 연산을 하게 되면 0이 나옵니다.


0000 1000

0000 0111

-------------&연산 (& 연산자는 두 개의 비트가 모두 1일 때 1이 됨, 고로 여기서는 0이 됨)

0000 0000


비트 연산 보기





※예제

#include <stdio.h>

bool CheckPowerOfTwo( int nNum );

int main( )
{
	int x = 0;
	bool bState = false;

	printf( "-1 입력시 종료 됩니다.\n" );

	while( x != -1 )
	{
		printf( "숫자를 입력하세요 : " );
		scanf( "%d", &x );

		bState = CheckPowerOfTwo( x );

		printf( "%d은(는) 2의 n승이 %s\n\n", x, (bState ? "맞습니다." : "아닙니다.") );
	}

	return 0;
}

bool CheckPowerOfTwo( int nNum )
{
	int nResult = nNum & (nNum-1);

	if( nResult == 0 )
	{
		return true;
	}
	else
	{
		return false;
	}
}



※요약

C언어에서 재귀 호출을 이용한 이진 탐색이다. 이진 탐색이란 이름 그대로 탐색할 데이터를 반으로 나눠 나머지 반만 탐색하는 방식을 반복하는 알고리즘이며 빠른 속도로 원하는 값을 찾을 수 있다. 이진 검색, 이분 검색 등으로도 불린다.


특징

 - 각 요소의 값들은 정렬되어 있어야 한다.

 - 각 요소의 값들은 모두 달라야 한다.


※예제

#include <stdio.h>

int RecusiveBinSearch( int nArr[], int nBegin, int nEnd, int nTarget )
{
	int nMid = 0;

	if( nBegin > nEnd )
	{
		return -1;	//탈출 조건 및 탐색 실패
	}

	nMid = (nBegin+nEnd) / 2;

	if( nArr[nMid] == nTarget )
	{
		return nMid;			//타겟을 찾았다.
	}
	else if( nTarget < nArr[nMid] )
	{
		return RecusiveBinSearch( nArr, nBegin, nMid-1, nTarget );
	}
	else
	{
		return RecusiveBinSearch( nArr, nMid+1, nEnd, nTarget );
	}
}

int main( )
{
	int nArr[] = { 1, 3, 5, 7, 9, 11, 13 };
	int nResult;

	nResult = RecusiveBinSearch( nArr, 0, sizeof(nArr)/sizeof(int)-1, 7 );
	
	if(nResult == -1)
	{
		printf( "값이 없습니다.\n" );
	}
	else
	{
		for( int i=0 ; i<sizeof(nArr)/sizeof(int) ; ++i )
		{
			printf( "%d ", nArr[i] );
		}
		printf( "\n%d번째 배열 요소에 있습니다.\n", nResult );
	}

	return 0;
}



결과



※요약

C언어에서 재귀 호출을 이용한 거듭제곱 구하는 함수다.

어떤 수 a에 대하여 n개 곱한 것을 a^n( a)이라 표시하고 a의 n제곱이라 하며, n을 거듭제곱의 지수라고 한다.

a = 3, n = 5일때 3^5 = 243 = 3 * 3 * 3 * 3 * 3 다.



※예제

#include <stdio.h>

double power( double a, int num );

int main( )
{	
	printf( "%.10lf", power( 3.141592653589793238462643383279, 3 ) );

	return 0;
}

double power( double a, int num )
{
    if( num == 0 )
        return 1;   
 
    return a * power( a, num-1 );
}


결과




요약

C언어에서 재귀 호출을 이용한 팩토리얼 구하는 함수이다.

정수 n의 팩토리얼은 n! 이라고 표시하며, n! = n * (n-1) * (n-2) * (n-3) * . . * 2 * 1 이며

5! 일 경우 5! = 120 = 5 * 4 * 3 * 2 * 1 다.



예제

#include <stdio.h>

unsigned __int64 factorial( unsigned __int64 num );

int main( )
{
	int nNum = 5;
	printf( "%d! : %I64u", nNum, factorial(nNum) );

	return 0;
}

unsigned __int64 factorial( unsigned __int64 num )
{
	if( num == 1 )
	{
		return 1;
	}

	return num * factorial( num-1 );
}


※결과



※요약

재귀 함수란 자기 자신을 호출하는 함수


일반적인 함수 호출 방법은 다음과 같다. 프로그램 실행 중 함수를 만나면 현재 위치를 저장 후, 호출할 함수의 주소로 점프해 함수 내용을 수행한다. 함수 실행이 끝나면 기억해뒀던 원래 위치로 복귀해 다음 코드를 수행한다. (일반적으로 잦은 함수 호출로 인한 점프의 반복은 속도를 느리게 한다.)


일반 함수나 재귀 함수나 위와 같은 방법으로 함수가 호출이 되는데 재귀 함수의 경우 함수의 실행이 채 끝나기도 전에 자기 자신을 호출한다. 이게 가능한 이유는 호출된 각 함수에 대한 복귀 번지, 인수, 지역 변수 등이 스택 프레임에 저장되기 때문에 기존의 작업 내용들이 서로 방해하지 않고 잘 동작할 수 있는 것이다.


※재귀 함수 특징

 - 무한 루프에 빠지지 않기 위해 일정한 탈출 조건이 있어야 한다.

 - 코드를 단순화 할 수 있다.

 - 재귀 함수는 호출 시 마다 스택 공간을 이용하므로 무리하게 호출하면 스택 오버플로우가 일어날 수 있다.

 - 재귀 함수의 호출 횟수는 스택의 남은 공간과 재귀 함수의 지역 변수 사이즈에 따라 달라진다.

 - 디버깅 및 실행 흐름을 파악하기 힘들다.



※예제

아래 예제는 기초적인 재귀 함수 예제다.

다음 게시물부터는 재귀를 이용한 팩토리얼(factorial), 거듭 제곱(power, square), 이진 탐색(binary search) 등을 알아보겠다.

#include <stdio.h>

void Recursive( int nCount )
{
	if( nCount <= 0 )
	{
		printf( "발사\n" );
		return ;
	}
	else
	{
		printf( "카운트 다운 : %d\n", nCount );
		Recursive( nCount-1 );        //인자로 받은 nCount에 -1을 줄여서 호출한다.
	}
}

int main( )
{
	Recursive( 5 );

	return 0;
}


결과




#include <ctime>
#include <vector>
#include <iostream>
#include <atlstr.h>

using namespace std;

struct tagRand
{
	int nCount;
	CString strRand;
};

int main( )
{
	srand( (unsigned)time(NULL) );
	
	int nInput(1);
	vector<tagRand> vecNum;

	//데이터 입력
	for( int i=0 ; i<10 ; ++i )
	{
		tagRand oTemp;

		oTemp.nCount = i+1;
		oTemp.strRand.Format( TEXT("%d"), rand( )%8 );

		vecNum.push_back( oTemp );
	}

	printf( "0을 입력하면 종료됩니다.\n\n" );

	while( nInput != 0 )
	{	
		//입력된 데이터 출력
		for( int i=0 ; i<vecNum.size( ) ; ++i )
		{
			printf( "%d, %s\n", vecNum[i].nCount, vecNum[i].strRand );
		}

		printf( "삭제할 수를 입력하세요 : " );
		scanf( "%d", &nInput );
		printf( "\n" );
		
		//선택한 항목 삭제
		//vector<tagRand>::iterator iter = vecNum.begin( );
		auto iter = vecNum.begin( );
		while( iter != vecNum.end( ) )
		{
			if( iter->nCount == nInput )
			{ 
				iter = vecNum.erase( iter );				
			} 
			else 
			{ 
				++iter; 
			} 
		}
		system( "cls" );
	}

	return 0;
}


OpenGL로 구현한 프로그램에 화면 캡쳐 기능이 필요해서 오동님 블로그를 참조해서 사용하다가 리눅스(Qt)에서도 사용하게 될 일이 있어서 수정한 거 올립니다. 확인 결과 MFC, Win32 API 에서 문제 없이 잘 동작하며 리눅스에의 확인은 제 친구가 했는 데 리눅스에서도 문제 없이 잘 동작한다고 하네요.(비트맵 관련 구조체가 없어서 구조체는 직접 추가했다고 함)


void ScreenCapture( const char *strFilePath )
{
	//비트맵 파일 처리를 위한 헤더 구조체
	BITMAPFILEHEADER	BMFH;
	BITMAPINFOHEADER	BMIH;

	int nWidth = 0;
	int nHeight = 0;
	unsigned long dwQuadrupleWidth = 0;		//LJH 추가, 가로 사이즈가 4의 배수가 아니라면 4의 배수로 만들어서 저장

	GLbyte *pPixelData = NULL;				//front buffer의 픽셀 값들을 얻어 오기 위한 버퍼의 포인터

#ifdef WIN32
	//윈도우의 클라이언트 영역 좌표
	RECT ImageRect;
	GetClientRect( *m_hWndCopy, &ImageRect );			

	//이미지 영역 좌표를 이용하여 실제 이미지의 사이즈를 계산
	nWidth  = ImageRect.right - ImageRect.left;		//윈도우 버전의 경우 사이즈 변경이 되므로 그때그때 조사
	nHeight = ImageRect.bottom - ImageRect.top;

#else
	nWidth  = 1024;		//(나의 경우)리눅스에서의 경우 해상도 고정이므로 그 값을 입력
	nHeight = 768;

#endif

	//4의 배수인지 아닌지 확인해서 4의 배수가 아니라면 4의 배수로 맞춰준다.
	dwQuadrupleWidth = ( nWidth % 4 ) ? ( ( nWidth ) + ( 4 - ( nWidth % 4 ) ) ) : ( nWidth );

	//비트맵 파일 헤더 처리
	BMFH.bfType  = 0x4D42;		//B(42)와 M(4D)에 해당하는 ASCII 값을 넣어준다.
	//바이트 단위로 전체파일 크기
	BMFH.bfSize  = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + ( dwQuadrupleWidth * 3 * nHeight );
	//영상 데이터 위치까지의 거리
	BMFH.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER );

	//비트맵 인포 헤더 처리
	BMIH.biSize				= sizeof( BITMAPINFOHEADER );		//이 구조체의 크기
	BMIH.biWidth			= nWidth;							//픽셀 단위로 영상의 폭
	BMIH.biHeight			= nHeight;							//영상의 높이
	BMIH.biPlanes			= 1;								//비트 플레인 수(항상 1)
	BMIH.biBitCount			= 24;								//픽셀당 비트수(컬러, 흑백 구별)
	BMIH.biCompression		= BI_RGB;							//압축 유무
	BMIH.biSizeImage		= dwQuadrupleWidth * 3 * nHeight;	//영상의 크기
	BMIH.biXPelsPerMeter	= 0;								//가로 해상도
	BMIH.biYPelsPerMeter	= 0;								//세로 해상도
	BMIH.biClrUsed			= 0;								//실제 사용 색상수
	BMIH.biClrImportant		= 0;								//중요한 색상 인덱스

	pPixelData = new GLbyte[ dwQuadrupleWidth * 3 * nHeight ];	//LJH 수정

    //프런트 버퍼로 부터 픽셀 정보들을 얻어온다.
	glReadPixels(
		0, 0,					//캡처할 영역의 좌측상단 좌표
		nWidth, nHeight,		//캡처할 영역의 크기
		GL_BGR,					//캡처한 이미지의 픽셀 포맷
		GL_UNSIGNED_BYTE,		//캡처한 이미지의 데이터 포맷
		pPixelData				//캡처한 이미지의 정보를 담아둘 버퍼 포인터
		);

	{//저장 부분
		FILE *outFile = fopen( strFilePath, "wb" );
		if( outFile == NULL )
		{
			//에러 처리
			//printf( "에러" );
			//fclose( outFile );
		}

		fwrite( &BMFH, sizeof( char ), sizeof(BITMAPFILEHEADER), outFile );			//파일 헤더 쓰기
		fwrite( &BMIH, sizeof( char ), sizeof(BITMAPINFOHEADER), outFile );			//인포 헤더 쓰기
		fwrite( pPixelData, sizeof( unsigned char ), BMIH.biSizeImage, outFile );	//glReadPixels로 읽은 데이터 쓰기

		fclose( outFile );	//파일 닫기
	}

	if ( pPixelData != NULL )
	{
		delete pPixelData;
	}
}


아래는 위의 함수로 저장한 bmp파일들입니다.

C언어나 C++에서 사용할 수 있는 함수입니다. 두 지점의 위경도 값을 정해서 함수에 대입하면 Meter로 값을 반환합니다. km로 값을 받고 싶으면 dDistance*=1000; 부분 주석치면 됩니다.

double GetDistance( tagPT pt1, tagPT pt2 )
{
	int radius = 6371;

	double dLat = rad( (pt2.y-pt1.y) );
	double dLon = rad( (pt2.x-pt1.x) );
	
	pt1.y = rad( pt1.y );
	pt2.y = rad( pt2.y );

	double a = sin(dLat⁄2) * sin(dLat⁄2) + sin(dLon⁄2) * sin(dLon⁄2) * cos(pt1.y) * cos(pt2.y);
	double c = 2 * atan2f(sqrtf(a), sqrtf(1-a));
	double dDistance = radius * c;

	dDistance*=1000;

	return dDistance;
}



아래는 예제 입니다.

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

#define rad(x) x*3.14159⁄180.0

struct tagPT
{
	double x;
	double y;
	double z;
};

double GetDistance( tagPT pt1, tagPT pt2 );

int main()
{
	tagPT pt1;
	tagPT pt2;
	
	//한밭수목원 거리
	pt1.y = 36.364808;
	pt1.x = 127.379525;

	pt2.y = 36.364772;
	pt2.x = 127.390275;

	double dDis = GetDistance( pt1, pt2 );

	return 0;
}

double GetDistance( tagPT pt1, tagPT pt2 )
{
	int radius = 6371;

	double dLat = rad( (pt2.y-pt1.y) );
	double dLon = rad( (pt2.x-pt1.x) );
	
	pt1.y = rad( pt1.y );
	pt2.y = rad( pt2.y );

	double a = sin(dLat⁄2) * sin(dLat⁄2) + sin(dLon⁄2) * sin(dLon⁄2) * cos(pt1.y) * cos(pt2.y);
	double c = 2 * atan2f(sqrtf(a), sqrtf(1-a));
	double dDistance = radius * c;

	dDistance*=1000;

	return dDistance;
}


마방진이란


정보처리기사 배열 마방진(Magic Square) 만들기 입니다.


#include <stdio.h>

//마방진의 크기를 결정
//홀수로 입력하되, 19미만이 보기 좋음
//7이상부터 19까지는 대각선 "우상좌하" 의 합이 다른 합과 다름
#define MAX 19

int main()
{
	//좌표 0, 2부터 시작 
	int i(0), j(2), k(1);
	int arr[MAX][MAX];

	for(k=1 ; k<=(MAX*MAX) ; k++)
	{
		//처음 0, 2에 1을 대입 
		arr[i][j] = k;
		
		//k가 배열 크기의 배수이면 규칙에 따라 행만 증가
		if(k%MAX == 0)
			++i;

		//k가 배열 크기의 배수가 아니면 규칙에 따라 "행감소, 열증가"
		if(k%MAX != 0)
		{			

			//마방진 규칙 "행감소, 열증가"
			--i;
			j++;

			//행이 감소하다가 0보다 작아지면 배열 크기의 가장 큰 값으로 교체
			if(i < 0)	i = MAX-1;

			//열이 증가하다가 배열 크기보다 커지면 0으로 교체
			if( j > (MAX-1) )	j = 0;
		}
	}

	//출력
	for(i=0 ; i<MAX ; i++)
	{
		for(j=0 ; j<MAX ; j++)
		{
			printf("%4d", arr[i][j]);
		}
		printf("\n");
	}

	printf("\n");

	return 0;
}


+ Recent posts