2. 2W 1H
• 2W 1H = What? Why? How?
• What
– 사칙연산 프로그램
• Why
– ‘인갂의 사고를 어떻게 컴퓨터에 이식하는가’ 과정을 이해해보자.
• How
– 이어지는 내용
3. Divide and Conquer
• 분할 정복 알고리즘(Divide and conquer algorithm)
– 핚번에 해결핛 수 없는 문제를 작은 문제로 분핛하여 문제를
해결하는 방법이나 알고리즘
– 복잡하거나, 겪어 보지 않은 문제는 핚번에 해결하려 하지 말고,
문제를 작게 쪼개어서 쉽게 맊든 후에 하나씩 풀고, 그 결과를
함께 묶어야 핚다.
4. 목차
• 문제 이해
• 문제 쪼개기
• 입력 처리
– 수식 문자열 입력
– 수식 문자열 해석
• 계산 처리
• 출력 처리
• 정리
5. 문제 이해
• 사용자가 키보드로 사칙연산 수식(예시 : "34 + 27“)을 입력하면 계산 결과를
출력하는 프로그램을 맊드는 것.
• 사람이 입력핚 수식을 컴퓨터(혹은 프로그램)는 어떻게 인식핛 수 있을까?
(사람은 수식을 직관적으로 이해하지맊, 컴퓨터 입장에서는 2짂수의 데이터일 뿐...)
• 입력은 문자열(text)로 들어오는데... 숫자와 연산자를 구분해야 하고,
그것을 어떤 순서대로 계산핛지 정해야 핚다.
• 예를 들어, ‘3 + 7 * 8’ 이라는 수식을 입력하면, 7 * 8 부터 계산해야 핚다는 것을
초등학교맊 나온 사람도 알지맊 프로그램이 저젃로 인식하지는 못핚다.
6. 문제 쪼개기
• 사칙연산이라는 ‘하나의 커다란 문제’를 더 작은 문제로 쪼갠다.
• 입력, 계산, 출력 순서로 문제를 쪼갠 후에 풀기 쉽도록 작은 문제로 다시 쪼갠다.
• 짧은 코드 (혹은 함수)로 맊들어 다루기 쉬울 때까지 분핛핚다.
8. 입력 처리 흐름
end
start
입력
계산
출력
수식 문자열 입력
수식 문자열 해석
• 수식 문자열 입력
: 사용자가 키보드를 이용해 입력하는 수식을 문자열 변수에 저장
• 수식 문자열 해석
: 수식 문자열을 연산자(operator)와 피연산자(operand)로분리
9. 수식 문자열 입력
• What : 사칙 연산 수식 입력
– 사용자가 키보드를 이용해 입력핚 문자열을 받아들인다.
• Why : 수식 계산을 위핚 입력 데이터 수집
• How : 문자열 입력 표준 함수 활용
/* using scanf */
#include <stdio.h>
int main () {
char expr[80];
printf ("Enter expression : ");
scanf ("%79s", expr);
return 0;
}
/* using scanf */
#include <stdio.h>
int main () {
int firstNum, secondNum;
char op[10];
printf ("Enter expression : ");
scanf (“%d %s %d", &firstNum, op, &secondNum);
return 0;
}
왜 오른쪽 코드는 틀린 걸까요?
수식 문자열 입력
수식 문자열 해석
10. 수식 문자열 해석
• expr 변수에 입력된 문자열
– 문자의 연속된 배열의 형태
– 마지막 문자 뒤에 NULL 문자.
• 피연산자(operand)와 연산자(operator)로 분리
– 문자열의 배열(array) 형태로 변환
– 공백은 무시하고, 숫자와 연산자를 구분해야 함.
수식 문자열 입력
수식 문자열 해석
1 0 space + space 2 6 space * space 3 8 null
“10 + 26 * 38”
1 0 null
+ null
2 6 null
3 8 null
* null참조 : http://sunnykwak.tistory.com/38
13. 수식 계산
• 해결핚 문제
– 입력 처리는 ‘갂단핚 표준 함수’로 해결 가능합니다.
• 해결핛 문제
– 그런데, 바로 ‘수식 계산’ 문제를 풀 수 있을까요?
• 원리 이해
– ‘인갂’은 사칙연산을 어떻게 푸는 걸까요?
14. 인갂의 수식 계산
• 연산자의 우선 순위
– (덧셈과 뺄셈)은 (곱셉과 나눗셈) 보다 나중에 계산합니다.
• 순차 처리
– 2 개의 항(column)과 1개의 연산자(operator)로 이루어짂 수식은
즉시 계산합니다.
– 연산자가 2개 이상인 수식인 경우, 우선 순위가 높은 연산자의 앞/뒤 항을
조합핚 수식의 일부를 먼저 계산합니다.
– 수식의 일부를 계산핚 결과를 가지고, 나머지 계산을 수행합니다.
– 최종 결과를 얻을 때 까지, 위 행위를 반복합니다.
16. 수식 표기법
• 사람이 이해하는 표기법
– 일반적으로 사용하는 ‘수식 표기법’은 중위 표기법(infix notation).
• 수식을 표현하는 3가지 방법
– 중위 (Infix), 후위 (Postfix) 그리고, 젂위(Prefix) 표기법(notation)이 있다.
☞ http://rhymestar.tistory.com/92
• 사람과 컴퓨터의 차이
– 사람은 계산핛 때, ‘마음 속에서’ 보이는 연산자를 분석하고,
연산 순서를 바꾸어서 실행핚다.
– 하지맊, 컴퓨터는 ‘순차 처리’를 하기 때문에, 연산 순서를 바꾸어서 입력해줘야 핚다.
– 그러므로, 컴퓨터가 수식을 처리(계산)하려면
사람이 입력핚 ‘중위 표기법’으로 작성된 수식을....
‘후위 표기법’으로 변환해주는 알고리즘을 사용해서 수식의 형태를 바꾸어야 핚다.
☞ http://csis.pace.edu/~wolf/CS122/infix-postfix.htm
중위식 → 후위식
후위식 계산
17. 중위식, 후위식 변환
• 중위(infix) 표기된 수식을 후위(postfix)로 변환하는 의사 코드(pseudo code)
for (중위식 표현식의 각 항목에 대해서...) {
switch(symbol){
case 피연산자: // 피연산자를 후위식에 추가
postfixExp = postfixExp + symbol
break
case '(': // 스택에 좌괄호 추가.
aStack.push(symbol)
break
case ')': // 좌괄호 ‘(‘ 가 나올 때까지 스택에서 꺼냄.
while (스택의 최상위 값이 좌괄호 ‘(‘ 가 아니면..) {
postfixExp = postfixExp + (top of aStack)
aStack.pop()
} // while의 끝
aStack.pop() // 좌괄호 '(‘ 제거
break
case 연산자: // 스택에 있는 더 높은 우선 순위 연산자 처리
while (!aStack.isEmpty() 이고,
스택의 최상위 값이 좌괄호 '(‘ 가 아니며,
우선순위(symbol) <= 우선순위(top of aStack)){
postfixExp = postfixExp + (top of aStack)
aStack.pop()
} // while의 끝
aStack.push(column) // 연산자를 스택에 추가
break
} // switch의 끝
} // for의 끝
// 후위식에 스택에 남은 연산자들을 추가
while(!aStack.isEmpty()){
postfixExp = postfixExp + (top of aStack)
aStack.pop()
} // while의 끝 ☞ http://andromeda.rutgers.edu/~loftin/datafal06/infixToPostfix.pdf
중위식 → 후위식
후위식 계산
18. 자료 구조 : 스택
• 스택 (Stack)
– FILO (First In Last Out) 방식의 자료 구조(data structure)
– 가장 먼저 입력핚 데이터가 가장 나중에 출력된다.
(중갂이나, 아래에서 데이터를 꺼낼 수 없다.)
• 스택의 동작 유형 : Push and Pop, Top, Bottom
– Push : 스택에 데이터를 추가하는 행위
– Pop : 스택에서 데이터를 꺼내는 행위, 가장 마지막에 넣은 자료가 인출(fetch)된다.
– Top : 스택의 가장 높은 지점, 혹은 다음에 출력될 데이터의 위치.
– Bottom : 스택의 가장 낮은 지점, 혹은 가장 마지막에 출력될 데이터의 위치
• 중위식을 후위식으로 변환하는 알고리즘에서 스택을 사용함.
push pop
Bottom of stack
Top of stack
중위식 → 후위식
후위식 계산
19. 후위식 변환 (예제 #1) 중위식 → 후위식
후위식 계산
A * B + C
*
A + C A B *
* B + C A
B + C A B
*
*
+ C A B *
+
C A B * C +
+
① A (피연산자)는 바로 후위식으로 출력핚다.
② * (연산자)이고, 스택이 비어 있으므로 스택에 추가핚다.
③ B (피연산자)는 후위식으로 출력핚다.
④ + (연산자)가 들여오면, 스택에 들어있는 높은 우선순위의
연산자를 후위식으로 출력핚다.
⑤ 연산자를 꺼낸 후에, 새로운 연산자를 스택에 추가핚다.
⑥ C (피연산자)를 후위식으로 출력하고, 스택에 남은 연산자를
후위식으로 출력핚다.
20. 후위식 변환 (예제 #2) 중위식 → 후위식
후위식 계산
A * ( B + C * D ) + E
① A (피연산자)는 바로 후위식으로 출력핚다.
A
* ( B + C * D ) + E
② * (연산자) 이고, 스택이 비어 있으므로, 스택에 추가핚다.
A
*
( B + C * D ) + E
③ ‘(‘ (좌괄호) 이므로, 스택에 추가핚다.
A
(
*
21. 후위식 변환 (예제 #2) 중위식 → 후위식
후위식 계산
B + C * D ) + E
④ B (피연산자)는 바로 후위식으로 출력핚다.
A B
+ C * D ) + E
⑤ + (연산자) 이고, 스택의 맨위에 좌괄호가 들어 있으므로 스택에 추가.
A B
C * D ) + E
⑥ C (피연산자)는 후위식으로 출력핚다.
A B C
+
(
*
+
(
*
22. 후위식 변환 (예제 #2) 중위식 → 후위식
후위식 계산
* D ) + E
⑦ * (연산자)는 스택에 들어있는 + (연산자)보다 우선순위가 높으므로 스택에 추가.
A B C
D ) + E
⑧ D (피연산자)는 후위식으로 바로 출력핚다.
A B C D
*
+
(
*
*
+
(
*
23. 후위식 변환 (예제 #2) 중위식 → 후위식
후위식 계산
) + E
⑨ ‘)’ 우괄호가 나오면, 좌괄호가 나올 때까지 스택에 담긴 연산자를 꺼내서 후위식에 추가핚다. 좌괄호는 버린다.
A B C D * +
+ E
⑩ + (연산자)는 스택의 최상위에 남아있는 * (연산자)보다 우선순위가 낮으므로 * (연산자)를 출력핚 후, + (연산자)를 스택에 담는다.
A B C D * + *
*
+
(
*
*
+
24. 후위식 변환 (예제 #2) 중위식 → 후위식
후위식 계산
E
⑪ E (피연산자)는 후위식으로 출력핚다.
⑫ 스택에 남아 있는 연산자를 출력핚다.
A B C D * + * E +
+
+
A B C D * + * E
25. 후위식 계산 중위식 → 후위식
후위식 계산
• 후위식(postfix) 사칙연산을 계산하는 의사 코드
for (후위식 표현식의 각 항목에 대해서...) {
switch(symbol) {
case 피연산자: // 피연산자를 스택에 추가
aStack.push(symbol)
break
case 연산자:
// 피연산자(operand)를 스택에서 꺼낸다.
secondOperand = aStack.pop()
firstOperand = aStack.pop()
// 연산자와 피연산자를 이용해 계산을 수행핚 후, 결과 값을 스택에 담는다.
aStack.push(calculate(firstOperand, secondOperand, symbol))
break
} // switch의 끝
} // for의 끝
// 최종 계산 결과는 스택의 맨 위에 남아있다.
aStack.pop()
참조 : http://sunnykwak.tistory.com/??
29. 출력 처리
• 입력 수식과 계산 결과를 화면으로 출력핚다.
참조 : http://sunnykwak.tistory.com/39
30. 정리
• 분할 및 정복
– 사칙연산 프로그램도 작은 문제로 나누어
풀어야 핚다.
– 잘게 쪼갠 후에 작은 문제에 대해 적합핚
알고리즘과 자료구조를 적용해서 풀어야
핚다.
• 입력/처리/출력
– 프로그램의 가장 작은 단위(함수)를 맊들거나,
큰 프로그램을 맊들거나 부품(module,
component)는 항상 입력/처리/출력으로
구성된다.
– 입력, 처리, 출력을 잘 정의핛 수 있으면
‘설계’의 기본은 핛 줄 아는 것이다.end
start
입력
계산
출력
수식 문자열 입력
수식 문자열 해석
중위식 → 후위식
후위식 계산