2. 과제수행일지
조원소개
조장 김현호
소 속 C2 조 자료조사 박구남, 서상현
프로그래밍 박태원, 박가혜
과제수행기간 12 일 288 시간
주 제
연구제목 말의 여행
스택과 백트래킹을 이용해 장기판 위에서 말(馬)이 장기판 위를 한번 씩만 지나가는 길을 구
연구배경
현함으로써 스택과 백트래킹의 개념을 알고 그 사용법을 익혀 다른 분야에 응용할 수 있다.
참 고 자 료
참고 서적 C로 쓴 자료구조론(저자 : 이석호, 교보문고 2nd)
http://underclub.tistory.com/245 : 스택과 큐의 개념이해
http://blog.naver.com/hkn10004?Redirect=Log&logNo=20109128319 : 스택과 큐의 그림
참고 URL
http://madkid.info/15 : 백트래킹 알고리즘의 이해
http://makebob.tistory.com/18 : 중위연산식을 후위연산식으로 바꾸는 법
과제의 수행
첫째날 2012 년 3 월 29 일 목요일
회의 주제 조원 자기소개 및 역할분담, 과제 파악
조장 : 김현호
자료조사 : 박구남, 서상현
프로그래밍 : 박태원, 박가혜
이상과 같이 이번 프로젝트의 역할분담을 실시했습니다.
수업 이전에 역할분담이 진행되지 않았기 때문에 서로가 조사해온 자료들을 공유하고, 이
를 이용해 어떤 형태의 알고리즘을 작성할 수 있을지에 대해 중점을 두고 논의하였으며 주
로 스택과 큐의 개념을 조원들 모두가 이해할 수 있도록 하는데 초점을 맞췄습니다.
회의 내용 스택 : 입구와 출구가 하나인 자료구조로 후입선출의 형태를 가지고 있다.
(그림 출처 : http://blog.naver.com/hkn10004?Redirect=Log&logNo=20109128319 )
3. -Push : 스택에 자료를 삽입하는 것으로 삽입된 자료는 아래쪽부터 순서대로 쌓이게 된다.
-Pop : 스택에 담긴 자료를 빼내는 행위로 이때 빠져나오는 자료는 스택 꼭대기에 담긴 내용이다.
-스택의 본 의미는 ‘건초더미’라는 뜻으로 차곡차곡 쌓인 건초더미를 사용하기 위해 가장 윗부분을
집어내는 모습을 연상하면 위 개념에 대해 이해하기가 한결 쉬워진다.
큐 : 입구와 출구가 따로 존재하는 자료구조로 선입선출형의 형태를 가진다.
(그림 출처 : http://blog.naver.com/hkn10004?Redirect=Log&logNo=20109128319 )
-Put : 큐에 자료를 삽입하는 행위로 데이터가 쌓이는 방식은 스택과 동일하다.
-Get : 큐에 삽입된 자료를 빼내는 행위로 이때 빠져나오는 자료는 가장 앞쪽(front)에 담긴 내용이다.
-큐의 경우, 매표소에 줄을 선 행렬을 생각하면 연상하기가 한결 수월하다.
백트래킹 : 깊이우선탐색(DFS)방식으로 해를 찾는 방법으로 해를 찾을 때까지 모든 경우의
수를 탐색하는 방식으로 전진&후진을 거듭한다. 스택을 이용해 표현할 수 있으
며 미로찾기와 같이 이동 경로를 찾는 프로그램에 자주 사용되어 진다.
위와 같은 내용을 함께 공부하고, 알고리즘을 작성하기 위해 논의했으나 2차 배열을 이용
해 장기판을 구현하고, 배열을 이용해 말의 이동 경로를 표현하려고 한 아이디어 외에 어
떤 성과도 얻지 못한 채 회의가 끝났습니다. 다음 수업시간까지 각자가 스택과 큐, 백트래
킹에 관한 c언어 소스를 구해 직접 구현해보고, 다음 시간까지 알고리즘을 작성해 보기로
하였습니다.
스택과 큐, 백트래킹에 관한 개념은 알고 있으나 이를 응용할 방법을 찾지 못했습니다.
각자가 분담한 역할에 따라 프로그램팀은 직접 배열을 이용한 큐와 스택 소스를 작성해보
반성
기로 했으며 조장과 자료조사팀은 이와 별개로 백트래킹을 이용해 작성된 다른 소스들을
찾아 과제를 해결할 방법을 찾아보기로 했습니다.
둘째날 2012 년 4 월 3 일 화요일
회의주제 스택과 큐, 백트래킹을 이용한 알고리즘 작성
회의내용
서로가 조사해온 내용을 바탕으로 알고리즘을 만들기에 앞서 이차배열을 대신 배열을 통해
- 2 -
4. 장기판을 구현하면 좀 더 쉬운 방법으로 말이 이동한 길을 구현할 수 있다는 사실을 알게
되었습니다. 오늘의 회의 내용은 배열을 통해 장기판을 구현하고 배열 위에서 말이 장기판
을 돌아다닐 수 있도록 하는 알고리즘을 짜는데 중점을 두고 진행되었으며 해당 알고리즘
은 아래와 같습니다.
말의 시작점을 입력받는다.
(0,0) or (8,0) or (0,9) or (8,9)
↓
FIND POSITION을 통해 말이 이동할 경로를 찾는다.
ab = 10*a + b를 이용해 배열좌표를 이동하도록 한다.
↓
말이 이동할 경로를 각각 STACK에 저장한다.
↓
STACK의 TOP에 위치한 주소를 새로운 배열(WAY)에 저장하고, 이를
바탕으로 새롭게 말이 이동할 경로를 찾아 STACK에 저장한다. 이때,
새롭게 이동할 수 있는 경로가 없을 경우에는 스택의 자료를 POP하고
새로운 TOP의 내용을 이전 STACK TOP를 저장했던 장소에 덮어씌우고
이용해 길을 찾는다.
↓
for문을 통해 따로 스택의 TOP주소들이 저장된 새로운 배열(WAY)을
따로 출력한다.
위와 같은 알고리즘으로 구동하는 프로그램 소스를 넷 상에 존재하는 스택 구현 소스를 참
고해 작성하기로 했습니다. 자료조사조는 해당 알고리즘과 관련해 비슷한 구조로 작동하는
c소스를 수집하기로 했으며 프로그래밍조는 해당 알고리즘에 따라 프로그램 초안을 작성하
기로 했습니다.
- 알고리즘은 작성했으나 해당 알고리즘을 구현하기 위한 스택의 구현과 재귀함수에 대한
문제점 조원들의 이해도가 높지 않습니다.
- 배열을 이용해 장기판을 구현했을 시에, 말이 이동할 수 없는 곳까지 말이 이동했습니다.
문제 해결의 방향을 잡았으나 배열을 이용한 장기판을 구현했을 때 말의 이동에 있어서 발
생하는 논리 오류를 해결할 방법과, 백트래킹을 위한 스택을 구현할 능력이 아직 모자란
반성
듯 싶습니다. 조장이 프로그래밍팀에 합류해 함께 프로그램 소스를 짜기로 했으며 자료조
사팀은 중위연산과 후위연산에 대해 조사해 조원들에게 알려주기로 하였습니다.
셋째날 2012 년 4 월 5 일 목요일
회의주제 프로그램 초안 분석 및 중위연산과 후위연산
#include<stdio.h>
#define size 89
int stack[size];
int top = 0; // stack pointer
int l = 0;
프로그램 초안 void push(int ch); // push
int pop(); // pop
void printstack(); // 현재 stacklist 출력
void bigsmall();
void insert();
int ma[89]={0};
- 3 -
5. int cheak[20]={0};
int i=0,j=0;
int k=0;
int a,b,s,h;
int sum=0;
int OK;
int main()
{
printf("시작 점을 입력하시오");
scanf("%d %d",&a,&b);
ma[(a*10)+b]=1;
while(top>=0 && top<size+1)
{
insert();
OK=0;
for(k=0;k<15;k++)
printf("cheak[k]=%dn",cheak[k]);
for(k=0;k<10;k++)
{
if(cheak[k]!=0)
{
push(cheak[k]);
OK=1;
}
}
printf("top=%dn",top);
if(OK==1)
{
ma[sum]=1;
a=sum/10;
b=sum-(a*10);
printf("a=%d b=%d sum=%dn",a,b,sum);
pop();
}
if(OK==0)
{
pop();
sum=stack[top-1];
ma[sum]=1;
a=sum/10;
b=sum-(a*10);
printf("a=%d b=%d sum=%dn",a,b,sum);
pop();
}
}
printstack();
}
void push(int ch)
{
stack[top] = ch; // 스택에 한 문자 삽입
printf("push stack[%d]=%dn", top, stack[top]);
top++; // stack pointer 1 증가
sum=stack[top-1];
- 4 -
7. {
cheak[i]=((a-1)*10)+((b-2)*1);
i++;
}
if(ma[((a-2)*10)+((b-1)*1)]==0 && (a-2)<9 && (a-2)>=0&&(b-1)<10 && (b-1)>=0)
{
cheak[i]=((a-2)*10)+((b-1)*1);
i++;
}
}
프로그램 초안을 구동했을 때 말이 이동한 방향을 출력하지 않고 이상한 방향을 출력하는 바람에
잘못된 부분을 찾는데 중점을 두고 회의를 진행했습니다. 그 결과 처음 단계에 구상했었던 WAY
(말이 지나온 길을 저장하는 배열)를 만들어주지 않은 채 STACK의 내용을 그대로 출력하게 되어
말이 지나온 길 대신 스택에 저장된 백트래킹 경로가 그대로 출력되는 바람에 원하는 값이 나오
지 않았다는 사실을 알고 이것을 프로그래밍팀이 주말 동안에 수정하기로 하였습니다.
중위연산(Infix expression) => 후위연산(Postfix expression)
-중위연산식이란 우리들이 기본적으로 알고 있는 수학적인 연산식을 생각하면 되며 이를 후위 연산식
으로 전환하는 과정은 아래와 같다.
a+b*c-d*(e-f)
스택 :
큐 :
제시된 중위연산식을 후위연산식으로 바꾸기 위해 스택과 큐를 하나씩 둔다. 가장 앞쪽의 자료부
터 스택과 큐에 저장하게 되는데 이때 상수는 곧바로 큐에, 연산식은 스택에 저장한다.
a+b*c-d*(e-f)
스택 : +*‘-‘
큐 : abc
회의내용 여기서, 스택의 TOP에 위치하게 되는 연산식이 바로 아래의 연산식보다 낮은 우선순위에 있다면
TOP의 연산식을 저장하기 전에 저장된 연산식들을 순서대로 Pop해서 큐에 저장한다.
a+b*c-d*(e-f)
스택 : -*‘(‘
큐 : abc*+d
만약 스택 안에 괄호가 저장되게 되면 스택 안에 새로운 스택을 구성하게 되는데 이때는 괄호 밖
의 연산자의 우선순위를 무시한 채 괄호 안에서의 우선순위만 고려해 스택과 큐에 저장한다.
a+b*c-d*(e-f)
스택 : -*(-)
큐 : abc*+def
괄호가 닫히면 괄호 안의 내용들부터 pop해서 큐에 저장한다.
a+b*c-d*(e-f)
스택 :
큐 : abc*+def-*-
결과 : abc*+def-*-
구해진 결과를 연산할 때에는 큐에 저장된 내용을 Get해서 스택의 안에 Push하는데 연산자가
Push된 경우 이전에 Push된 두 상수를 Pop해서 연산 후 다시 Push하는 과정을 반복한다. 결과
적으로 스택 안에 남게 되는 자료가 해당 연산과정의 결과가 된다.
(참고내용출처 : http://makebob.tistory.com/18 )
문제점 말이 이동한 경로 대신, FIND POSITION의 과정이 결과로 출력되었습니다. 해결 방법으로
- 6 -
8. 말이 도착한 지점(스택의 TOP)의 값을 따로 저장하는 배열(WAY)를 만들어 출력하도록 소
스를 개량하기로 하였습니다.
프로그램 작성에 있어서 중요한 문제를 간과하고 넘어가 제법 긴 시간을 허비했습니다. 좀
반성 더 명확하게 구성된 알고리즘을 바탕으로 내용을 작성할 수 있도록 노력해야 할 것 같습니
다.
넷째날 2012 년 4 월 8 일 일요일
회의주제 프로그램 보완
#include<stdio.h>
#define size 180
int stack[size];
int top = 0; // stack pointer
int l = 0;
void push(int ch); // push
int pop(); // pop
void printstack(); // 현재 stacklist 출력
void bigsmall();
void insert();
int ma[89]={0};
int cheak[20]={0};
int way[100]={0};
int i=0,j=0;
int k=0;
int a,b,s,h;
int sum=0;
int OK;
int main()
{
int z,p,cnt,sp;
for(cnt=0;cnt<=100;cnt++)
{
way[cnt] = 0;
}
cnt=1;
프로그램
/*시작점 설정*/
수정안 printf("시작점을 입력해 주세요.n 1.(0,0), 2.(8,0), 3.(0,9), 4(8,9) n : ");
scanf("%d",&p);
switch(p)
{
case 1:
a = 0;
b = 0;
break;
case 2:
a = 0;
b = 8;
break;
case 3:
a = 9;
b = 0;
break;
case 4:
a = 9;
b = 8;
break;
default:
printf("입력값이 올바르지 않습니다.n 아무 키나 눌러 종료해 주세요.n");
getch();
return 0;
}
way[0] = (b*10)+a;
ma[(b*10)+a]=1;
while(top>=0 && top<size)
{
- 7 -
10. void insert( ) //말이 움직일 공간 탐색
{
for(s=0;s<8;s++)
{
cheak[s]=-1;
}
i=0;
if(ma[((b-2)*10)+((a+1)*1)]==0&& (b-2)>=0 &&(b-2)<9 &&(a+1)<10 && (a+1)>=0)
{
cheak[i]=((b-2)*10)+((a+1)*1);
i++;
}
if(ma[((b-1)*10)+((a+2)*1)]==0 && (b-1)>=0&&(b-1)<9&&(a+2)<10 && (a+2)>=0)
{
cheak[i]=((b-1)*10)+((a+2)*1);
i++;
}
if( ma[((b+1)*10)+((a+2)*1)]==0&& (b+1)>=0&&(b+1)<9&&(a+2)<10 && (a+2)>=0)
{
cheak[i]=((b+1)*10)+((a+2)*1);
i++;
}
if(ma[((b+2)*10)+((a+1)*1)]==0&& (b+2)>=0&&(b+2)<9&&(a+1)<10 && (a+1)>=0)
{
cheak[i]=((b+2)*10)+((a+1)*1);
i++;
}
if(ma[((b+2)*10)+((a-1)*1)]==0&& (b+2)<9 && (b+2)>=0&&(a-1)<10 && (a-1)>=0)
{
cheak[i]=((b+2)*10)+((a-1)*1);
i++;
}
if(ma[((b+1)*10)+((a-2)*1)]==0 && (b+1)<9 && (b+1)>=0&&(a-2)<10 && (a-2)>=0)
{
cheak[i]=((b+1)*10)+((a-2)*1);
i++;
}
if(ma[((b-1)*10)+((a-2)*1)]==0&& (b-1)<9 && (b-1)>=0&&(a-2)<10 && (a-2)>=0)
{
cheak[i]=((b-1)*10)+((a-2)*1);
i++;
}
if(ma[((b-2)*10)+((a-1)*1)]==0 && (b-2)<9 && (b-2)>=0&&(a-1)<10 && (a-1)>=0)
{
cheak[i]=((b-2)*10)+((a-1)*1);
i++;
}
}
주말동안 조원들의 회의 내용을 바탕으로 시작점의 입력 방식을 스위치 형태로 바꾸고
Stack의 TOP에 위치한 자료를 WAY에 저장해 출력하도록 함으로써 말이 이동 경로를 출
회의내용 력할 수 있도록 했습니다. 다만 백트래킹 알고리즘에 문제가 있어 말이 길을 찾아가지 못
하고 80번대에서 멈춰버리는 현상이 발생했습니다. 이를 해결하기 위해 백트래킹을 주도하
는 조건문 if(OK==0)안에 존재하는 명령어들에 중점을 두고 문제를 해결하기로 했습니다.
백트래킹 과정이 온전히 실행되지 않았습니다. 조건에 따라 ma[sum] = 1로 저장하는 구문
문제점 때문에 말이 길을 찾지 못하고 있다는 사실을 알았으나 이를 해결할 방법을 찾지 못했습니
다.
if(OK==0)안에 존재하는 ma[sum] = 1;을 치우고 후퇴 과정에서 이를 0으로 바꾸는 명령
어를 넣어봤지만 이 또한 계속해서 길을 찾지 못한 채 스택 포인터가 제자리에서 이리저리
반성
움직이다 오류로 인해 프로그램이 종료되는 결과 밖에 내지 못했습니다. 화요일까지 이를
해결할 방안을 찾기로 하고 회의를 마쳤습니다.
결과 발표
#include<stdio.h>
#define size 300
프로그램 소스 int stack[size];
int top = 0; // stack pointer
int l = 0;
- 9 -
11. void push(int ch); // push
int pop(); // pop
void printstack(); // 말이 이동한 경로 출력
void bigsmall();
void insert();
int ma[89]={0};
int cheak[20]={0};
int way[100]={0};
int i=0,j=0;
int k=0;
int a,b,s,h;
int sum=0;
int OK;
int main()
{
int z,p,cnt;
for(cnt=0;cnt<=100;cnt++)
{
way[cnt] = 0;
}
cnt=1;
/*시작점 설정*/
printf("시작점을 입력해 주세요.n 1.(0,0), 2.(8,0), 3.(0,9), 4(8,9) n : ");
scanf("%d",&p);
switch(p)
{
case 1:
a = 0;
b = 0;
break;
case 2:
a = 0;
b = 8;
break;
case 3:
a = 9;
b = 0;
break;
case 4:
a = 9;
b = 8;
break;
default:
printf("입력값이 올바르지 않습니다.n 아무 키나 눌러 종료해 주세요.n");
getch();
return 0;
}
way[0] = (b*10)+a;
ma[(b*10)+a]=1;
- 10 -
12. while(top>=0 && top<size)
{
OK=0; // 말이 이동할 수 있는지 여부 확인 0 : 말이 이동할 길이 없음, 1 : 말이 이동할 수 있음
insert();
for(k=0;k<8;k++) // 말이 이동할 길 탐색.
printf("cheak[%d]=%dn",k,cheak[k]);
for(k=0;k<8;k++)
{
if(cheak[k]!=-1)
{
push(cheak[k]);
OK++;
}
}
printf("top=%dn",top-1);
if(OK>=1)
{
way[cnt]=stack[top-1];
sum = way[cnt];
ma[sum]=1;
a=sum%10;
b=sum/10;
printf("a=%d b=%d sum=%dn",b,a,sum);
way[cnt]=sum;
}
if(OK==0)
{
pop();
way[cnt-1]=stack[top-1];
sum = way[cnt-1];
ma[sum]=0;
a=sum%10;
b=sum/10;
printf("a=%d b=%d sum=%dn",b,a,sum);
way[cnt-1]=sum;
cnt--;
}
cnt++;
}
printstack();
getch();
}
- 11 -
15. 말의 시작점을 입력받는다.
(0,0) or (8,0) or (0,9) or (8,9)
↓
FIND POSITION을 통해 말이 이동할 경로를 찾는다.
ab = 10*a + b를 이용해 배열좌표를 이동하도록 한다.
↓
말이 이동할 경로를 각각 STACK에 저장한다.
알고리즘 개요 ↓
STACK의 TOP에 위치한 주소를 새로운 배열(WAY)에 저장하고, 이를
바탕으로 새롭게 말이 이동할 경로를 찾아 STACK에 저장한다. 이때,
새롭게 이동할 수 있는 경로가 없을 경우에는 스택의 자료를 POP하고
새로운 TOP의 내용을 이전 STACK TOP를 저장했던 장소에 덮어씌우고
이용해 길을 찾는다.
↓
for문을 통해 따로 스택의 TOP주소들이 저장된 새로운 배열(WAY)을
따로 출력한다.
결국 프로그램의 문제점을 고치지 못한 채 프로젝트를 종결했습니다. 알고리즘의 문제를
최종 반성 찾았지만 이를 수정하지 못한 채로 끝맺게 되어 아쉬움이 남았습니다. 좀 더 c언어에 대해
공부할 수 있었더라면 문제를 해결할 수 있었을거라 생각됩니다.
- 14 -