반응형

이진트리(바이너리트리)를 이용하여 검색하는 방법 O(logN) 의 성능 : 성능이 괜챃다

이진검색트리는 이진트리(바이너리트리)를 이용한 검색 방법

조건 Heap 은 부모노드가 두개의 자식노드보다 커야 하지만

1.이진검색트리는 부모노드는 왼쪽 sub-tree 의 모든 노드보다 크고

2.부모노드는 오른쪽 sub-tree 의 모든 노드보다 작아야 한다

재귀적으로 모든 조건을 만족해야 한다

그랫 In-Order Traversal 하면 정렬된 순서가 된다 (왼쪽 부모, 오른쪽 노드순으로 방문)

트리는 부모가 양 자식노드의 포인터를 가지고 있는 상태

검색

1.. 루트부터 검색 시작

2. 각 노드와 찾을 값을 비교해가면서 노드에서 좌측은 작고 우측은 큰것이기 때문에 키값이 같을때까지 비교를 반복해간다

=> 조건에 따라 자식 포인터를 타고 계속 내려간다 키를 찾을때까지

마지막 까리 노드까지 내려가서 찾지 못하면 fail

삽입

루트부터 비교를 하면서 새로운 노드는 항상 leaf 에 배치한다

(이때 트리 구조상 부모가 자식을 가르키고 있어야 함으로

포인터를 두개를 두어 이전 포인터 값을 유지하고 있어야 한다

하나는 현재 노드 하나는 이전노드를 가르키는 포인터)

또한 마지막 노드의 왼쪽 오른쪽에 들어가야 하므로

부모노드를 저장하고 있는 포인터가 P 였고 P 의 오른쪽에 노드가 추가 되어야 한다면

P 의 Right 에 노드 하나가 추가 되면서 추가된 노드 또한 아무것도 가르키고 있지 않은 포인터 두개(자식2개) 를 갖는 노드가 되어

추가 되어야 한다

삭제

삭제를 하고도 이진트리의 조건에 맞아야 한다

마지막 노드는 그냥 삭제 하면 되지만 중간에 잇는 노드를 삭제할때는

자신보다 작은 노드중 최대노드 또는

자신보다 큰 노드중 최소 노드를 선택

선택 방법은 부모 노드에서 왼쪽은 모두 부모모다 작은 것임으로

자신보다 작은 노드중 최대노드는 부모에서 왼쪽으로 한번갔다가 오른쪽으로 쭉 가는 순서

=>부모의 왼쪽 트리중에서(부모제외한) 중위순회를 해서 제일 마지막에 나오는 노드가 작은것중에 큰것

자신보다 큰 노드중 최소 노드는 부모를 제외한 오른쪽 노드트리에서 중위순회를 하여 가장 첫번째 나오는 것이

부모를 제외한 오른쪽 노드트리에서 가장 작은 값이다

...자식의 자식에 대한 처리 이하 생략..


이진 트리는 좌우로 균형이 맞을때 성능이 가장 좋다

입력 순서가 중요하다,

만약 정렬된 순서(순차 or 역순)로 입력이 될 경우 링크드리스트 처럼 입력이 될 수 있다 => 최악의 경우로 가게 된다

find, insert 최악의 경우 O(N), 균형이 맞으면 O(logN)

remove O(logN) => 삭제할때 insert 시 O(N)의 성능을 가져도 노드끼리의 포인터 연결이 간단해 짐으로 결론적으로 O(logN) 의 성능을 가짐

insert, find, remove 평균적으로는 O(logN) 의 성능을 갖는다

결국 트리의 균형을 맞추는 것이 중요하다 그래서 Balanced Tree 가 많이 연구됨

그래서 이진트리의 삽입을 개선한다


이진트리를 중위순회 하면 정렬된 순서가 나온다

자료가 균형에 맞게 대강 들어가는데 O(NlogN)의 성능을 갖는다

하나가 insert 되는데 logN 이 걸림으로 N 개의 자료에 대해 걸쳐 수행하는 것이니 N*logN

그런데 정렬된 순서나 역순이면 최악의 성능 O(N^2) 의 성능을 가진다


[이진트리 균형 맞추기]

정렬된 데이터를 배열로 가지고 있고 이것을 이진트리에 저장하려고 할때

트리를 중위순회 방식으로 순회하면서 트리를 생성할때 중위순회시 데이터를 노드에 저장하면 한쪽으로 치우친 형태가 아닌

균형이 맞는 balance 형태의 트리로구성할 수 있다

- 성능이 않좋을때 balance 로 균형을 한번 맞춰준다


중복키 문제

이진트리는 중복키에 대해서 제대로 처리 하지는 못하는데 이걸 해결 하기 위해 링크드 리스트와 바이너리 리스트를 결합해 해결한다

중복되는 키를 가지고 있는 노드에 대해서 링크드 리스트를 두어 링크드 리스트 뒤에 넣어주는 식으로 해놓고

키에 대해서 next 를 호출하면 중복된 키들의 첫번째에서 두번째로 이동하여 해당 데이터를 리턴


이진트리 특징

all nodes in left subtree < parent < all nodes in right subtree

성능이 트리의 균형에 좌우된다

반응형
반응형


n >= 1

F_n = F_{n-1} + F_{n-2}

F_1 = F_2 = 1

1 1 2 3 5 ....

F_1 F_2 F_3 F_n

n 은 피보나치 수열을 만드는 번째..

int fibonacci(int n){

if( n == 1 || n == 2)

return 1;

return fibonacci( n - 1 ) + fibonacci( n - 2 ) ; // 후위 순회와 유사하다 값을 나중에 리턴 함으로..

}








피보나치수열 참고


참고 : http://blog.naver.com/kwanskim?Redirect=Log&logNo=20110843012




자연계의 피보나치 수열  게시판 

2010/08/02 05:30

복사http://blog.naver.com/kwanskim/20110843012

전용뷰어

피보나치 수열 (Fibonacci Numbers)

 

1. 피보나치 수열

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, …

처음의 두 숫자는 0 1이고그 다음부터는 이전 두 항의 합으로 이루어진다첫번째 항 0을 제외하는 경우도 있다.

 

2. 피보나치와 피보나치 문제

피보나치 ( 1170-1250)는 이탈리아의 수학자로서 <Liber Abaci>라는 책을 통해 힌두-아라비아 숫자 체계를 유럽에 전파한 것으로도 잘 알려져 있는 중세 유럽의 최고 지성이다또한 그는 이 책에서 다음과 같은 문제를 제시하였다.

 

l        1 1일에 태어난 암수 한 쌍의 토끼가 있다.

l        모든 달의 길이는 동일하다고 가정한다.

l        한 쌍의 토끼는 태어난지 두 달이 지나면 매달 두 마리의 새끼(암수)를 낳는다즉 첫번째 쌍은 3 1일에 처음으로 두 마리의 새끼를 낳는다.

l        그들에게서 태어난 새끼들도 두 달이 지나면 짝을 이루어 매달 두 마리의 새끼를 낳는다.

l        어떠한 토끼도 중간에 죽지 않는다.

l        일 년 후에 전체 토끼는 모두 몇 쌍일까?

 

피보나치는 근친교배환경먹이다산불임 등 일어날 수 있는 여러 상황은 모두 제거하고 문제를 단순화 시킴으로써 상황 속에 숨은 원리를 파악하고자 하였다.

 

그 결과 피보나치는 매달 관찰되는 토끼의 쌍이 특정한 규칙을 따르고 있음을 발견하고 12월 말에 144쌍의 토끼를 예상하였다.

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144

 

3. 자연계에서 발견되는 피보나치 수열

자연계에서 이러한 수열이 제법 흔하게 발견된다는 사실은 잘 알려져 있다여러 꽃잎의 갯수를 조사해보면 피보나치 수열이 자주 나타난다.

 

가상적으로 표현된 아래의 그림을 보면 식물 줄기의 가지와 잎의 갯수에서도 피보나치 수열이 나타난다.

 

아래 그림의 솔방울은 한쪽 방향으로 8개의 나선이 나타나고 다른 방향으로는 13개의 나선이 나타난다모두 피보나치 수열에서 나타나는 숫자들이다.

 

아래 그림의 파인애플은 한쪽 방향으로 13개의 나선이 나타나고 다른 방향으로는 21개의 나선이 나타난다.

 

아래 그림에서 해바라기 씨앗들이 이루는 나선 무늬는 양쪽 방향으로 몇 줄씩인지 세어보자.

 

그 외에도 과일 씨앗의 갯수도 피보나치 수열을 따르는 경우가 많다.

 

4. 식물계에 피보나치 수열이 흔한 이유

학자들에 따라 진화적인 측면에서 피보나치 수열의 효율성을 말하는 분들도 있지만식물성장의 역학적 측면에서 고찰한 의견도 있다.

우리는 이 이야기를 토끼의 번식에 관한 이야기로 시작했다토끼의 번식을 다시 생각해 본다면 수열의 출현은 자연스럽다즉 한쌍의 토끼만을 고려해보자그들은 평생을 살면서 매달 두 마리씩의 새끼를 낳지만 생후 2달이 되어서야 성장을 완료하기 때문에 둘째 달부터 출산이 가능하다는 것이다생물학적 진위를 논의함이 아니다수학적인 원리를 논의하는 것이다.

그렇다면 동일한 원리를 식물의 성장에 적용시키지 못할 이유가 없다식물의 줄기를 생각해보자이들이 두 갈래로 갈라지기 위해서는 (아니 좀더 정확하게 표현한다면 본 줄기에서 새끼 가지를 치는 것이 옳다고 생각하자.) 줄기의 굵기가 어느 정도 이상이어야 된다그리고 필요한 굵기 이상이 된 이후에는 정기적으로 가지치기를 할 수 있다. 따라서 새로 돋아난 새끼 가지도 어느 정도의 시간이 지난 후에는 정기적으로 가지를 칠 수 있다는 얘기이다그 원리가 토끼의 번식과 완전히 동일하다.

꽃잎의 모태가 되는 입자 (이런 표현을 써도 된다면)도 어느 정도 성장하고 나서 작은 새끼 입자 하나를 낳고(?) 그것이 다시 새로운 입자를 낳기 위해서는 특정한 시간이 필요하다고 가정한다면 꽃잎이나 씨앗의 갯수에서도 피보나치 수열이 나타나는 것에 대한 궁금증에 대한 어느 정도의 설명이 될 수 있을 것 같다.

 

5. 마치며

우리가 자연계에 대해서 아는 것이 너무 보잘것 없다는 사실에 전적으로 동의한다위에 열거한 많은 사례가 모두 동일한 원리로 확실하게 설명된다고 말하기는 어려울지도 모른다특히 솔방울이나 파인애플그리고 해바라기 씨앗에서 관찰되는 나선의 갯수에 대해서는 이들 씨앗들의 초기 분열과정에서 일어나는 (위와 비슷한밀고 당기는 동적 역학과정이 관여하지 않을까 정도의 추측 정도 만이 내게는 있을 뿐이다그러나 그러한 수열이 관찰되는 이상 그것을 무조건 우연의 일치로 생각할 수는 없다이러한 것들과 여기에 미처 언급하지도 못한 많은 사실에 대한 엄청난 비밀과 원리가 이 글을 읽으시는 여러분에 의해 밝혀지기를 바라는 마음 간절하다혹시 이 모든 것이 누군가에 의해 이미 명백하게 밝혀져 거의 모든 사람들이 아는 상식이 되어버린지 오래인데 나만 모르고 있었다면 나의 무지에 대한 용서를 구하며

 

피아노 건반의 한 옥타브 사이에는 5개의 까만 건반, 8개의 하얀 건반모두 13개의 건반이 있다우연인가어린 시절 아카시아 같은 나뭇잎을 하나씩 떼어내며 여러 가지 운을 물었던 기억이 있다서양에도 비슷하게 꽃잎을 떼어내며 운을 묻는 경우가 있나보다피보나치 수열을 알고 있다면 사랑을 얻을 수도 있지 않을까?

He loves me. He loves me not. He loves me.…

 

(위 사진들과 그림들은 인터넷의 여러 곳에서 찾아 편집한 것입니다)

 

2010 8 1 (큰 아들의 스물 세번째 생일을 축하하며…)

빗줄기././

반응형
반응형

1. 문제의 크기는 점점 작아져야 한다.

2. 재귀호출이 끝나는 조건이 있어야 한다

Factorial 구하기

문제의 크기 감소 : N! = N*(N-1)!

종료조건 0! = 1

int factorial( int n )

{

if( n == 0 ) // 만약 n =0 을초기 값으로 입력 했으면 1 이 나오야 함으로....

return 1;

else

return n * factorial( n-1);

}

반응형
반응형

Terminal node : 끝인 노드

Non-Terminal node : 끝이 아닌 노드

괄호의 식을 기준으로 operator 노드 양쪽에 operand 를 배치해 놓으면

위의 그림처럼 수식노드가 만들어지는데 이때의 이점은

순회 방법에 따라 순회 방식에 따라 전위,중위,후위의 수식구성을 만들 수 있다는 것

만약 위의 수식이 후위수식으로 표기 되어 있었다면

A B + CD - * E / F G * +

다음을 통해 Parse Tree 를 구성할 수 있다

1. Operand 는 Node를 만들어 Stack 에 Push

2. Operator 느 Node를 생서하여

1. Stack 에서 Pop 한 노드를 오른쪽 자식으로 하고

2. stack 에서 또 Pop 한 노드를 왼쪽자식으로 하고

3. Operator Node 를 Stack 에 Push

3. Stack 에 마지막으로 남은 노드가 Root 이다.

손으로 계산시

전위,중위,후위 에 대한 수식을 뽑아올때 Parse Tree 로 수식을 괄호 우선 순위에 맞게 트리로 구성 한후

순회 방식에 따라 수식을 구성하면 되다

반응형
반응형

 재귀적인 방법은 속도가 떨어짐으로 iteration 한 방법으로 다시 구성한다

PreOrder_UsedStack( Node* pNode ) // pNode 이중 리스트형태로 트리를 구상하고 있는 노드

{

ListStack<Node*> stack;

stack.push( pNode );

while( !stack.IsEmpty() ){

pNode = stack.Pop();

if( pNode != m_pNodeTail )

{

visit(pNode);

stack.Push( pNode->pRight );

stack.Push( pNode->pLeft );

}

}

}

스택에 쌓아 가면서 순회한다, 재귀로 구성되지 않는다

트리로 구서되어 있는 pNode 는 헤드 노드와 꼬리 노드를 가상노들 가지고 있는 이중연결 리스트 트리이다

참고 : http://www.cyworld.com/sjh0628/6852486

but 다른 순회 들을 스택으로 바꿀려면 이보다는 쉽지 않다

반응형
반응형

[트리순회는 재귀호출로 이해될 수 있다]

트리순회(Tree Traversal)

1. Pre-order Traversal

2. In-order Traversal

3. Post-order Traversal

4. Level-order Traversal

의 순회 방법들이 있는데 이러한 순회 방법이 있는 이유는

트리는 선형이 아닌 비선혀이기 때문에 모드 노드들을 거쳐가기 위한 방법이 필요하게 된다

그리하여 위와 같은 순회 방법이 나오게 되었다

( 반례 : list 의 경우 선형이기 때문에 특별한 노드를 거쳐가는 방법을 필요로 하진 않는다)

sub 트리 : 트리의 부분 집합 참고 : http://www.cyworld.com/sjh0628/6850615

서브트리는 자식을 하나라도 가지고 있는 노드가 있다면 그 노드를 중심으로 서브트라 볼 수 있다

전위순회 (Pre-order)

Pre-Order Traversal

1. Root 를 방문한다

2. 왼쪽 subtree를 방문한다

3. 오른쪽 subtree 를 방문한다

특징 : 루트가 가장 먼저 나온다

노드를 먼저 들린다, 그래서 Pre

PreOrderTraversal( Node* pNode ){

if( pNode != m_pNodeTail )

{

visit(pNode); // 해당 노드에서 왼쪽, 또는 오른쪽으로 갈지를 정하기 전에 해당 노드를 들린다

PreOrderTraversal(pNode->pLeft );

PreOrderTraversal(pNode->pRight );

}

}

tip : 노드와 처음 마주칠때 방문한다

중위순회 (in-order)

1. 왼쪽 Subtree 를 방문한다

2. Root 를 방문한다.

3. 오른쪽

특징 : 가장 왼쪽노드가 먼저 나온다

노드를 중간에 들린다, 그래서 In

InOrderTraversal( Node* pNode ){

if( pNode != m_pNodeTail )

{

InOrderTraversal(pNode->pLeft );

visit(pNode); // 왼쪽 먼저 트리를 찾아 갔다가 왼쪽이 없으면 방문하고 오른쪽을 가기 때문에 중위 순회

InOrderTraversal(pNode->pRight );

}

}

tip : 왼쪽으로 갔다가(왼쪽 끝까지 갔다가) 다시 올라올때 방문한다

후위순회 (post-order)

1. 왼쪽 subtree 를 방문한다.

2. 오른쪽 subtree 를 방문한다.

3. root 를 방문한다

특징

1. 가장 온쪽 노드가 첫번째 나온다

2.루트가 가장 나중에 나온다

노드를 마지막에 들린다, 그래서 Post

PostOrderTraversal( Node* pNode ){

if( pNode != m_pNodeTail )

{

PostOrderTraversal(pNode->pLeft );

PostOrderTraversal(pNode->pRight );

visit(pNode); // 왼쪽, 오른쪽 다 들린 후 마지막에 올라올때 들린다

}

}

tip : 현재 노드에서 오른쪽으로 갔다가 오른쪽 끝까지 간 후 다시 올라올때 방문한다

레벨순회 (Level-order)

특징 : 루트가 가장 먼저 나온다

큐를 이용

PreOrderTraversal( Node* pNode ){

ListQueue<Node*> queue;

queue.Put(pNode);

while( !queue.IsEmpty() ) // iteration

{

pNode = queue.Get();

if( pNode != m_pNodeTail )

{

visit(pNode);

queue.Put(pNode->pLeft );

queue.Put(pNode->pRight );

}

}

}

정리하면 A 에 딸려 있는 바로 아래 자식들을 큐에 순서대로 추가 한다( A를 큐에서 제거 후 B,C 추가)

순서적으로 B,C 의 순이니 추가된 B를 제거 후 B 의 바로 한칸 아래 자식들을 다시 큐에 추가 하면

C , D, E 의 순이 된다

그러면 다시 C 에 대한 자식 F 를 큐에 추가 하고 C 를 제거(즉 앞에 있는 것을 제거 하면

트리 각 레벨에서 왼쪽에서 오른쪽의 순으로 큐 뒤에 추가 하는 것이 된다

즉 큐에서 제거 되는 노드의 자식들을 큐 뒤에 추가 함으로써 위에서 아래로, 왼쪽에서 오른쪽으로의 노드를 순회 할 수 있게 된다

tip : 큐의 가장 처음 즉 queue.Get() 할때의 노드를 제거 하면서 제거되는 노드들의 자식(2개 또는 1개) 을 큐의 뒤에 추가해준다

자식을 추가 하기 전에 노드를 방문한다

반응형

'알고리즘 & 자료구조 > 알고리즘&자료구조' 카테고리의 다른 글

수식트리(Parse Tree)  (0) 2012.10.31
스택을 이용한 전위 순회 (트리)  (0) 2012.10.31
이진트리 모델링  (0) 2012.10.31
트리(Tree)  (0) 2012.10.31
계산기 프로그램 (중위->후위)  (0) 2012.10.31
반응형

생성자에서 헤드노드와 테일 노드를 만든다

파괴자에서는 추가된 노드를 모드 제거한 후 가상 노드(헤드, 꼬리) 를 제거한다

이중리스트 처럼 가상누드인 헤드와 테일 노드를 기준으로 이진트리를 생성한다

Tip 트리를 생성할때 Left, 또는 Right 중 가르키는 겂이 없을 때는 테일 노드를 가르키도록 한다

반응형
반응형

트리는 비선형 자료구조이다(선형이 아니다 )

Node( vertex) 와 Link(edge)로 구성됨

Node 는 정보(data)를 포함한다

Link는 두 Node 간의 연결을 나타냄

특징 :

Root 가 하나 존재 해야 한다 Root 는 최상의 Node

Root를 제외한 모든 Node 는 하나의 부모를 가져야 함

Node 는 여러개으 자식을 가질 수 있다

한 Node로 부터 다른 Node로 가는 경로는 유일해야 함

ex ) 윈도우 파일 구조( 윈도우 탐색기 )

leafNode

줄기의 끝 = 자식이 없는 노드 H,J,D,E,F,G , 최 하위 노드

Terminal node : 끝인 노드

Internal Node : Leaf Node 가 아닌 노드 , 즉 자식이 하나라도 있는 노드 B,C,I,A

sub tree : 트리의 부분 집합 {B,E,F,G} , {I,J} , {D}, ,

Path : 한 Node로 부터 다른 Node 로 가는 경로(중복없이 가능 경로)

만약 G와 C 가 연결 되어 있다면 G 에서 C 로 가는 경로가 한가지가 아닌 다른 경우의 수가 생김으로 그때는 트리가 되지 않느다

만약 연결됐다면 이때는 이것을 그래프 라고 한다

레벨(Level) : Root 에서 특정 노드로 가는 경로의 노드 수, 이때 Root 를 Level 1 로 친다

그래서 위 그래프의 레벨은 4 이다

최소공통선조(Least Common Ancedstors) ; 두 노드의 공통적인 선조중 가장 레벨이 높은 선조 노드

H,J 를 놓고 봤을때

H의 선조 = C,A

J 의 선조 = I ,C, A

공통 선조 C,A

가장 높은 선조는 Level 2 인 C 가 된다

이것이 중요한 이유는 경로는 항상 최소공통선조를 거쳐서 지나가기 때문!!!

자식 노드 : 자신의 아래에 연결되어 있는 노드 => C 의 자식 노드는 H,I

부모노드는 : 자신의 위에 있는 노드 =>I 의 부모는 C

조부모 : 자신의 부모의 부모노드 I 의 부모는 C , C의 부모는 A, 그래서 I 의 조부모 노드는 A 가 된다, J 의 조부모는 C

반응형
반응형



중위표기법 4+3 인 연산자가 중간에 있는 식을


먼저 후위 표기법으로 바꾼후 다음을 실행한다


1. Operand(숫자) 를 만나면 Operand를 Stack 에 PUsh

2. Operator 를 만나면 Stack 에서 Pop 을 두번해서 이 두 값으로 연산을 한 다음

연산된 결과를 다시 Stack에 Push 한다

3. 최종결과는 마지막에 Stack 에 남아 있는 값이다(마지막 연산된 결과를 PoP 하면 결과값)


연산자를 놓느 순서는


연산자 오른쪽에 먼저 그다음 연산자 왼쪽 순으로 놓는다

연산시 +,* 는 교환이 성립함으로 간단하게 한줄로 처리 할 수 있지만

/,- 는 교환 법칙이 성립하지 않음으로 Pop 을 할때 순서에 맞게끔 구성해야 한다

문자를 숫자로 바꾸는 것은 다음 링크를 참고 한다

반응형
반응형

문자를 숫자로 변환

만약 "365" 라는 문자가 있을 경우 이것을 숫자로 변환 하려면

int n=0,i=0;

char num[4]="365", c; // num[0] == 3

num[3]='\0';

c = num[0];

do{

n = n*10 + c - '0';

c = num[++i];

}while( c >= '0' && c<='9' ) // c 가 0~ 9 사이의 숫자 안에 있다면

// 널문자를 만나면 바져나온다

결과 3 , 36 , 365


int 변수의 숫자를 한개씩 때오기

// 문자의 숫자가 증가할 수록 매칭 숫자도 증가한다

int inc=10;
int total = 123;
int result;
for(int i=0;i<3;++i){

result = total%10;
total/=10;

}

반응형
반응형

계산기 : 수식을 입력받아 계산하여 출력한다

계산기를 만들려고 할때의 데이터들로 순회구조를 변환해 본다

operator +,-,*,/ 만 가능하다고 가정(사칙연산)

Operand : 연산자 양쪽에 있는 자료형

이때의 Operand 는 정수만 가능하다고 가정

* 중위 표기법 (Infix Notation) A + B

- 우선순위를 위해 괄호를 사용해야 함

*후위 표기법(Postfix Notation):

수식을 계산하기 위한 알고리즘이 단순!

- 괄호를 쓰지 않아도 연산 가능

AB+

*전위 표기법 (Prefix Notation)

- Operator 가 두 Operand 앞에 있음

+AB

중위를 후위 표기법으로 알고리즘을 풀기 위해 중위로된 식을 후위 표기법으로 변환 해야 한다

the way that 1. 중위 표기법에서 괄호가 씌워져 있는 경우에만

조건 A+B*C 에 대한 연산이 완전한 괄호로 되어 있어야 한다

ex) (A+(B*C))

알고리즘( 결과 저장소는 String 이라 가정 )

1. '(' 문자는 무시한다

2. Operand(숫자) 는 그대로 결과저장소에 추가( += )

-하나의 숫자(항) 이 입력되면 구분하기 위해 ' ' 을 추가로 입력해 준다

3. Operator(연산자) 는 Stack 에 PUsh 한다

- 저장소에 연산자를 저장할때 여산자와 구분을 하기 위해서 스택에서 빼오 연산자를 저장소에 넣은 후 ' ' 을 하나 연달아 넣어준다,

4. ')' 를 만나면 Stack 에서 Pop 하여 결과저장소에 추가 ( += )

the way that 2. 중위 표기법에서 괄호가 없는 경우

먼저 연산자 우선 순위를 정해야 한다

'(' : 0

+,- : 1

*,/ : 2

로 우선 순위를 매기고

반응형
반응형

큐, 환원큐 모두 양방향 리스트 또는 배열로 구현 할 수 있는데

*리스트로 짤 경우 양방향리스트로 구성해야 한다

배열로 하면 배열값을 밀어내야 하는 상황이 발생 함으로

양방향 리스트로 만드는 것이 일반적으로 효율적이다

환원큐가 비었을 때는 front == real 이며

꽉 찾을때는 하나의 공간을 두고 front 와 real 이 1 간격으로 배치하게 된다

real 에서 데이터 추가, front 에서 데이터 빼온다

real 에 추가시 현재 가르키고 있는 곳에 데이터를 추가 한 후 real++

front 에서는 현재 가르키고 있는 곳에서 데이터를 빼낸 후 front++

그냥 front의 값만 가져오려면 {리스트&배열}[ front ] 의 값을 가져오고

(+ 가 순방향이라고 가정했을때)

real 의 값만 가져오려면 {리스트&배열}[Dec(real-1 )] 을 가져오면 된다

환원큐의 인덱싱은 % 를 활용

m_size 가 배열 사이즈 만약 10 이라고 한다면

현재 가르키는 곳의 다음 번지를 알기 위하여

가르키는 곳 n 에 +1 을 한 후

전체 사이즈로 나누면 배열 사이즈 버위 내에서

순환하는 인덱스 번호를 얻어올 수 있다

감소 ( Dec() )는 (n-1) % m_size;

반응형
반응형

단일연결 리스트 자체가 스택이다

단 끝노드에서 끝방향 쪽으로 노드를 추가해가며 스택을 만들면 효율이 떨어진다

단일 연결리스트로 구성된 스택은

단인 열결 리스트의 초기 구성이

{헤드노드} ~ {꼬리노드}

로 구성되어 있음으로

노드 추가 함수인 AddNode() 을 헤드 노드에서 호출 하게 된다

{헤드노드}.AddNode( pNode );

원소 추가(Push)

이것을 스택으로 사용 할려면

list_instance.AddHead( pNode );

로 써 입력해주면 된다, 즉 헤드 노드 다음에 새로운 노드를 계속 추가 하는 것

GetVal() , 그렇다면 값을 가져오기 위해선?

list_instance.GetHeadVal() 은 헤드노드가 가르키고 있는 다음 노드의 값을 가져 오면 되고

만약 다음 노드가 꼬리 노드인 경우 에러처리 또는 예외 처리 하면 된다

제거(POP) 은 헤더노드 다음 노드를 제거 하면 된다

반응형
반응형

스택(배열)

데이터 집합을 배열에 저장

Top 의 위치를 저장하는 멤버 필요

생성자에서 디폴트 배열 크기를 정해준다

-특징

top 은 가장 최근 원소를 가르키는 것임으로 top 의 초기치는 -1 이 된다

배열에 가득 차면 에러를 반환하거나 더큰 배열로 옮기든가 해야 한다

스택(리스트:다일연결리스트)

단일 연결리스트 자체가 스택이다

연결리스트에 저장( 단순 연결 리스트로 구현 할 수 있다 )

add, remove

연결리스트 특성상 스택의 크기가 자유롭다

결론 :

일반적으로 리스트 스택을 많이 사용

반응형
반응형

 첫 노드부터 끝노드 까지는

반복문을 돌면서 레퍼런스로 넘어가는 다음 포인터를 넘어받아와 이 구문을 계속 돌리면 된다

끝에서 처음으로 가는 것은 이전의 노드를 얻어오는 함수를 호출하면서 포인터를 레퍼런스로 넘기면 된다

list == 이중리스트

*find( 값 )

값과 같은 값을 찾기 위해 위와 같은 조회 방식을 이용해 값을 비교해 가며 찾을 수 있다

Tip : 끝노드를 자기 자신을 가르키고 있으니 즉

tail 의 next 는 &tailNode 이니 디버그 상에서

어느 한 노드의 next 포인터를 따라가다보면 마지막엔 tail 노드의 주소 값이 계속해서 반복적으로 나타남을 알 수 있다

반응형
반응형

정의

- 양방향 연결 리스트

- Data 와 Next pointer, Prev Pointer 로 구성

헤더와 꼬리가 초기에 주어진다는 점은 단순연결 리스트와 동일

초기화는

헤더는 의 prev 는 자기자신 이고 next 는 테일을 가르킨다

테일에서는 next 가 자기자신을 가르키고 prev 가 헤드를 가르키도록 한다

각 노드에는 데이터를 답을 수 있는 templte< typename T > 인 T 로 정의 한다

Tip : insert 시 기준이 되는 노드와 멀리떨어져 있는 노드와 새로운 노드와의 연결을 먼저 연결한다

because 기준이 되는 노드와 새로운 노드를 먼저 연결하면 기존에 연결 되어 있던 노드의 포인터를 잃어버리기 때문

1. 삽입

이전 삽입 :

define void* POS

- 다음삽입( Insert Next) 도 InsertBefore 와 유사하게 마들어 질 수 있다

*삭제

이중리스트는 양쪽의 포인터 정보를 알기 때문에 자기 자신을 지울 수 있다

삭제할 노드의 양쪽 노드들이 삭제 할 노드를 가르키지 않고 기준 노드의 양쪽노드들을 서로가 가르키게 한다

그런 후 삭제기준 노드를 삭제

반응형
반응형

1.

클래스 내에서 enum 으로 설정한 값을 리턴하여 catch 구문에서

enum 명으로 예외처리할 수 있다

try catch 구문에서는 int 의 0 과 enum 의 0 을 다른 것으로 인식 할 수 있다 단 enum 의 변수명으로 받아야 한다

2. 생성자, 소멸자에서 에러가 나는 경우 try, catch 구문으로 처리 할 수 있다

#include "stdafx.h"

class a{
public :
enum DD{ FIRST=0 };

DD get(){ return FIRST; }
int getzero(){ return 0; }

};

int _tmain(int argc, _TCHAR* argv[])
{
a ai;

try{
//throw ai.get();
throw ai.getzero();

}catch( a::DD data ){
int ddd=data;
ddd+=1;
}catch( int data ){
int ddd=data;
ddd+=1;
}


return 0;
}

반응형
반응형


http://cafe.naver.com/jzsdn/2468




#ifndef __ICMSINGLETON_H
#define __ICMSINGLETON_H

#include <stdio.h>
#include <WINDOWS.H>

template< class C >
class ISingleTon
{
public:
ISingleTon()
{
if( !m_pInstance )
{
int nOffset = ( int )( C* )1 - ( int )( ISingleTon< C >* )( C* )1;
m_pInstance = ( C* )( ( int )this + nOffset );
}
}
virtual ~ISingleTon() {}

static bool CreateInstance()
{
if( !m_pInstance )
{
m_pInstance = new C;
}

if( m_pInstance )
return true;
return false;
}

static void ReleaseInstance()
{
SAFE_DELETE( m_pInstance );
}

static C* GetInstance()
{
return m_pInstance;
}

protected:
static C* m_pInstance;
};

template< class C >C *ISingleTon< C >::m_pInstance = NULL;

#endif

이건 싱클톤 패턴을 템플릿으로 구성한 인터페이스 클래스입니다.( Game Programming Gems에서 봤었던걸로 기억합니다. )

일일이 클래스에 GetInstance()와 ReleaseInstance()를 추가하는 수고를 덜수 있습니다.

CreateInstance()를 따로 둔건 쓸데없는 걱정일지도 모르지만 GetInstance()에서 m_pInstance의

유효성체크를 하는부분이 거슬려서입니다.

사용법은

#include "ISingleTon.h"

class CSession;

class CSessionManager : public ISingleTon < CCMSessionManager >

{

public:

CSessionManager();
virtual ~CSessionManager();

void AddSession( CSession* pSession );

......

}

#define g_SessionMng CSessionManager::GetInstance()

main()

{

CSessionManager::CreateInstance();

CSession* pSession = new CSession;

g_SessionMng->AddSession( pSession );

.......

}

반응형
반응형

http://openparadigm.tistory.com/20

빅오표기법은 간단하게 알고리즘의 성능을 평가할 수 있는 방법이다. 벤치마킹을 통해 실제적인 알고리즘 분석도 가능하지만 어떻게 모든 알고리즘을 전부 테스트하고 사용하겠는가.. 그냥 대충 '각'을 잡아본 후에 사용하는 것이 효율적이다.

이러한 알고리즘 테스트를 위한 여러가지 방법이 있다. 3가지 정도가 있는데 Ω,θ,O 이렇게 세가지이다.
- O표기법은 알고리즘의 최악의 성능을 표시해준다.
- Ω표기법은 알고리즘의 최고의 성능을 표시해준다.
- θ표기법은 정확한 알고리즘의 성능을 표시해준다.
그럼에도 불구하고 O표기법을 많이 사용하는 이유는 '아무리 최악의 상황이라도 이정도의 성능을 보장할 수 있다'라는 것을 보여주기 위해 O표기법을 사용하는 것이다. 괜히 Ω표기법으로 알고리즘을 평가해서 최악을 성능을 내는 경우가 발생하면 기분나쁘잖아 ㅡㅡ;; θ표기법은 사실 알고리즘을 계산으로는 정확히 알기 어렵기 때문에 그닦 많이 사용되진 않는다.

그럼 우리는 O표기법만 알아보자.
다음과 같은 코드가 있다.

void main(){
      int sum = 0;
      int i;

      for(i=1;i<=100;i++){
            sum=sum+i;
      }
      printf("1부터 100까지의 합: %d\n",sum);
}


요런 코드가 있다면 이것은 1부터 100까지 더하는 프로그램이 된다.
이걸 각각의 코드가 몇번 실행 되는지 확인해 보자.

int sum = 0;
1번실행
int i;
1번실행
for(i=1;i<=100;i++){
101번실행(100번실행후 한번 더 실행하는 과정에서 밖으로 빠진다.)
sum = sum + i;
100번실행
printf("1부터 100까지 합:",sum); 1번실행


결국 총 204번이 실행된다. 이것을 빅O표기법으로 표시하면 O(204)가 된다. 쉽죠잉?
"O표기법에서 상수의 존재는 알고리즘의 성능에 아무런 영향을 끼치지 못한다"는 것을 알아두길 바란다.
그럼 O(204)를 O(1)로 간단히 표시하겠다. 앞으로 O(100000)이든 O(3)이든 상수만 들어간 O는 모조리 O(1)로 생각하자.
왜냐하면 위와 같은 코드의 실행 시간은 10번 실행이 10000번 실행보다 빠른게 당연하겠지만 알고리즘 자체로 본다면 데이터의 양이 100 이런식으로 고정되어 있기 때문에(위에 100번 돈다고 했으니까) 다른 입력된 데이터가 많더라도 알고리즘은 여전히 100번만 돌게된다. 즉 입력데이터의 양에 상관없이 성능이 일정하다는 말이다.

근데 만약에 위의 for문이 for(i=1;i<=N;i++) 라고 되어 있다면 어떻게 될까? N번 도는 것이다. N이 얼마인지는 모르겠지만 외부에서 N을 입력받던 계산의 값으로 받던 하여튼 임의의 값이 들어오게 될것이다. 이럴때 우리는 O(N)이라고 표시한다.

여기서 다른 코드를 보자.



이건 최대값을 구하는 코드이다. array[0]을 max에 넣고 for문을 시작한다. array[1]부터 max와 비교하며 array[1]이 max보다 크다면 array[1]을 max에 넣는다. 아니라면 array[2]와 max를 비교한다. 이런식으로 계속 큰 값만 max에 넣으면 배열에 마지막에 가서는 가장 큰값이 max에 있을것이다.

이코드는 총 몇번이 실행되는가? 총 for문은 size-1만큼 실행된다. 편의상 size를 N으로 두자. N-1만큼 실행된다. 이걸 O표기법으로 표시하면 O(N-1)이 된다. 만약 N이 거의 수억천만조경의 값이라면 -1따위야 가뿐히 무시할 수 있는 값이다. 그래서 O(N)으로 표시한다.(O표기법에서 상수란 무용지물이란 말이다..) N의 값이 많아지면 많아질 수록 처리 시간도 그와 비례해서 늘어난다는 의미이다.

위의 코드와 같은 결과를 가지는 아래의 코드를 보자.

int returnMax(int array[], int size){
    if (size < 1) return -1;                       
    bool findMax;                                   
    for (int i = size-1; i > 0; i--) {             
         for (int j = 0; j < size; j++) {          
              if (array[j] > array[i]) {           
                   findMax = false;               
              }
         }
         if(findMax)                               
              break;
    }
    return array[i];
}


위의 코드도 최대 값을 반환한다. 코드를 말로 해석하려 했는데 너무 어렵다 ㅡㅡ;; 그냥 코드를 보시고 이해하길...
보면 알겠지만 이코드는 안쪽 for문에서 N번 반복하고 바깥쪽에서도 N번 반복한다. 다시말해 안쪽의 for문은 N번 반복을 N번이나 한다는 말이다. 물론 최대값이 배열 가장 앞에있는 최악의 경우에 말이다. 이것을 빅오로 나타내면 O(N²)으로 표시할 수 있다. 입력된 데이터의 양(N)에 따라 시간은 그 제곱배로 늘어난다는 말이다.

물론 이코드는 상황에 따라 다르다. 만약 가장 큰값이 가장 끝에 있다면 가장 끝값과 앞에 값들을 각각 한번씩만 비교하면 답이 나오기 때문이다. 이것은 O(N)이된다. 이건 최선의 경우일 때이다. 그리고 평균적으로 계산해 보자면 최대값이 배열 가운데있다면 N번 반복을 N/2번만 하면 되기에 결국 O(N²/2)로 나타낼 수 있다. 그럼 빨라진건가? 그렇지 않다 평균결과는 여전히 O(N²)이다. 왜냐하면 위에서 밝혔듯이 빅오에서 상수의 의미는 없다고 생각하는 것이 좋다고 했다. 책에는 그 이유를 '기계어와 CPU가 처리하는 시간에 따라 결과시간이 달라지기 때문' 이라고 밝혔지만 나도 정확이 이게 무슨말인지는 모르겠다. 여튼 빅오에서는 상수를 무시하고 최고차항이 얼마느냐에만 촛점을 맞추고 있다. 그래서 평균 시간도 O(N²)라고 정의해야한다(고 한다 ㅡㅡ;)

그밖에 경우는 아래와 같다.

O(logN) - 처리 데이터의 양이 증가할 수록 실행시간이 약간씩 증가한다. 하지만 logN의 그래프가 그렇듯이 증가 폭이 급격히 증가하진 않는다. 일반적으로 좋은 효율의 알고리즘이 여기에 해당한다.

O(NlogN) - 처리 데이터의 양의 증가에 대해 처리 시간은 정비례보다 약간 더 많이 증가를 한다. 이것도 효율이 괜찮은 알고리즘일 경우의 계산이다.

O(N³) - 데이터에 양에 대해 세제곱 만큼 시간이 증가하기에 좋은 알고리즘이 아니다. for문이 세번 중첩되면 이꼴이 난다.

O(2^N) - 2의 N제곱이 되는 알고리즘은 거의 최악의 알고리즘 중 하나이다.

아래에 각 결과의 처리시간에 대한 그래프를 가져와 봤다.

<출처 : http://media.photobucket.com/image/o%20notation/amit_9b/BIGO_GRAPH.jpg>


<Big O Notation Graph>

간단하게 빅오표기법을 사용하는 방법을 정리하자면
1. 입력값이 무엇인지 확인한다.(N)
2. 수행 연산 횟수를 N의 식으로 표현한다.
3. 가장 차수가 높은 항만 남기고 그 아래차수와 상수는 날려버린다.

주의점
1. 반복문이 중첩이 아니라 그냥 두개가 나란히 있는경우는 더 높은 차수의 for문을 알고리즘 성능으로 사용한다.
2. 상수가 곱해져 있고 옆에 N이 더 있더라도 최고차수만 사용한다.

+
재귀호출에서의 빅오표기법???? 재귀호출은 덧셈처럼 계산되어서 O(N)으로 사용된다는데 이건 이후에 다시 포스팅하자.

반응형
반응형
http://goo.gl/ZMRMU


이번에 알아볼 것은 힙(Heap)이다. 얼핏 힙을 들어보면 어려울 것만 같이 느껴질 수도 있으나

집중해서 보면 이해가 금방되는 구조다.

 

힙 트리는 간단하게 말하면 이진트리이나 다음과 같은 성질을 가지고 있다.

 

1. 아래에서 선택

   1-1. 부모 ≥ 자식들 (최대 힙)

   1-2. 부모 ≤ 자식들 (최소 힙)

2. 완전이진트리

3. 데이터 중복허용

 

 

힙은 조밀조밀하게 얽혀있는 완전이진트리이다.

 



                               완전이진트리                                                             완전이진트리

 

완전이진트리에 중간에 이상하게 허한 트리는 아닌 하나씩 촘촘하게 들어가는 트리를 말하니 이 점을 확실히 알아두자.

만약 5 노드 아래의 둘 중 하나 없다면 완전이진트리의 구조가 깨지는 것이다.

 

힙 트리는 조밀조밀하게 구성되어있다고 했다. 그러기에 힙을 표현하는 방법은 보통 배열을 이용해서 나타낸다.

(정적배열이든 동적배열이든 상관없다.)

 

중요하게 생각해야하는 가장 기초적인 지식은 트리의 배열 표현 방법이다.

배열로 이진트리를 표현하는 방법은

기본적으로 array 배열의 부모 노드의 인덱스가 i 라 하면,

자식노드는 array[i*2] (왼쪽 자식)와 array[i*2+1] (오른쪽 자식)이 된다.

 

왼쪽 자식의 인덱스 = (부모 인덱스)*2

오른쪽 자식의 인덱스 = (부모 인덱스)*2+1

 

힙트리도 이진트리라는 점을 상기하면 금방 이해할 수 있다. 보통 배열로 트리를 표현함에 있어서

자식의 위치에서 부모의 존재를 유추할 때는 반으로 나누면 된다. 그렇기에 루트노드는 1부터 시작한다.

아무리 나눠도 0으로 나눌 수 없으니까 말이다.

 

 

힙트리에는 두 가지 종류가 있다.

1-1. 부모 ≥ 자식들 (최대 힙)

1-2. 부모 ≤ 자식들 (최소 힙)

 

부모의 노드가 자식보다 큰 힙트리와 보모의 노드가 자식보다 작은 힙트리이다.

의도에 맞게 선택하여 프로그래밍 하면 된다.

 

이제 최대 힙을 이용하여 힙 트리를 설명하겠다.

 

02: #define MAX     100
03: typedef struct HeapTree04: {
05:         int heap[MAX];
06:         int size;
07: }HeapTree;
08: 
09: HeapTree *Heap;


 

힙 트리의 구조체이다. 일반적으로 힙트리는 배열이라고 했다. 여기서는 정적배열을 이용한다.

현재 힙트리의 크기를 알 수 있도록 size 라는 변수를 두었다.

 

힙도 트리다보니 삽입과 삭제의 방법이 중요하다. 일단 삽입에 대해서 이야기해보자.

최대 힙의 경우 새롭게 넣은 값이 부모보다 크다면 값을 교환을 해주면 된다. 하나의 조직에서 서열이 바뀌는 셈이다.

 

삽입의 방법은

 

1. 힙 트리의 나머지 공간에 넣는다.

2. 부모와 비교하며 자신보다 작으면 바꾼다.

3. 자신보다 크거나 같은 값이 나올 때까지 2번을 반복한다.

 

 

일단 위의 트리에 10을 넣어보자.

 

 

 

 

그렇다면 새롭게 추가된 10은 부모노드와 비교해가며 자신보다 부모가 클 때까지 서로 위치를 교환하며 주욱 올라간다.

 

 

 

 

다음과 같이 교환이 모두 끝났다면 제일 위의 노드는 바로 10이 된다.

 

 

 

 

그렇다면 7을 넣는다면 어떨까? 마찬가지로 차례로 트리의 끝에 추가된다.

 

 

 

 

그리고 바로 위의 부모노드 5보다 크니 교환을 해서 올라가나 다음 부모인 9보다 작으니 그 상태에서 멈춰버린다.

 

 

  

 

 

이것이 바로 힙트리의 삽입과정을 나타낸 것이다.

 

16: void InserData(HeapTree *Heap, int element)
17: {
18:         int i;
19: i=++Heap->size;
20: 
21:         while( (i!=1) && 
22:                         (element > Heap->heap[i/2]) )
23:         {
24:                 Heap->heap[i] = Heap->heap[i/2];
25:                 i/=2;
26:         }
27:         Heap->heap[i]=element;
28: }

 

삽입은 뭐 그리 대단한 것은 아니다. 일단 배열의 인덱스를 나타낼 i 를 선언하고

힙의 크기를 +1 시켜준다. (19번줄) 일단 삽입했다고 가정을 하고

 

끝단에서부터 부모로 올라가라면 2 씩 나눠서 올라가야 한다는 점을 상기시켜보자.

루트의 인덱스는 1이니 루트에 도착하지 않았고        ( i!=1 )

삽입한 요소가 부모보다 클 때 교환이 일어나야 한다. (element > Heap->heap[i/2])

 

 

24:                 Heap->heap[i] = Heap->heap[i/2];
25:                 i/=2;

 

 

24번줄은 부모노드를 자식노드로 끌어내리고 인덱스를 부모노드로 올라가는 것이다.

이렇게 루프가 끝나고 나서 현재의 위치에 요소를 대입하면 힙 트리에서 삽입이 끝난다.

 

 

이제 삭제를 알아보자. 힙 트리에서 삭제는 바로 루트를 빼내는 것이다.

루트를 빼내다보니 트리 구조가 무너지게 되는 것은 당연지사. 이를 위해 트리를 재구축 시키는 과정이 필요하다.

재구축 방법은 힙트리의 끝의 단말노드를 루트에 위치시키고 차례로 차식노드들과 비교해가며 재정렬하는 과정이 필요하다.

 

1. 루트를 꺼낸다.

2. 트리의 끝에 해당하는 단말노드를 임시적으로 루트로 위치시킨다.

3. 루트에 위치한 임시노드는 차례로 자식들과 비교하며 자신의 자리를 찾아간다.

 

 

아래의 힙트리에서 삭제연산을 하면 맨 위의 루트 10이 꺼내지게 된다.

 

 

 

 

꺼내진 자리를 힙 트리의 끝단에 위치한 노드 5가 그 자리를 대신한다.

 

 

 

그리고 임시노드 5는 자신의 위치를 찾아간다. 찾아갈 때 자식의 두 노드 중에서 어디로 나아갈지를 정해야하는데

자식들 중 큰 값이 있는 방향으로 나아간다. 임시노드 5는 자식노드인 9와 4 중, 9로 나아간다. (9>4)

 

 

 

 

 

이것을 코드로 표현하면 다음과 같이 된다.

 

30: int DeleteData(HeapTree *Heap)
31: {
32:         int parent=1, child=2;
33:         int element, temp;
34:         
35:         element=Heap->heap[1];
36:         temp=Heap->heap[Heap->size--];
37: 
38:         while( child <= Heap->size )
39:         {
40: if( Heap->heap[child] < Heap->heap[child+1] )
41:                         child++;
42: 
43:                 if( temp >= Heap->heap[child] ) break;
44: 
45:                 Heap->heap[parent]=Heap->heap[child];
46: 
47:                 parent=child;
48:                 child*=2;
49: 
50:         }
51:         Heap->heap[parent]=temp;
52:         return element;
53: }

 


차근차근 설명해볼까? 삭제를 할 때 꺼내지는 요소(element)는 루트이다. Heap->heap[1]

그리고 임시노드가 되는 것은 힙 트리의 끝에 있는 단말노드이므로 그 자리에서 떼어낸다.  Heap->heap[Heap->size--]

 

 

35:         element=Heap->heap[1];
36:         temp=Heap->heap[Heap->size--];

 

 

그리고 눈여겨 봐야할 것이 루트에 위치시킨 임시노드(temp)을 자신의 위치로 찾아가도록 하는 과정이다.

 

 

32:         int parent=1, child=2;

 

 

에서 일단 루트에서부터 찾아갈 것이니 parent=1 로, child=2로 선언한다.

 

 

38:         while( child <= Heap->size )


 

부모의 위치에서 자식들을 비교해야하기 때문에 힙트리의 크기(Heap->size)를 넘어서면 안 된다.

 

 

40: if( Heap->heap[child] < Heap->heap[child+1] )
41:                         child++;

 

 

위에서 임시노드는 자신의 위치를 찾아가기 위해 자식들과 비교하며 나아가는 방향을 정할 때

자식들 중 큰 값을 가지는 자식들로 방향을 정해서 내려간다고 했다.

 

만약 임시노드가 자식노드보다 크다면, 일단 힙트리의 조건을 만족하기에 교환을 멈춘다. (43번줄)

 

 

43:                 if( temp >= Heap->heap[child] ) break;

 

 

그것이 아니면 자식을 부모노드와 교환을 하며(45번줄) 계속 인덱스를 자식단으로 내려간다 (47-48번줄)

 

 

45:                 Heap->heap[parent]=Heap->heap[child];
46: 
47:                 parent=child;
48:                 child*=2;

 

 

루프가 끝났다는 말은 일단 임시노드의 진짜 자리를 찾았다는 의미가 되니 그 자리에 임시노드를 위치시킨다.

 

 

51:         Heap->heap[parent]=temp;

 

 

child 는 일단 *2 를 시킨다고 했으니 parent 가 바로 그 자리인 셈이다.

그리고 처음에 꺼낸 값을 리턴한다. (return element)

 

 

여기까지가 힙트리의 삽입과 삭제의 과정이다. 여기서 설명한 힙트리는 최대 힙이다. 

최대 힙은 컴퓨터과학 전분야에서 사용되는 우선순위라는 개념이다.

우선순위가 높은 프로세스에게 먼저 메모리를 할당하거나 처리할 때 힙 트리가 사용된다.

 

이러한 힙트리를 이용해서 힙 정렬도 할 수 있다.

 

힙 정렬은 그냥 힙트리에 데이터를 막 집어넣고 하나씩 뽑아내면 정렬이 된다!!!!

너무 간단하다! 트리이다보니 시간복잡도는 O(log2 (n)) 이다. 합병정렬(Merge sort)와 최악의 시간복잡도가 같다.

 

 

55: void HeapSort(HeapTree *Heap, int *array, int array_length)
56: {
57:         int i;
58: 
59:         for(i=0;i<array_length;i++)
60:                 InserData(Heap, *(array+i));
61: 
62:         for(i=0;i<array_length;i++)
63:                 *(array+i)=DeleteData(Heap);
64: }


 

힙 정렬 함수는 이렇게 간단하게 구현이 된다. 주욱 넣어서 주욱 빼내면 된다.

이것을 사용하는 메인함수를 만들어볼까?

 

67: int main()
68: {
69:         int i;
70:         int array[5]={3,9,1,4,8};
71:         HeapTree Heap;
72:         InitHeap(&Heap);
73: 
74:         HeapSort(&Heap,array,5);
75: 
76:         for(i=0;i<5;i++)
77:                 printf("%d ",array[i]);
78:         
79: 
80:         return 0;
81: }

 

 

 

오름차순으로 정렬을 하려면 최소 힙으로 고쳐주면 된다. 삽입함수와 삭제함수에서 비교문만 살짝 고치면 된다.

첨부소스를 참조하여 확실히 외워두자.

반응형
반응형

http://yeols.com/80074139499




힙 정렬(Heap Sort)

 

  • 고급 정렬 알고리즘(병합 정렬, 퀵 정렬, 힙 정렬) 중 하나.

    먼저, 힙 정렬을 알아보기 전에 힙 정렬에 필요한 이진 트리(Binary Tree)의 개념 부터 알고 넘어가자.

  • 이진 트리(Binary Tree)

    • 최대 두 개까지의 자식 노드를 가질 수 있는 트리
      • 하나의 노드는 0, 1 혹은 2개의 서브 트리를 가질 수 있다.
      • 좌 서브 트리(Left Subtree)
      • 우 서브 트리(Right Subtree)

      • 널 트리(Null tree)
        - 어떠한 노드도 가지고 있지 않은 트리

  • 포화 이진 트리(Full Binary Tree)

    • 높이 h 인 이진 트리에서 모든 단말 노드가 레벨 h 에 있는 트리
      • 최대 노드 수 : 

  • 완전 이진 트리(Complete Binary Tree)

    • 높이(h-1)까지는 포환 이진트리 이고, 마지막 레벨 h에서, 왼쪽에서 오른쪽으로 단말 노드를 채운 것

  • 편향 이진 트리(Skewed Binary Tree)

    • 이진 트리 중에서 최소 개수의 노드를 가지면서 왼쪽이나 오른쪽 서브 트리만 가지고 있는 트리

  • 이진 트리의 순차 자료구조 표현

    • 완전 이진 트리의 배열 표현 방법

  • 이진 트리의 순차 자료구조 표현

    • 편향 이진 트리의 배열 표현 방법


그럼 이제 힙(Heap)에 대해서 알아 보자.

  • 힙(Heap)

    • 완전 이진 트리에 있는 노드 중에서 키 값이 가장 큰 노드나 키 값이 가장 작은 노드를 찾기 위해서 만든 자료구조

    • 최대 힙(Max Heap)
      • 부모 노드의 키 값이 자식 노드의 키 값보다 항상 크거나 같은 크기의 관계
        - ( 부모 노드의 키 값 ≥ 자식 노드의 키 값 ) 의 관계를 가지는 노드들의 완전 이진 트리

    • 최소 힙(Min Heap)
      • 부모 노드의 키 값이 자식 노드의 키 값보다 항상 작거나 같은 크기의 관계
        - ( 부모 노드의 키 값 ≤ 자식 노드의 키 값 ) 의 관계를 가지는 노드들의 완전 이진 트리

  • 힙(Heap)의 예

  • 힙(Heap)이 아닌 예

  • 힙 정렬(Heap Sort)의 개념

    • 힙 자료구조를 이용한 정렬 방법

    • 힙(Heap)에서 항상 가장 큰 원소가 루트 노드가 되고 삭제 연산을 수행하면 항상 루트 노드의 원소를 삭제하여 반환
      • 최대 힙에서 대해서 원소의 개수만큼 삭제 연산을 수행하여 내림차순으로 정렬 수행
      • 최소 힙에 대해서 원소의 개수만큼 삭제 연산을 수행하여 오름차순으로 정렬 수행

    • 힙 정렬(Heap Sort) 수행 방법
      1. 정렬한 원소들을 입력하여 최대 힙 구성
      2. 힙에 대하여 삭제 연산을 수행하여 얻은 원소를 마지막 자리에 배치
      3. 나머지 원소에 대해서 다시 최대 힙로 재구성
           원소의 개수만큼 2~3을 반복 수행

 

  • 힙 정렬 수행 과정

    • 정렬되지 않은 [69, 10, 30, 2, 16, 8, 31, 22]의 자료들을 힙 정렬 방법으로 정렬하는 과정을 살펴보자.
      • 초기 상태 : 정렬할 원소가 8개 이므로 노드가 8개인 완전 이진 트리를 만들고, 최대 힙(Max Heap)으로 구성함

(최대힙 구성시 우측 서브트리의 숫자 20 >> 30 임을 알려드립니다.)

 

1. 힙에 사제 연산을 수행하여 루트 노드의 원소 69를 구해서 배열의 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

2. 힙에 삭제 연산을 수행하여 루트 노드의 원소 31을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

3. 힙에 삭제 연산을 수행하여 루트 노드의 원소 30을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

4. 힙에 삭제 연산을 수행하여 루트 노드의 원소 22를 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

5. 힙에 삭제 연산을 수행하여 루트 노드의 원소 16을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

6. 힙에 삭제 연산을 수행하여 루트 노드의 원소 10을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

7. 힙에 삭제 연산을 수행하여 루트 노드의 원소 8을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

8. 힙에 삭제 연산을 수행하여 루트 노드의 원소 2을 구해서 배열의 비어있는 마지막 자리에 저장한다.

나머지 원소들에 대해서 최대 힙으로 재 구성함

 

 

 

  • 힙 정렬 알고리즘 분석

    • 메모리 사용공간
      • 원소 n개에 대해서 n개의 메모리 공간 사용
      • 크기 n의 힙 저장 공간

    • 연산 시간
      • 힙 재구성 연산 시간
        - n개의 노드에 대해서 완전 이진 트리는 log₂(n+1)의 레벨을 가지므로 완전 이진 트리를 힙으로 구성하는 평균 시간은 O(log n)
        - n개의 노드에 대해서 n번의 힙 재구성 작업 수행
      • 평균 시간 복잡도 : O(n log n)

반응형
반응형

http://www.codesos.com/book/algori/hash/hash.html

해슁의정의

여러개의 명칭(identifier)들이 무작위로 들어있는 테이블에서 특정 명칭을 찾고자 하는 경우

원하는 키값을 가지는 테이블 항목을 검색하기 위해 특정한 변환 함수를 이용하여 키값을 항목 의 주소로 직접 바꿔서 검색하는 방법을 해슁이라 한다. 이때 변환 함수는 해쉬 함수(hash function)라 한다.


해슁의필요성

명칭 테이블에서 키 값과 일치하는 명칭을 찾는 방법으로는 테이블에 있는 각각의 명칭을 키 값과 차례로 비교하는 방법이 있다.

이 방법을 사용하면 최악의 경우 n회의 비교가 필요하다. 해슁을 이용하면 해쉬 함수가 키값을 해당 주소로 단번에 변환해 주므로 매우 빠른 검색이 가능하다.


정적 해슁

정적 해슁은 고정 크기의 테이블을 이용하여 해슁하는 방법으로서 한번 저장된 명칭의 상대적 위치가 변경되지 않는다.

정적 해슁은 다음 순서로 설명한다.


해쉬 테이블(hash 테이블)

해슁을 이용하지 않는 경우에는 명칭들의 모든 가능한 조합을 수용할 수 있도록 명칭 테이블을 만들어야 한다.

조합 가능한 명칭들 중에 실제로 존재하는 명칭들의 수는 매우 적기 때문에 대부분의 공간은 낭비된다.
명칭 테이블의 기억공간 낭비를 막기 위해 해쉬 테이블을 사용한다.
해쉬 테이블은 b개의 버켓(bucket)으로 구성되고, 하나의 버켓은 s개의 슬롯(slot)으로 구성된다. 각각의 슬롯에는 명칭 테이블 항목처럼 하나의 명칭이 저장된다.

< 해쉬 테이블의 구조 >

해쉬 테이블의 크기는 필요에 따라 달리할 수 있는데 일반적인 명칭 테이블보다는 크기가 현저히 작다. 하나의 버켓에 여러개의 슬롯을 두는 이유는 서로 다른 두개의 명칭이 해쉬 함수에 의해 동일한 주소로 변환되는 경우 두 명칭을 같은 버켓에 저장하기 위해서이다.


해슁의 문제점

해슁을 하는 경우 서로 다른 두개 이상의 명칭이 해쉬 함수에 의해 동일한 주소로 변환되는 경우가 있다. 이 현상을 "충돌(collision)"이라 한다. 충돌이 자주 발생하면 탐색 시간이 길어지는 등 성능이 저하되므로 해쉬 함수를 수정하거나 해쉬 테이블의 크기를 적절히 조절해 주어야 한다.

충돌이 발생한 경우 같은 버켓에 있는 다른 슬롯에 명칭을 저장하면 된다. 그러나 슬롯의 갯수만큼 충돌이 생기면 빈 슬롯이 소진되어 오버플로우(overflow)가 생길 수 있다. 오버 플로우가 발생하면 해슁에 의해 원하는 명칭을 찾을 수 없게 되므로, 오버 플로우를 해결하기 위한 방법이 고안되어야 한다.


해쉬 함수

해쉬 함수는 입력된 키값을 해쉬 테이블의 주소로 변환시켜주는 함수이다. 해쉬 함수는 가능한 충돌이 적게 발생해야 하므로, 함수의 출력값이 해쉬 테이블의 주소 영역 내에서 고르게 분포되어야 한다.

주로 사용되는 해쉬 함수는 다음과 같다.

mid-square 함수
키 값을 제곱한 후에 중간에 몇 비트를 취해서 해쉬 테이블의 버켓 주소로 만들어 준다. 키 값을 제곱하면 결과값의 중간 비트들은 키 값의 모든 비트들로부터 영향을 받으므로 버켓 주소가 고르게 분산될 가능성이 높다.
다음은 검색 키가 5비트로 구성되어 있고, 버켓 주소도 5비트로 구성된 해쉬 테이블에서 mid-square 함수 해슁을 하는 예를 보여준다.

division 함수
키 값을 해쉬 테이블의 크기로 나누어서 그 나머지를 버켓 주소로 만들어준다.
버켓 주소 = 키 값 % 해쉬 테이블 크기
folding 함수
키 값을 버켓 주소 크기만큼의 비트수를 가지는 부분으로 분할한 후, 분할된 것을 합산하여 버켓 주소를 만들어준다.
다음 그림은 키 값이 "12321631067"이고 버켓 주소가 10진수 3자리로 구성되어 있는 경우의 예를 보여준다.


오버플로우의 해결방법

선형 개방 주소지정법

선형 개방 주소지정법은 충돌이 발생한 경우에 그 위치로부터 비어있는 다른 버켓을 찾아 그곳에 명칭을 저장하는 방법으로서 선형 탐색법(linear probing)이라고도 불린다.
선형 개방 주소지정법을 이용할 때 해쉬 테이블은 1차원 배열 형태를 가진다.

명칭을 삽입하는 경우

  1. 해당 키 값을 가지는 명칭을 해쉬 함수로 변환하였을 때 그 위치의 버켓이 비어있으면 삽입한다.
  2. 버켓이 비어있지 않으면 그 위치부터 해쉬테이블을 차례로 조사하여 빈 버켓을 찾은 후 그 위치에 명칭을 삽입한다.
  3. 해쉬 테이블의 끝까지 빈 버켓이 없으면 테이블의 처음부터 원래 삽입한 위치까지 빈 버켓을 찾는다.
  4. 빈 버켓이 전혀 없는 경우는 명칭을 삽입할 수 없으므로 오류 발생을 고지한다.

명칭을 검색하는 경우

  1. 해당 키 값을 가지는 명칭을 해쉬 함수로 변환하여 얻어진 위치에 원하는 명칭이 들어있는지 조사한다.
  2. 원하는 명칭이 없다면 삽입시에 충돌이 발생한 것이므로, 그 위치부터 차례로 해당 명칭을 검색한다.

선형 개방 주소 지정법의 장단점

장점

  • 해쉬 테이블의 구조가 간단하다.

단점

  • 충돌이 발생한 경우에, 최악의 경우 해쉬 테이블 전체를 검색해야 하는 경우가 발생하므로 비효율적이다.


체인 이용법

충돌이 발생한 경우에 동일 버켓에 들어가야할 명칭들을 연결 리스트로 저장해 두는 방법이다.

체인 이용법을 이용할 때는 해쉬 테이블의 각 버켓이 하나의 연결 리스트가 된다.

명칭을 삽입하는 경우

  1. 해당 키 값을 가지는 명칭을 해쉬 함수로 변환하여 주소를 얻어낸 후 그 위치에 있는 연결 리스트에 삽입한다.

명칭을 검색하는 경우

  1. 해당 키 값을 가지는 명칭을 해쉬 함수로 변환하여 주소를 얻어낸다.
  2. 얻어진 위치의 연결 리스트에 노드가 한개 뿐이면 충돌이 발생하지 않은 것이므로 그 값을 읽어온다.
  3. 얻어진 위치의 연길 리스트에 노드가 두개 이상이면, 그 연결 리스트의 노드들을 차례로 검색하여 그 값을 읽어온다.

체인 이용법의 장단점

장점

  • 충돌이 발생한 명칭들만을 연결 리스트에서 검색해 주면 되므로 속도가 빠르다.
  • 삽입 가능한 명칭의 수가 해쉬 테이블 크기에 영향을 받지 않는다.

단점

  • 구조가 복잡하고, 기억장소 소모량이 많다.


동적 해슁

동적 해슁의 필요성

항목과 삽입과 삭제가 빈번히 발생하는 응용에는 정적 해슁이 적합치 적합치 못하다. 고정된 크기의 해쉬 테이블을 사용하는 정적 해슁의 경우, 삽입이 많아지면 테이블이 가득차서 사용이 불가능하고 삭제가 많아지면 많은 공간이 사용되지 않으므로 낭비가 발생한다. 이러한 응용에 적합하도록 고안된 것이 동적 해슁(dynamic hashing) 또는 확장성 해슁(extendible hashing)이다.

동적 해슁의 구성

동적 해슁을 위해서 해쉬테이블 대신에 트라이(trie)라는 자료구조를 이용한다.

트라이는 입력키의 각 비트값에 따라 2개의 가지로 분기되도록 만들어진 것이다. 위의 그림은 두 비트의 키를 가지는 트라이다. 키 값의 우단 비트(LSB)부터 검사하여 그 값이 0이면 첫번째 레벨에서 위쪽으로 분기하고 , 1이면 아래쪽으로 분기한다. 그 다음에는 오른쪽 끝에서 두번째 비트를 검사하여 그 값이 0이면 두번째 레벨에서 윗쪽으로 1이면 아래쪽으로 분기한다. 실제 항목들이 저장되는 부분은 해쉬 테이블의 버켓처럼 여러개의 슬롯이 존재한다. 동적 해슁에서는 버켓을 페이지라 부른다.

동적 해슁에서의 검색

위에 나열된 것처럼 6비트로 이루어진 6개의 명칭이 트라이에 저장되어 있다고 하자. 트라이의 말단에 있는 페이지에는 두개의 슬롯이 있다. 이러한 경우 4개의 페이지에 명칭들을 저장할 수 있으므로 두비트의 키가 필요하다.

검색을 위해서 우단의 두 비트를 검사하여 비트값에 따라 위, 아래로 분기한다. 해당 페이지를 찾은 후 페이지내의 스롯에 저장된 명칭들을 차례로 비교하여 원하는 명칭을 검색해낸다.

동적 해슁에서의 삽입

<삽입전>

위의 그림에 나온 트라이에 "110101"이라는 명칭을 삽입해 본다. 먼저 명칭이 삽입될 페이지를 찾는다. 우단의 두 비트를 이용하여 검색하면 3번 페이지가 해당되는데 이 페이지에는 더이상 명칭을 저장할 슬롯이 없기 때문에 둘로 분할해야 한다. 페이지가 분할되어 레벨이 한 단계 깊어지면 검색에 필요한 비트 수가 한개 늘어난다.

<삽입후>

반응형
반응형

참고)

http://en.wikipedia.org/wiki/Convex_hull

요렇게 stage에 무작위로 찍은 점들이 있다치자.

원기둥 쯤으로 생각하고 원기둥들을 실로 감싸면 아래 그림처럼 될 것이다. 가운데쯤 있는 원기둥은 강력접착제로 붙이지 않고는 차례가 돌아가지 않을 것이다.

위 원기둥이 그저 눈에 보이라고 그려 놓은 것이고 크기가 없는 점이라고 하면 다음과 같이 될 것이다.

오목한 부분이 없는 폴리곤 형태다. 그래서convex hull이라 부르는게 아닐까 싶다.

기하알고리즘들이 그렇듯 눈으로 보면 대충 알겠는데 이걸 코드로 바꾸려면… 어렵다.

Convex hull관련 알고리즘도 굉장히 여러가지가 있고 그럭저럭 쉽게 이해한 Graham Scan 방식을 설명해보려한다.

  1. 첫 시작점을 어떤걸로 정할지
  2. 둘러칠 점의 순서는 또 어떻게 정할지
  3. 요 점이 내부에 포함될 점인지 아니면 외곽에 해당하는 점이지는 어떻게 알아낼지

3가지가 핵심이다.

GrahamScan은 시작점과 다른 모든 점이 이루는 각도를 가지고 가장 작은 각에서 가장 큰 각을 순서로 선을 잇는다.

시작점은 가장 상하좌우 가장 끝에 있는 걸 고른다.

이렇게 얻은 각도를 로 양의 정수로 고쳐서 소팅하거나 (Math.atan2의 경우 마이너스 값이 나올 수도 있다.)

양의 각도, 음의각도 두개를 따로 소팅한 뒤 합치는 것도 방법일 수 있겠다.

플래시에서는 맨 좌측에 있으면서 가장 위에 있는 녀석을 고르는 것이 각도 구할 때 유리할 것이다. 음의 각도가 나오지 않는다.

선을 이어나가는 방향은 ccw나 cw 한방향을 만족시켜야 되는데... 무슨 말인가 하면:

1번그림처럼 시계방향으로 회전한 다음 시계반대방향으로 회전하게 되면 오목한 부분이 생긴다는 말이다.

2번에서 시계반대방향이 생긴경우 해당 점을 제외시킨다.

3번 그림처럼 다음 점과 방향검사를 한다. 처음 시도한 방향과 같으면 선을 연결한다.

이러한 방향은 두 벡터의 외적으로 쉽게 알 수 있다. 외적의 값이 0이면 collinear(공선벡터: 스칼라 값은 다르더라도 방향이 같다)이고 음수이면 왼쪽(cw:시계방향)으로 돌고 양수이면 오른쪽(ccw:반시계방향)으로 돈다.

그럼 시작점을 찾아보자.

시작점과의 각도를 구한다. (시작점이 원점이 되도록 한다.)







http://blog.naver.com/helloktk/80050600334




주어진  convexhull (counterclockwise)에 임의로 한 점을 추가하여 새로운 convexhull 를 구성하는 procedure 다. 작동의 원리는 아래의 그림과 같다. 만약에 주어진 점이  convexhull 의 내부점이면 원래의 convexhull이 반환된다.

 

 

std::vector<CPoint>
chull_point_merge(CPoint Q, std::vector<CPoint>& chull) {
    std::vector<CPoint> out;

    int n = chull.size();

    if(n < 2) {
        out = chull; out.push_back(Q);
        return  out;
    }

    bool pushed = false;

    bool prev = isright(chull[n-1], chull[0], Q)  ; 
    for(int i = 0; i < n; i++) {
        bool curr = isright(chull[i], chull[(i+1)%n], Q)  ;
        if(prev) {
            if(curr) {
                if(!pushed) {
                    out.push_back(Q);
                    pushed = true;
                }
                continue;
            }
            else if(!pushed) {
                out.push_back(Q);
                pushed = true;
            }
        }
        out.push_back(chull[i]);
        prev = curr;
    } 
    return out;
}

//C is on the right side of segment AB ;
bool isright(CPoint A, CPoint B, CPoint C) {
    B -= A; 
    C -= A;
    return (B.x * C.y - B.y * C.x) < 0   ;
}


반응형
반응형

어딘가에서 뽑아온 그림 기억이 안남..;


반응형
반응형

휴먼고딕 부호가 작아서 확 눈에 띄지 않는다

한컴돋음 : 글씨도 크지 않아 한페이지에 많이 담을 수 있고 - 부호도 길어서 잘보인다


R 219 G251 B 227

색상90 채도192 명도 221


로 설정하면 잘보임...


나만그런가? ㅎ

반응형
반응형

http://cloudkiss.tistory.com/6

ECS P67H2-A3(B3) 오버클럭 후 클럭 고정 방법
기타 | 2011/08/28 15:23


AMD의 차기 CPU 불도저를 기다리길 대략 4개월.
출시가 임박하고 나서야 하나씩 나오는 B2 스테핑 정보를 보고나서야 확실히 불도저에 대한 기대를 접을 수가 있었다.
그간 B0, B1 스테핑 벤치 정보도 많이 나왔지만, B2는 달라지겠거니 기대했었는데..
어쨌든 그런 김에 오랫동안 유용하게 썼던 헤카를 처분하고 나도 인텔로 넘어갔다.

CPU는 i5-2500K로 바꾸고, 램은 삼성 DDR3 4G로 바꾸고, 보드는 ECS P67H2-A3(B3)로 바꾸었다.
시밤 이 보드가 모든 문제의 시발점이다.

2500K의 국민 오버가 4.5G는 파코즌들의 말을 곧이 곧대로 믿지는 않았지만, 항상 토요일마다 로또 가게 앞이 붐비는 것처럼, 내 CPU는 찍어주겠지라는 마음으로 오버를 시작했다.




아아.. 샌디느님 화끈하게 한큐에 4.5G를 찍어주셨습니다.
바이오스에서 배수만 x45로 주고 부팅했는데도 윈도우 진입부터 파이까지 잘 굽는다.

쉽게 성공!

이 될 줄 알았는데...

본격적인 스트레스 테스트를 위해 링스와 프라임을 준비하고 링스를 돌렸더니, 어라? 클럭이 떨어진다.



어, 어? 하는 사이 클럭은 계속 떨어져 이내 3.4G를 찍고서야 멈춰선다.


LinX가 뭔가 잘못된 것일 수 있단 생각에 프라임을 돌려봤지만, 결과는 역시 마찬가지. 클럭 다운 폭이 좀 줄긴 했지만, 어쨌든 4.5G 와는 상당히 먼 거리였다.





< 이렇게 CPU만 쓰면 계속 떨어진다>





< 그러다 CPU가 놀면 클럭은 급 상승>



대체 뭐 때문에 이런 지 도통 알 수가 없었다. 계속 인터넷을 검색하고, ECS 홈페이지에 문의 글을 남겨봐도 원하는 대답이 나오지 않았다.

그러다가 이것이 배수 하락 현상이라는 것을 오늘 알아냈다. 그리고 이 문제를 해결 하기 위해선 Core Curruent Limit 라는 옵션에서 수치를 더 높게 주어야 하는데, 망할 ECS 바이오스에는 이 항목이 없다.

오버는 실패인가? 대체 이 보드로 오버가 가능하다는 사람들은 뭔가? 가격대 성능비가 괴물이라는 소리를 하기에 구매한 거였는데, 그냥 파코즈에서 오버 성공한 보드로 심심찮게 보이던 TP67XE나 살껄..

온갖 후회들이 물 밀듯 밀려 왔으나, 시부럴 늦었다고 생각할 때가 정말 늦었다는 명수형 말처럼 이미 질러버린 것을 어찌하랴란 생각에 다시 구글을 뒤지기 시작.
이상하게도 국내에선 이 보드로 오버를 성공했다는 글은 많지만, 바이오스 설정에 대한 가이드는 하나도 존재하지 않았다. 아니, 몇 개가 있긴 했는데, 그 설정대로 해보았자 배수 하락 현상은 여전했다.
그래서 국외로 시선을 돌려 되도 않은 영어 실력으로 외국 오버클러커들이 설명해 준 방법대로 따라해 보았으나 역시 실패. 배수는 계속 풀렸다.

아.. 시밤 때려치자. 3.3G도 충분히 빠르다. 무슨 컴퓨터로 하루 종일 인코딩만 할 것도 아니고..

슬슬 자기 합리화가 되어갈 시점 발견한 것이 ECS에서 제공하는 eOC라는 윈도우 오버클럭 툴!
윈도우에서 쉽고, 파격적인 오버클럭을 도와준다기에 서둘러 eOC 설치.


개뿔... 전압 설정이라면 굳이 윈도우에서 할 필요는 없단 말이다.

마지막 희망의 연소로 모든 것을 포기하려던 찰나, 혹시나 하는 생각에 메인보드 설치 시디 안을 뒤져 보았다.
그러다 발견한 폴더. XTU.
익스트림한 채널 XTM과 뭔가 유사한 포스를 풀풀 풍기는 XTU.
폴더 안에 보니 html 형태의 릴리즈 노트가 있기에 살펴보니, 이것이 바로 인텔 익스트림 튜닝 유틸리티.
짝퉁 튜닝 유틸리티인 eOC와는 비교를 거부하는 정품의 포스.
일단 깔고 봤다.



해냈다. 해냈어! XTU가 해냈어!

그토록 바이오스에서 찾아 헤매던, Core Current Limit 항목이 그곳에 있었다.
위 스크린샷에서는 120.000A라고 표시되어 있지만, 본래는 97.xxxA 였다.

이곳에서 Core Current Limit를 120으로 올려주고(정보를 찾아 헤매던 중, 120~130 사이로 해주는 게 좋다는 말을 본 것이 기억났다), Turbo Boost Power 는 처음엔 둘 다 150을 줬다가 천천히 내려서 지금은 120으로 해두었다.

Core Current Limit에서 120을 준 후에야 배수 하락 현상은 사라졌다.

어떤 문제를 해결 할 때 항상 그렇듯, 답은 너무나 쉬운 곳에 있다. 하지만 그것을 찾아내기까지가 너무 어려울 뿐.

혹시나 나와 같은 문제로 고민하는 사람들이 좀 더 그 답을 쉽게 찾을 수 있도록, 밑에 오버클럭 설정을 남겨둔다. (사실 도움을 구하려는 분들에게는 이 아래부터가 본문임요)

바이오스에서의 설정


제일 중요하다. 이거 하나면 바이오스에서의 설정은 끝이라고 봐도 된다.
M.I.B III 탭의 CPU 설정에서 IA Core Current 옵션을 기본 설정인 Normal 에서 Max로 바꿔주어야 한다. 이것이 바로 ECS P67H2-A3(B3) 보드에서 지긋지긋한 배수 하락의 원인인 Core Current Limit와 연관된 항목이다. 개인적으로 추측컨데, 이 바이오스에서는 Limit 수치를 직접적으로 정할 수 없는 대신에 기본 수치인 97A를 기준으로 상대적으로 얼마쯤 적용할 것인지 결정하는 항목인 것 같다. 때문에 이 수치가 Nomal이면 XTU에서 Limit 수치를 상당히 줘도 그 절반 정도 밖에 적용이 안 되는 것이다. 따라서 이 수치를 MAX로 바꿔줄 필요가 있다.
기타 나머지 옵션은 자유다. 여기서 코어 배수를 바꿔줄 수도 있지만, 어차피 XTU에서 바꾸면 바이오스에 있는 배수도 알아서 바뀌더라. 그러니 굳이 바이오에서 건드릴 것을 찾자면, XTU에 없는 옵션들만 필요에 따라거 건드릴 것을 추천한다.

XTU에서의 설정
오버수치는 먹는데, CPU 사용량이 증가할 때 배수가 하락하는 것은 TDU와 Core Current Limit의 문제다. 때문에 배수 하락을 막기 위해선 아래 그림에 표시된 빨간 네모 표시를 잘 조절해야 한다.
이것은 CPU마다 편차가 있는 것이니 오버하는 개인이 알아서 하길 바란다.


그리고 XTU에서 꺼보면 알겠지만, SpeedStep과 Turbo Boost는 Disable 할 시, 클럭오버 자체가 안 되므로, 절대로 꺼선 안 된다.

이상으로 ECS P67H2-A3 (B3) 보드 사용 시 발생하는 CPU 배수 하락에 관련된 포스팅을 마친다.

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

이후 위 설정에서 링스 테스트를 해보니 에러가 발견되어서, 최종적으로 링스를 통과한 세팅을 덧붙인다.

반응형
반응형

http://cafe.naver.com/ydg10/28

Subversion (SVN) 설명,사용 이유 |

안녕하세요 2조 10 플밍 오재윤 이라고 합니다.

이 글을 쓰는건 다름이 아니라 프로젝트를 진행하실때 괜찮은 툴이 있어 소개시켜드리려고 합니다

들어보신분도 계시고 써보신분도 계시고 저보다 더 잘 아시는 분도 계시겠지만 염치 불구하고 몇자 적어봅니다.

제가 소개해드릴것은 Subversion 흔히 SVN 또는 거북이 라고 불리우는 소프트웨어 버전 관리 시스템입니다.

오픈소스이다보니 무료로 다운받으셔서 사용하실수 있습니다

그리고 SVN 말고도 CVS , Visual SourceSafe 등 많은 관리 시스템들이 있지만..

SVN 과 CVS 와의 차이점을 설명드리겠습니다.

이 2가지 차이를 설명드리는것은 가장 많이 쓰이는 툴이기 때문입니다.

SVN 은 CVS 의 업데이트 판이라고 생각하시면 쉽습니다.

SVN 이 CVS 와 비교했을때의 장점은

  • 원자적으로 쓰기를 지원하므로, 다른 사용자의 쓰기와 엉키지 않는다.
  • 이름을 바꾸거나, 복사하거나, 파일을 지워도 리비전 기록을 유지한다.
  • 이진 파일의 경우 한번 저장한 후 변경될 경우 차이점만 저장하기 때문에 저장소를 효율적으로 사용할 수 있다.
  • 디렉터리도 버전 관리를 할 수 있다. 디렉터리 전체를 빠르게 옮기거나 복사할 수 있으며, 리비전 기록도 그대로 유지한다.
  • 소스 저장고의 크기에 상관 없이 일정한 시간 안에 가지 치기(branching)나 테그 넣기(tagging)를 할 수 있다.
  • 소스 저장고로의 접근이 최적화되어 있으므로, 소스 저장고에서 필요 없는 네트워크 트래픽을 줄일 수 있다.

    (출처 : Wiki)

    등이 있습니다

    그럼 왜 이 SVN 이나 CVS 같은 버전 관리 시스템을 사용하느냐.. 하실수도 있습니다

    그 이유는

  • 무언가 잘못되었을 때 복구를 돕기 위하여
  • 프로젝트 진행 중 과거의 어떤 시점으로 돌아갈 수 있게 하기 위하여
  • 여러사람이 같은 프로젝트에 참여할 경우, 각자가 수정한 부분을 팀원 전체가 동기화하는 과정을 자동화하기 위하여
  • 소스 코드의 변경 사항을 추적하기 위하여
  • 소스 코드에서 누가 수정했는지 추적하기 위하여
  • 대규모 수정 작업을 더욱 안전하게 진행하기 위하여
  • 가지내기(Branch)로 프로젝트에 영향을 최소화 하면서 새로운 부분을 개발하기 위하여
  • 접붙이기(Merge)로 검증이 끝난 후 새로이 개발된 부분을 본류(trunk)에 합치기 위하여
  • 많은 오픈 소스 프로젝트에서 어떠한 형태로든 버전 관리를 사용하고 있으므로
  • 코드의 특정 부분이 왜 그렇게 쓰여 졌는지 의미를 추적하기 위하여

    (출처 : Wiki .. 제가 적을수도 있지만 그것보다는 Wiki 가 다양하게 설명해주어서 적어봅니다.)

    등이 있습니다

    일반적인 사용방식 개념은

  • 갑돌이가 어떤 File을 저장소(repository)에 추가(add) 한다.
  • 추가되었던 File을 갑돌이가 인출(Check out) 한다.
  • 갑돌이가 인출된 file을 수정한 다음, 저장소에 예치(Commit) 하면서 설명을 붙인다.
  • 다음날 을순이가 자신의 작업 공간을 동기화(Update) 한다. 이 때 갑돌이가 추가했던 file이 전달된다.
  • 을순이가 추가된 file의 수정 기록(Change log)을 보면서 갑돌이가 처음 추가한 file과 이후 변경된 file의 차이를 본다(Diff).

    (출처 : Wiki)

    입니다

    아마 빨간색으로 표현한 부분이 가장 중요하고 가장 자주 쓰일것입니다.

    그럼 다음 글에서 설치법,사용법을 안내해 드리도록 하겠습니다.


  • http://a.tk.co.kr/456


    소스세이프(Source Safe)보다 좋은 버전관리 프로그램 Tortoise SVN 을 소개해드립니다.

    홈페이지 : http://tortoisesvn.net/

    버전관리를 통한 소스 관리의 중요성

    프로그래머, 개발자라면 반드시 소스의 버전관리를 해야 합니다. (선택이 아니라 필수입니다.)

    소스관리 소프트웨어를 사용하는 대표적인 이유는 다음과 같습니다.

    - 백업

    - 팀 프로젝트 (팀원과 공통 소스 개발)

    - 잘못 만들어진 소스 복구

    저(Kyuseo) 역시 예전에는 MS 사의 소스세이프(Source Safe)를 10년 가까이 사용하였으나 최근 3년간 Tortoise SVN을 사용해본 결과 이제는 소스세이프를 사용하는 프로젝트는 손까락도 대기 싫습니다. 업무 효율상 20%~30% 이상 이득을 보았다고 생각합니다.

    특히 프로그래머, 팀장님들은 꼭!!! SVN 을 적극 활용하시기를 권해드립니다.

    tortoise( 터틀스, 거북이) SVN 을 사용해야하는 이유

    - 2명 이상의 작업자가 코드 수정이 가능

    - 지능적인 자동 Marge

    - 무료 공개 소스 프로그램

    - 지속적인 업데이트

    - 한글 지원

    - 탐색기 기반

    - 모든 프로그램 호환 (소스코드 뿐만이 아니라 그래픽, 기획자도 사용하기 편리함)

    - 프로그램의 안정성

    - 세세한 옵션 조정 가능

    - 전폭적인 업데이트 로그 관리

    스크린샷


    http://a.tk.co.kr/529

    개요..

    터틀스 SVN 을 약 3년간 사용하면서 소스세이프보다 매우 만족하면서 사용을 하였습니다.

    하지만 최대의 문제점은 잦은 하드 디스크 엑세스로 인한 전체적인 컴퓨터 성능의 저하로 불편함을 느꼈는데 이것을 조금이나마 해소할 수 있는 터틀스 SVN(TortoiseSVN) 속도 향상 방법에 대하여 소개해드립니다.

    로그캐싱을 중단한다 .

    설정 -> 로그캐싱 ->로그캐싱사용 불가로 설정

    로그 보기의 속도가 느려지기는 하지만 하드 엑세스를 하지 않아 전반적인 성능이 올라갑니다.

    (Kyuseo 가 추천합니다.)

    아이콘 상태 캐시를 중단한다 .

    설정 -> 아이콘 오버레이 -> 상태캐시 없음으로 설정

    아이콘 자동 갱신을 사용하지 않아 상태 표시가 어렵기는 하지만 하지만 하드 엑세스를 하지 않아 전반적인 성능이 올라갑니다.

    (개인적으로는 추천하지 않습니다.)


    SVN 병합기능 : http://cafe.naver.com/jquerymytour.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=328&

    반응형
    반응형

    http://blog.naver.com/humanist23/90027831935




    오버클럭은 말 그대로 CPU나 램 등 반도체의 작동 속도를 강제로 빠르게 하는 것으로 돈을 들이지 않고 성능 높이는 하나의 방법이다.

    그래픽카드는 PC게임이 3D그래픽(영화, 기타등등)으로 나오는 요즘. PC전체의 성능을 좌우하는 중요한 부품이 그래픽카드 이다.

     

    그래픽카드 오버클럭 방법은 두가지가 있으며, 다음과 같다.

    1.GPU(그래픽 프로세서)클럭의 오버클럭

    2.메모리 클럭의 오버클럭

     

    이 두 부품의 클럭이 그래픽카드의 성능을 대변하므로 이를 높여 성능을 향상시킬 수 있다.

    출시되는 대부분의 그래픽카드는 GPU와 메모리 클럭이 다르다.

     

    메모리는 대부분 DDR SD램을 사용하고 있어 실제 표시 클럭의 2배로 작동한다.

    이건 그래픽카드 구조상 GPU보다는 메모리쪽에 더 많은 부하가 걸리기 때문이다.

    오버클럭 효과를 높이기 위해서는 GPU보다는 메모리클럭을 올리는 것이 더 확실하다.

     

    그래픽카드 오버클럭이 가능한 것은 프로세서와 마찬가지로 GPU와 메모리에 마진율이 적용되기 때문이다.

    반도체를 설계할 때는 목표동작 주파수(클럭)를 정하고 설계한다.

    그러나 실제 제조 공정을 거쳐 실리콘 웨어퍼 상에 칩을 만들면 정확히 목표한 클럭대로 칩이 만들어지는 것은 아니다.

    따라서 그래픽카드 제조사에서는 이 칩을 테스트해 일정한 마진을 두고 안정적으로 동작하는 클럭으로 마킹해 출시한다.

    이 과정에서 발생하는 마진으로 인해 오버클럭이 가능한 것이다.

     

    여기서 설명하는 엔디비아 그래픽카드의 오버클럭 방법은 엔디비아에서 제공하는 nTune 라는 유틸리티를 사용하는 것으로, ForceWare 1xx.xx 대 버전의  그래픽카드 드라이버에서 할 수 있는 방법이다.

     

    참고로 nTune 는 엔디비아 메인보드용 유틸리티로서 엔디비아 메인보드를 사용하고 있다면, 시스템에 대한 좀더 폭넓은 정보(메인보드 온도 등등) 및 CPU 오버클럭 그리고 안정성 검사 등을 할수 있는 유틸리티이다.

     

     

    아래 링크를 클릭하여 nTune 를 다운받는다.

      2007년 9월 19일 NVIDIA 홈페이지에 업데이트된 NVIDIA nTune v5.05.54.00 정식 버전

      WinXP/WinXP 64Bit/WinVista/WinVista 64Bit OS용

      다운로드   (파일서버가 미국에 있는것이라 다운받는데 시간이 좀 걸린다.)

     

     

    프로그램을 설치하고 바탕화면 -> 마우스 오른클릭 -> NVIDIA 제어판 선택

    혹은 시작 -> 제어판 ->  NVIDIA 제어판 

     

     

    유틸리티가 정상적으로 설치 되었다면 아래의 이미지와 같이 성능 이라는 탭이 생성된다.

     

     

    왼쪽창에서 "최종 사용자 라이센스 계약 동의"를 선택하고 오른쪽 창의 "동의" 버튼을 클릭한다.

     

     

    성능 탭아래 안보이던 메뉴가 몇개 추가된 것을 확인할 수 있으며, GPU 설정 조정 메뉴를 선택한다.

    엔디비아 메인보드를 사용하고 있다면, GPU 설정 조정 아래 나열되어 있는 메뉴에서 시스템 정보를 얻거나 시스템 설정, 시스템 안정성 테스트 등을 할 수 있다.

    오른쪽 창의 GPU 클럭 설정 에서 오버클럭을 할 수 있으며, 그 아래 GPU 팬 설정이 보인다. 

    아쉽게도 필자의 그래픽카드에는 GPU 팬 컨트롤러가 없는 제품이다. 따라서 GPU 팬 설정 항목이 활성화 되지 않는다.

    (GPU팬 전원이 2핀이라면 GPU 팬 컨트롤러가 없다고 보면 된다.)

     

     

     

    오른쪽 창에서 사용자 정의 클럭 주파수 선택

     

     

    마우스로 Core클럭바와 Memory클럭바를 오른쪽으로 이동시켜 오버클럭을 설정하며, 정밀조정은 키보드의 왼쪽 혹은 오른쪽 방향키 를 사용한다.

    (참고. 안정적인 오버클럭율은 기본클럭의 10% ~ 20% 이며, 과도한 오버클럭은 오히려 시스템성능을 저하 시키기도 한다.)

     

     

    오버클럭값을 정했다면, 테스트 버튼 클릭


     

     

    테스트가 끝날때 까지 기다리자.

     

     

    테스트를 통과 했다면 다음과 같은 메시지 창을 볼수가 있다.

    확인 하고 시스템에 적용 시키자.

     

     

     

    오버클럭후 평소에 자주하던 3D 게임을 최소 3~5번 정도 실행해 보고 이상이 없어야 한다.

     

     




    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
    쉐이더 기법  (0) 2012.11.02
    셰이더 내장함수  (0) 2012.11.02
    smoothstep  (0) 2012.11.02
    반응형

    블렌더, 무료라네

    http://cafe.naver.com/blender3d/

    오랫동안 여러가지 3D 소프트웨어의 기능을 학습하고 사용해온 카페지기의 경험에 비추어볼 때 블렌더(www.blender.org)의 기능은 참으로 놀라웠습니다. 간단히 정리하자면 블렌더는 3D 애니메이션을 만들기에 완전한 All-IN-ONE 패키지입니다. 모델링과 쉐이딩, 애니메이션, 다이나믹스, 렌더링은 물론이고 정교한 컴포지팅까지 포함하고 있으며 간단한 사운드 작업까지 가능합니다. 이런 패키지가 용량이 15M라는 것이 놀라우며 거기에 무료라는 것이 더욱 놀랍습니다. 2000년 말에 전 세계적으로 공식 홈페이지에 등록한 사용자가 25만명이 넘었으니 대단한 반응이라고 할 수 있습니다(등록하지 않고도 얼마든지 합법적으로 사용할 수 있고 그런 사용자가 얼마인지는 추산되지 않는다). 이러한 장점에도 불구하고 카페지기에게 가장 큰 매력으로 다가온 것은 다른데 있습니다. 그것은 블렌더에 매우 훌륭한 게임엔진이 탑재되있다는 것입니다. 블렌더의 게임엔진은 노드 기반으로 이루어져 있어서 아티스트들 조차도 쉽게 학습하여 3D 게임을 제작할 수 있게 되어 있으며 물리엔진까지 포함하고 있습니다. 그리고 파이썬 스크립트를 지원하여 얼마든지 기능을 확장할 수 있습니다. 이 모든 것이 무료이니 얼마나 놀랍습니까!

    블렌더는 매우 다양한 포맷의 데이터를 입출력할 수 있습니다. 썬-마이크로 시스템즈는 2008년에 인터넷을 활용하는 자사의 네트워킹 기술과 블렌더를 활용하여 Big Buck Bunny라는 HD 버전의 애니메이션을 발표한 바 있습니다. 이 프로젝트는 PC만 가지고 있으면 이제 소프트웨어 비용에 대한 걱정없이 극장용 포맷의 3D 애니메이션을 제작할 수 있음을 증명하였습니다.

    이런 자유소프트웨어에 대한 가장 잦은 질문 중에 하나는 "이런 프로그램을 이용하여 만든 애니메이션이나 게임을 돈을 주고 팔거나 저작권을 내 것으로 주장하는데 어떤 제약이 있느냐?"는 것입니다. 답은 "전혀! 전혀! 없다."입니다. 이 질문을 벗어난 한 가지 제약이 있다면 블렌더라는 프로그램 자체를 개선하여 돈을 주고 팔려고 공개했을 때 다른 사람이 프로그램 소스를 보고 싶다고 말할 경우 그 사람에게 공개해야 한다는 것입니다. 그러나 이 글을 읽는 독자 중에 블렌더를 굳이 개선하여 팔려고 마음 먹는 분은 거의 없을 것입니다. 왜냐하면 블렌더는 매년 수 차례에 걸쳐 공식 협회를 통해 가장 훌륭하게 업그레이드 되고 있기 때문입니다.^^ 우리의 관심사는 만들어진 컨텐츠의 소유권입니다. 그것은 100% 만든 사람의 것입니다.

    자유소프트웨어의 소유권에 대해 법적으로 정확한 내용을 알고 싶다면 http://www.gnu.org/licenses/gpl-faq.ko.html의 "[영어]"부분을 참고하기 바랍니다. 결론으로 필자는 소프트웨어 비용의 부담없이 애니메이션과 게임을 만들고 싶은 개인이나 기업이 있다면 블렌더를 적극 추천합니다. 또한 애니메이션과 게임을 만드는 커리큘럼을 개설하고자 하나 소프트웨어 구매비용 때문에 부담을 느끼는 교육기관이나 대학이 있다면 블렌더를 사용하라고 적극 추천하고 싶습니다. 해피 블렌딩~^^

    <카페지기 소개>

    1993년부터 3D Graphic분야에서 일해오고 있다. 그동안 애니메이션, 영화특수효과, 드라마 특수효과, 건축 CG, 시뮬레이션 영상, 입체영상 등 다양한 분야의 경험을 쌓았으며 세 권의 Maya 학습서를 집필하였다. 더불어 2000년부터 교육분야에서 학생들을 가르치는 일을 겸해오고 있다. 2004년부터 3D게임의 Rendering분야 프로그래밍에 관심을 가지고 연구해오고 있으며 Z-Brush2.0 버전부터 많은 학습을 하였고 Z-Brush3.0 런칭 세미나에서 신기술 분야 강사로 초청되었다. 최근에는 오픈소스 진영의 자유소프트웨어 활용에 많은 노력을 기울이고 있다. E-mail : blender3d.co.kr@gmail.com

    반응형
    반응형

    http://fendee.egloos.com/2230359



    응용 프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다VC++(2005)

    원 제목: 응용 프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다
    "응용 프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다"
    실행파일을 실행했을때, 위와같은 에러가 발생했다면,


    이런 오류가 발생했을때.
    물론, 갖가지 상황들이 있을 수 있으므로, 만병 통치약은 아니겠지만,
    내가 접한 이 오류에 대한 분석내용을 올린다.
    Visual Studio C++ 2005 가 설치되어 있는 Windows XP pro 에서 만들어진 실행파일(콘솔응용)을
    아무것도 설치되지 않은(.Net Framework, C++ Runtime) Windows XP pro 에서 실행했을때,
    위와같은 에러를 만나게 되었다.
    여러가지 상황에서 테스트를 해보았는데,
    결론은 이러하다.
    여러가지 상황테스트.
    .Net Framework 만 설치하고 실행시켰을때, C++ Runtime 만 설치하고 실행시켰을때, 두가지 모두 설치하고 실행시켰을때,
    그러나, 모두 에러가 발생했다.
    그래서, 웹서핑도 해보고, 이것저것 해본결과.
    우선, 내 경우는, debug 모드로 만들어진 실행파일이었다.
    아래의 위치로 들어가보면,
    C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT (Visual Studio 2005의 경우)
    C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT (Visual Studio 2008의 경우)
    내경우에는 Visual Studio 2005 이어서 이것을 기준으로 설명하겠다.
    그 폴더에는 아래의 4개 파일이 있다.
    Microsoft.VC80.CRT.manifest
    msvcm80.dll
    msvcp80.dll
    msvcr80.dll
    이 파일을 실행파일과 같은 폴더에 위치시키면 된다.
    그런데, 문제는 그리 간단하지 않다.
    위의 파일은 Release 모드로 만들어진 실행파일의 경우 필요한 파일이다.
    만약, Debug 모드로 만들어진 실행파일이라면 아래의 파일이 있어야 한다.
    C:\Program Files\Microsoft Visual Studio 8\VC\redist\Debug_NonRedist\x86\Microsoft.VC80.DebugCRT (Visual Studio 2005의 경우)
    폴더안에 아래의 4개 파일이 있다.
    Microsoft.VC80.DebugCRT.manifest
    msvcm80d.dll
    msvcp80d.dll
    msvcr80d.dll
    즉, 이 파일들이 없어서 에러가 났다는 것이다.
    다시한번 주의하자, Debug 모드로 실행파일을 만든경우와, Release 모드로 실행파일을 만든경우 실행파일과 같이 넣어줘야할 파일이 틀리다.
    보면, 디버그모드(Debug) 의 파일들에는 Debug 가 붙거나, 파일이름에 d 가 붙어 있다.
    자신이 만든 실행파일이 어떤 모드에서 만들었는지 잘 기억했다가 필요한 파일로 첨부시키면 된다.
    그런데, 한가지 재미있는 점은,
    만약, Release 모드로 실행파일을 만들었다면, C++ Runtime 가 컴퓨터에 깔려있으면, 위의 파일들을 첨부하지 않아도 된다는 것이다.
    Debug 모드로 만든경우에는 C++ Runtime 가 설치되어 있어도 에러가 난다.
    그러나, 내가 만든 실행파일은, 정말 기초적인 코딩들만 들어있었다.
    그런데도, C++ Runtime 를 필요로 했다면, 이건 정말 기막힐 노릇이다.
    항상 프로그램을 누군가에게 주기전에 C++ Runtime 를 먼저 설치하라고 해야 한다는 말인데,
    누가 그걸 설치하겠는가.
    결국, 그냥 Release 모드로 만든후, dll 파일과 manifest 파일(총4개)을 실행파일과 함께 주는법 밖에는 없다는 결론이다.
    정말 지극히도 단순한 코딩인데도 말이다.(Debug 모드로 만든경우에는 48kbyte 이고, Release 모드로 만든경우에는 5kbyte 밖에 안되는데도 말이다)
    더 황당한것은, .Net Framework 2.0 을 설치했는데도 에러가 났다는 것이다.

    그리고, 위에서 말한 폴더들을 왔다갔다 하다보면,
    그 폴더만 있는게 아니고,
    Debug 관련 폴더의 경우,
    Microsoft.VC80.DebugMFC 폴더, Micorosft.VC80.DebugOpenMP 폴더가 있고,
    Release 관련 폴더의 경우,
    Microsoft.VC80.ATL 폴더, Microsoft.VC80.MFC 폴더, Microsoft.VC80.MFCLOC 폴더, Microsoft.VC80.OPENMP 폴더가 있는데,
    만약, 코딩에서 MFC 를 사용했다거나, 기타 다른 것들을 사용했다면, 각 폴더안의 파일들도 같이 넣어줘야한다.
    테스트결과, MFC 를 이용한 실행파일은 CRT 와 MFC 관련 폴더안에 있던 파일들을 모두 넣어줘야 에러가 나지 않았다.
    하여간, 뭐가 이리 복잡한겐가.
    TurboC++ 3.1(win) 에서 만든 실행파일은 기냥 돌아가더구만.
    Visual Basic 의 경우에도, dll 파일을 같이 동봉해야 하는 경우가 많아 이런 에러를 만날일이 많을것이다.
    듣기로는 델파이의 경우, 아예 실행파일에 dll 내용을 첨부한다던데,
    물론, 그렇게 하면 실행파일의 덩치가 커지는 문제점도 있지만, 이와같이 배포시 문제점은 완벽히 해결되지 않을까?



    <Visual Studio> 응용프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다. 해결방법 낙서장

    2010/08/27 19:14

    복사http://blog.naver.com/myrandy1/80114297241

    VS2005 이후로는 제작한 프로그램을 VS가 설치되지 않은 PC에서 실행할 경우

    구성이 잘못되어 응용프로그램이 실행되지 않는다는 에러가 뜨면서 실행되지 않는다.

    이유는 런타임 라이브러리를 exe파일에 내재하지 않고 dll파일을 이용하여 참조하기 때문에

    dll파일이 없거나 그 버젼이 다를 경우 에러가 발생된다.

    몇 시간의 삽질 끝에 드디어 위의 문제를 해결할 수 있는 방법을 알아냈다.

    1. 일반적으로 인터넷에 나온 방법은 다음과 같다.

    C:\Program Files\Microsoft Visual Studio 9.0\VC\redist

    C:\Program Files\Microsoft Visual Studio 8\VC\redist

    를 보면, 재배포 정보들이 있는데

    릴리즈 모드로 빌드된 프로그램은 x86, 디버그 모드로 빌드된 프로그램은 Debug_NonRedist폴더 에 들어있다.

    예로 든 실행exe파일은 디버그 모드로 빌드된 프로그램이므로

    C:\Program Files\Microsoft Visual Studio 8\VC\redist\Debug_NonRedist\x86\Microsoft.VC80.DebugCRT

    C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugCRT

    에 있는 파일들을 실행exe파일과 같이 배포하면 된다.

    배포해야 하는 파일에는 다음과 같은 것들이 있다.

    Microsoft.VC90(VC80).DebugCRT.manifest

    msvcm90(80)d.dll

    msvcp90(80)d.dll

    msvcr90(80)d.dll

    2. 하지만 실행이 되지 않았다. 보통 인터넷에 나와있는 설명에는 버전에 대한 설명이 없어서 정말 많이 해멨다...

    실행파일이 요구하는 dll파일의 버젼을 알아내는 방법은 다음과 같다.

    Visual Studio의 File -> Open -> File -> 실행 exe파일로 열면 메니페스트 디펜던시 정보를 얻을 수 있는데..

    메니페스트 정보를 더블 클릭하면 디펜던시 정보가 hex파일로 나온다.

    이를 마우스로 긁어서 메모판에 붙여 넣기를 하면 메니페스트 정보를 확인할 수 있다.

    癤??xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
    <requestedPrivileges>
    <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
    </requestedPrivileges>
    </security>
    </trustInfo>
    <dependency>
    <dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
    </dependency>
    <dependency>
    <dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
    </dependency>
    </assembly>

    해당 실행파일이 필요로 하는 정보는 Microsoft.VC90.DebugCRT (9.0.21022.8), Microsoft.VC80.DebugCRT (8.0.50727.762)임을 알았다.

    하지만, C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugCRT

    에 있는 dll 파일들의 버젼을 확인해보니까 9.0.30729.1였다. 알고보니 dll파일의 버젼이 하나라도 다르면 실행이 안되는 것이었다.

    우리가 필요한 dll버젼은 9.0.21022.8 이기 때문이 이를 찾아야 한다.

    이는 C:\Windows\winsxs 에서 찾을 수 있다.

    엄청나게 많은 폴더들이 있는데, 잘 찾아보면

    x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.21022.8_none_96748342450f6aa2 란 이름의 폴더가 있는 것을 볼 수 있다.

    이를 열어보면 우리가 찾는 9.0.21022.8 버젼의 dll파일들이 있음을 알 수 있다.

    dll의 버젼이 9.0.21022.8이므로 Microsoft.VC90.DebugCRT.manifest 파일도 수정돼야 한다.

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable></noInheritable>
    <assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    ....

    버젼이 9.0.30729.1로 되어 있으므로 이를 9.0.21022.8로 고친다.

    이렇게 해서 고친 매니페스트 파일과 런타임 dll을 실행exe파일과 함께 배포하면....

    VS가 설치되지 않은 PC 에서도 잘 동작함을 볼 수 있다.

    요약하면, 실행exe파일과 함께 배포해야 하는 파일은

    Microsoft.VC90(VC80).DebugCRT.manifest

    msvcm90(80)d.dll

    msvcp90(80)d.dll

    msvcr90(80)d.dll

    인데, dll의 버젼과 manifest파일에 명시된 버젼과 실행 exe파일이 요구하는 dll의 버전이 모두 일치해야 한다는 것이다.


    http://blog.naver.com/drvoss/20048402862

    Dependency Walker : http://www.dependencywalker.com/

    오늘 세미나에서 VS2008, 혹은 VS2008 feature pack에 있는 DLL들을 같이 배포 해야 하는가 라는 질문을 받았습니다. 아마도 클라이언트 PC에 2005로 컴파일된 바이너리와 2005 재배포패키지에 있는 DLL파일이 이미 배포되어 있는 상태이셨던것 같습니다.

    어플리케이션은 빌드 당시에 물었었던 DLL로 링크가 됩니다. 메니페스트 파일에 이러한 내용들이 링크타임에 기술이 되게 되며,운영체제에서는 이것들을 DLL 버전등에 기반한 side by side로 관리를 합니다.

    Windows 폴더에 가보시면 WinSxS 라는 폴더가 존재 하는데, 이 폴더에 보시면 같은 파일들이 각 버전별로 서로 분리가 되어 있습니다. 처음 보시는 분들은 시스템에 따라 엄청나게 많은 폴더에 있는 같은 파일에 당황스러우실 껍니다.

    Side by side를 고려 하지 않으면, 보통 exe파일과 같은 폴더에 있는 DLL을 사용할 것이라 잘못 생각할 수 있습니다.

    Dependency Walker exe 파일을 열면 아래와 같이 exe 파일이 물고 있는 DLL들의 목록이 보여 집니다.

    여기서 F9를 눌러 보면 각 DLL 들의 Full Path가 나오게 됩니다.

    아래 그림에서 보시는 것처럼 ALZip.exe은 커먼컨트롤 DLL winsxs 폴더에 저 경로의 것을 사용합니다. ALZip.exe가 있는 폴더에 다른 버전의 같은 DLL 파일을 복사해도 ALZip.exe winsxs에 있는 DLL 파일을 이용할 껍니다.

    제 컴퓨터에는 아래의 두개의 경로에 같은 커먼컨트롤DLL 파일이 존재 합니다.

    C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.6000.16386_none_87e0cb09378714f1

    C:\Windows\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6000.16386_none_5d07289e07e1d100

    디펜즈에서 보시는 것 처럼 첫번째 것을 사용하고 있는데, 두번째 것을 ALZip.exe와 같은 경로로 복사를 해도 여전히 winsxs 폴더에 것을 사용하고 있음을 확인하실 수 있습니다.

    exe파일의 경우 자신이 링킹될 때 참고된 DLL에 대한 메니페스트를 가지고 있고, 나중에나중에 될 때 가장 우선으로 자신이 링킹될 때 참고한 DLLside by side에서 선택합니다. 맞지 않는 DLL을 참고할경우 윈도 제어판 이벤트로그에 워닝이 남습니다.

    이번 VS2008에서 side by side정보가 변경되었습니다. 따라서, dll 파일을 같이 배포해 주셔야 맞습니다.

    재배포 패키지를 찾아서 배포해 주셔도 되고, 디펜즈에 있는 DLL 파일들을 복사해서 같이 복사해 주셔도 됩니다. 편한 방법을 선택하시면 됩니다.


    Dependency Walker 사용법

    http://notgivuphil.tistory.com/413

    [쥔장]--------------------------------
    전에는 idasm (visual studio.net) 을 통해서 dll을 확인했었는데, dependency walker 라는 프로그램이 있다는 사실을 알고서 이에 대한 사용방법이 있기에 스크랩을 해본다.

    하지만 봐도 잘 모르겠다는... ^^;
    --------------------------------------

    0 - Dependency Walker?

    쉽게 이야기해서 프로그래밍이 사용한 DLL 정보와 DLL에 들어있는 함수 중 사용한 함수를 보여주는 툴 (~)

    1- 프로그램위치 (VS 2005기준) – 편하게 아래쪽 그림 참고

    2- 그럼 실행해보자 ^0^

    (1) 연결된 DLL정보를 볼 프로그래밍을 선택하기 위해 아래 아이콘을 클릭한다.

    (2) 연결된 DLL 정보를 볼 프로그래밍을 선택한다

    < 참고로 난 안사람은 안다는 저 흐뭇한 게임의 속을 보려고 한다 *_ *(개인적취향에 존중을)>

    ! 프로그래밍을 선택하고 열기를 누르자!

    (3) 나온 화면

    잠시 아래 Warning은 무시하자.. 그냥 대충 보니 뭔가 미확인 DLL때문에 빠진 함수가 있다는 말인 것 같다.. 참고로 난 영어 못한다. 그래서 틀린 해석일 수도 있다는 점 밝힌다. ㅡㅡㅋ;

    (4) 자 사용된 DLL과 그 DLL에서 사용된 함수를 봐보자!

    딱 보니 이 흐뭇한 게임에 사용된 DX9 버전은 9_32 버전이라는 점과 함께 D3DXCreateEffect 사용된것 봐서 쉐이더 HLSL를 사용했다는 것이 보이군요 후후후

    (5) 자 그럼 내 프로그래밍에서 사용된 DLL는 어디에?

    파란색 네모에 있는 아이콘을 누르면 바로 DLL 있는 FullPaths 가 표시 된다.

    (6) 기타

    * 이외에도 여러 기능들이 있는 것 같으니 직접 한번 해보시길

    * Dependency Walker에 아쉬운점은 바로 아까 무시한 워닝을 출력하게 한 것처럼

    MS나 공식적인 DLL가 아닌 사용자가 만든 DLL (ex, 게임엔진의 DLL)는 표씨 하지 않는다는 점이다. (어떻게 생각해보면 당연한 것 일수도 있다.) -> 응? 아닌데 나오던데?

    (7) 끝으로 프로그램을 배포할 때 무슨 DLL이 사용되었고 어떤 DLL를 포함해야 할지 이 프로그램으로 먼저 파악하고

    아래 같은 상황이 생겨 당황해 하지 않기 바란다..

    반응형

    + Recent posts