CHAP 2:순환 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.

Slides:



Advertisements
Similar presentations
Recursion SANGJI University KO Kwangman
Advertisements

제14장 자기호출과 함수포인터 자기호출 함수 (Recursive Functions) 재귀적 함수 recursion
쉽게 풀어쓴 C언어 Express 제11장 포인터 C Express Slide 1 (of 27)
CHAP 1:자료구조와 알고리즘.
(Program = Algorithm + Data Structure)
19세기 말 프랑스 수학자 Lucas가 제안 전설 Benares(베트남 하노이 외곽)에는 세계의 중심이 있고, 그 곳에는 아주 큰 사원이 있다. 이 사원에는 높이 50cm 정도 되는 다이아몬드 막대 3 개가 있다. 그 중 한 막대에는 천지 창조 때에 신이 구멍이 뚫린 64.
9장. C 언어의 핵심! 함수. 9장. C 언어의 핵심! 함수 9-1 함수의 정의와 선언 main 함수 다시 보기 : 함수의 기본 형태 { } 그림 9-1.
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
누구나 즐기는 C언어 콘서트 제7장 함수.
Report #2 - Solution 문제 #1: 다음과 같이 프로그램을 작성하라.
컴퓨터 프로그래밍 기초 [Final] 기말고사
보고서 #7 (기한: 6/2) 2개의 스택, stk1, stk2를 이용하여 큐를 구현하라.
쉽게 풀어쓴 C언어 Express 제9장 함수와 변수 C Express.
CHAP 1:자료구조와 알고리즘 순천향대학교 컴퓨터공학과 하 상 호.
시스템 보안 [Buffer Overflow] DEC, 15, 2013 By 박동혁.
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
CHAP 2:순환 순천향대학교 컴퓨터공학과.
Chapter 02 순환 (Recursion).
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 2012.
11장. 포인터 01_ 포인터의 기본 02_ 포인터와 Const.
CHAP 2:순환 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
8장 함수 함수의 필요성 라이브러리 함수와 사용자 정의 함수 함수의 정의, 원형, 호출 배열을 함수 인자로 전달 재귀호출.
컴퓨터 프로그래밍 기초 #02 : printf(), scanf()
C++ Espresso 제12장 템플릿.
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 Slide 1 (of 28)
CHAP 1:자료구조와 알고리즘.
2007 1학기 11 프로젝트 기초 실습.
보고서 #5(제출기한: 10/14) 다음 문제를 해결하시오.
쉽게 풀어쓴 C언어 Express 제9장 함수와 변수 C Express.
쉽게 풀어쓴 C언어 Express 제9장 함수와 변수 C Express Slide 1 (of 33)
Introduction To Data Structures Using C
자바 5.0 프로그래밍.
프로그래밍 개요
어서와 C언어는 처음이지 제14장.
자료구조: CHAP 7 트리 –review 순천향대학교 컴퓨터공학과 하 상 호.
3장 상수 변수 기본 자료형 키워드와 식별자 상수와 변수 기본 자료형 형변환 자료형의 재정의.
메모리 관리 & 동적 할당.
CHAP 2:순환.
Quiz #7 다음 수들을 합병 정렬과 퀵 정렬 알고리즘을 이용하여 오름 차순으로 정렬하였을 때, 데이터 이동 회수를 각각 구하라. 여러분은 정렬 과정을 단계별로 보이면서 이동 회수를 추적해야 한다. 단, 퀵 정렬시에 피봇으로 배열의 왼쪽 첫 번째 원소를 선택한다. 5.
다음 주 과제 3장 읽어오기 숙제 해서 제출하기. 자료구조와 알고리즘, 순환 E304호,
쉽게 풀어쓴 C언어 Express 제14장 포인터 활용 C Express Slide 1 (of 22)
19. 함수 포인터와 void 포인터.
Chap. 1 Data Structure & Algorithms
함수와 변수 컴퓨터시뮬레이션학과 2016년 봄학기 담당교수 : 이형원 E304호,
컴퓨터 프로그래밍 기초 - 10th : 포인터 및 구조체 -
CHAP 2:순환.
CHAP 2:순환 C로 쉽게 풀어쓴 자료구조 생능출판사 Slide 1 (of 19).
제 1 강.
CHAP 2:순환 순천향대학교 컴퓨터공학과.
Hanoi Tower.
김선균 컴퓨터 프로그래밍 기초 - 7th : 함수 - 김선균
컴퓨터 프로그래밍 기초 - 8th : 함수와 변수 / 배열 -
Chapter 08. 함수.
보고서 #7 (기한: 6/2) 2개의 스택, stk1, stk2를 이용하여 큐를 구현하라.
컴퓨터 프로그래밍 기초 [01] Visual Studio 설치 및 사용방법
9장. C 언어의 핵심! 함수. 9장. C 언어의 핵심! 함수 9-1 함수의 정의와 선언 main 함수 다시 보기 : 함수의 기본 형태 { } 그림 9-1.
CHAP 2:순환 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
CHAP 1:자료구조와 알고리즘.
4장. 데이터 표현 방식의 이해. 4장. 데이터 표현 방식의 이해 4-1 컴퓨터의 데이터 표현 진법에 대한 이해 n 진수 표현 방식 : n개의 문자를 이용해서 데이터를 표현 그림 4-1.
7주차: Functions and Arrays
CHAP 2:순환.
Homework #8 (실습 #7) [1/2] 다음을 수행하는 PHP 프로그램을 작성하여 프로그램과 결과물을 프린트하여 제출한다. sin(45º), cos(45º), tan(45º)를 출력하는 프로그램을 작성하시오. 피보나치 수를 구하는 함수 fib($n)을 작성하고,
쉽게 배우는 알고리즘 2장. 점화식과 점근적 복잡도 분석
실습과제 (변수와 자료형, ) 1. 다음 작업 (가), (나), (다)를 수행하는 프로그램 작성
CHAP 1:자료구조와 알고리즘.
개정판 누구나 즐기는 C언어 콘서트 제13장 동적 메모리 출처: pixabay.
강의 #3. 순환(Recursion).
함수 정의, void 자료형 함수 원형선언 함수 호출 변수 영역 규칙 재귀 함수
5. 1 두 수를 입력받아 큰 수를 구하는 순서도를 작성하시오
Presentation transcript:

CHAP 2:순환 C로 쉽게 풀어쓴 자료구조 생능출판사 2005

순환(recursion)이란? 알고리즘이나 함수가 수행 도중에 자기 자신을 다시 호출하여 문제를 해결하는 기법 정의자체가 순환적으로 되어 있는 경우에 적합한 방법 (예제) 팩토리얼 값 구하기 피보나치 수열 하노이의 탑 이진탐색

팩토리얼 프로그래밍 #1 팩토리얼의 정의 팩토리얼 프로그래밍 #1: 위의 정의대로 구현 (n-1)! 팩토리얼을 구하는 서브 함수 factorial_n_1를 따로 제작 int factorial(int n) { if( n == 1 ) return(1); else return (n * factorial_n_1(n-1) ); }

팩토리얼 프로그래밍 #2 팩토리얼 프로그래밍 #2: (n-1)! 팩토리얼을 현재 작성중인 함수를 다시 호출하여 계산(순환 호출) int factorial(int n) { if( n == 1 ) return(1); else return (n * factorial(n-1) ); } 3!은? 3!를 계산하려면 3! = 3*2! 2!를 계산하려면 2! = 2*1! 1!은 1 3!는? 2!는? 1!는? 6 2 1

순환호출순서 개념설명: 순환호출 팩토리얼 함수의 호출 순서 factorial(5) = 5 * factorial(4) = 5 * 4 * 3 * 2 * 1 = 120 factorial(3) { if( 3 == 1 ) return 1;     else return (3 * factorial(3-1) ); } ④ ① factorial(2) { if( 2 == 1 ) return 1;     else return (2 * factorial(2-1) ); } ③ ② factorial(1) { if( 1 == 1 ) return 1; ..... }

순환 호출의 내부적인 구현 p.46 그림 2-3 factorial(3)의 호출 중의 시스템 스택의 변화

시스템 스택을 다 사용하여 결국 에러 발생 (Stack Overflow) 순환 알고리즘의 구조 가상실습: 팩토리얼 순환 알고리즘은 다음과 같은 부분들을 포함한다. 순환 호출을 하는 부분 순환 호출을 멈추는 부분 else return n * factorial(n-1); int factorial(int n) { 순환을 멈추는 부분 순환호출을 하는 부분 } if( n == 1 ) return 1 만약 순환 호출을 멈추는 부분이 없다면?. 시스템 오류가 발생할 때까지 무한정 호출하게 된다. 시스템 스택을 다 사용하여 결국 에러 발생 (Stack Overflow)

순환 <-> 반복 컴퓨터에서의 되풀이 대부분의 순환은 반복으로 바꾸어 작성할 수 있다. 순환 반복 순환(recursion): 순환 호출 이용 반복(iteration): for나 while을 이용한 반복 대부분의 순환은 반복으로 바꾸어 작성할 수 있다. 순환 순환적인 문제에서는 자연스러운 방법 함수 호출의 오버헤드 반복 수행속도가 빠름 순환적인 문제에 대해서는 프로그램 작성이 아주 어려울 수 있음 1!=1 2!=2 3!=6 … 3!를 계산하려면 3! = 3*2! 2!를 2! = 2*1! 1!은 1 2!는? 1!는? 1 2

팩토리얼의 반복적 구현 int factorial_iter(int n) { int k, v=1; for(k=n; k>0; k--) v = v*k; return(v); }

순환의 원리 p.50 [그림2-7] [그림2-8] 알고리즘 정의가 순환적으로 되어있는 경우에 유리 해결된 부분 + 남아있는 부분 [그림2-8] 최대 값을 구하는 순환호출 알고리즘 정의가 순환적으로 되어있는 경우에 유리 (예) 팩토리얼 함수, 피보나치 수열, 이항계수 계산, 각종 이진트리 알고리즘, 이진탐색, 하노이 탑 문제 순환은 강력한 프로그래밍 도구!!

순환 알고리즘의 성능 n번의 순환 호출 : O(n) int factorial(int n) { if( n == 1 ) return(1); else return (n * factorial(n-1) ); } int factorial_iter(int n) { int k, v=1; for(k=n; k>0; k--) v = v*k; return(v); } 순환 반복 n번의 순환 호출 : O(n) 하지만 함수 호출의 경우 여분의 기억공간이 더 필요, 파라미터를 스택에 저장하는 등의 사전작업 필요 for를 이용하여 n번 반복 : O(n)

거듭제곱 프로그래밍 #1 순환적인 방법이 반복적인 방법보다 더 효율적인 예 숫자 x의 n제곱값을 구하는 문제: xn double slow_power(double x, int n) { int i; double r = 1.0; for(i=0; i<n; i++) r = r * x; return(r); }

거들제곱값 프로그래밍 #2 순환적인 방법 power(x, n) if n=0 then return 1; else if n이 짝수 then return power(x2,n/2); else if n이 홀수 then return x*power(x2, (n-1)/2); double power(double x, int n) { if( n==0 ) return 1; else if ( (n%2)==0 ) return power(x*x, n/2); else return x*power(x*x, (n-1)/2); } // 생능출판장테스트.cpp : 콘솔응용프로그램에대한진입점을정의합니다. // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <time.h> double slow_power(double x, int n) { int i; double r = 1.0; for(i=0; i<n; i++) r = r * x; return(r); } double power(double x, int n) if( n==0 ) return 1; else if ( (n%2)==0 ) return power(x*x, n/2); else return x*power(x*x, (n-1)/2); int _tmain(int argc, _TCHAR* argv[]) clock_t start, finish; double duration; start = clock(); // 수행시간을측정하고하는코드.... double r = slow_power(2,500); printf("%lf\n", r); finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf("%f 초입니다.\n", duration); double r2 = power(2,500); printf("%lf\n", r2); return 0;

거들제곱값 프로그래밍 분석 순환적인 방법의 시간 복잡도 반복적인 방법과 순환적인 방법의 비교 만약 n이 2의 제곱이라고 가정하면 다음과 같이 문제의 크기가 줄어든다. 반복적인 방법과 순환적인 방법의 비교   반복적인 함수 slow_power 순환적인 함수 power 시간복잡도 O(n) O(logn) 실제수행속도 7.17초 0.47초

피보나치 수열의 계산 #1 순환 호출을 사용하면 비효율적인 예 피보나치 수열 순환적인 구현 0,1,1,2,3,5,8,13,21,… 순환적인 구현 // 생능출판장테스트.cpp : 콘솔응용프로그램에대한진입점을정의합니다. // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <time.h> int fib(int n) { if( n==0 ) return 0; if( n==1 ) return 1; return (fib(n-1) + fib(n-2)); } int fib_iter(int n) if( n < 2 ) return n; else { int i, tmp, current=1, last=0; for(i=2;i<=n;i++){ tmp = current; current += last; last = tmp; return current; int _tmain(int argc, _TCHAR* argv[]) clock_t start, finish; double duration; puts("순환적방법\n"); start = clock(); // 수행시간을측정하고하는코드.... printf("%d\n", fib(30)); finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf("%f 초입니다.\n", duration); puts("반복적방법\n"); printf("%d\n", fib_iter(30)); return 0; int fib(int n) { if( n==0 ) return 0; if( n==1 ) return 1; return (fib(n-1) + fib(n-2)); }

피보나치 수열의 계산 #2 순환 호출을 사용했을 경우의 비효율성 fib(6) fib(4) fib(5) fib(2) fib(3) 같은 항이 중복해서 계산됨 예를 들어 fib(6)을 호출하게 되면 fib(3)이 4번이나 중복되어서 계산됨 이러한 현상은 n이 커지면 더 심해짐 fib(6) fib(4) fib(5) // 생능출판장테스트.cpp : 콘솔응용프로그램에대한진입점을정의합니다. // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <time.h> int fib(int n) { if( n==0 ) return 0; if( n==1 ) return 1; return (fib(n-1) + fib(n-2)); } int fib_iter(int n) if( n < 2 ) return n; else { int i, tmp, current=1, last=0; for(i=2;i<=n;i++){ tmp = current; current += last; last = tmp; return current; int _tmain(int argc, _TCHAR* argv[]) clock_t start, finish; double duration; puts("순환적방법\n"); start = clock(); // 수행시간을측정하고하는코드.... printf("%d\n", fib(30)); finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf("%f 초입니다.\n", duration); puts("반복적방법\n"); printf("%d\n", fib_iter(30)); return 0; fib(2) fib(3) fib(3) fib(4) fib(2) fib(3) fib(1) fib(2) fib(1) fib(2) fib(2) fib(3)

피보나치 수열의 반복구현 반복 구조를 사용한 구현 fib_iter(int n) { if( n < 2 ) return n; else { int i, tmp, current=1, last=0; for(i=2;i<=n;i++){ tmp = current; current += last; last = tmp; } return current; // 생능출판장테스트.cpp : 콘솔응용프로그램에대한진입점을정의합니다. // #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <time.h> int fib(int n) { if( n==0 ) return 0; if( n==1 ) return 1; return (fib(n-1) + fib(n-2)); } int fib_iter(int n) if( n < 2 ) return n; else { int i, tmp, current=1, last=0; for(i=2;i<=n;i++){ tmp = current; current += last; last = tmp; return current; int _tmain(int argc, _TCHAR* argv[]) clock_t start, finish; double duration; puts("순환적방법\n"); start = clock(); // 수행시간을측정하고하는코드.... printf("%d\n", fib(30)); finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf("%f 초입니다.\n", duration); puts("반복적방법\n"); printf("%d\n", fib_iter(30)); return 0;

하노이 탑 문제 문제는 막대 A에 쌓여있는 원판 n개를 막대 C로 옮기는 것이다. 단 다음의 조건을 지켜야 한다. A B C 한 번에 하나의 원판만 이동할 수 있다 맨 위에 있는 원판만 이동할 수 있다 크기가 작은 원판위에 큰 원판이 쌓일 수 없다. 중간의 막대를 임시적으로 이용할 수 있으나 앞의 조건들을 지켜야 한다. A B C

n=3인 경우의 해답 A B C A B C

일반적인 경우에는? C를 임시버퍼로 사용하여 A에 쌓여있는 n-1개의 원판을 B로 옮긴다. A의 가장 큰 원판을 C로 옮긴다. A를 임시버퍼로 사용하여 B에 쌓여있는 n-1개의 원판을 C로 옮긴다.

남아있는 문제는? 자 그러면 어떻게 n-1개의 원판을 A에서 B로, 또 B에서 C로 이동하는가? (힌트) 우리의 원래 문제가 n개의 원판을 A에서 C로 옮기는 것임을 기억하라. 따라서 지금 작성하고 있는 함수를 파라미터를 n-1로 바꾸어 순환호출하면 된다. // 막대 from에 쌓여있는 n개의 원판을 막대 tmp를 사용하여 막대 to로 // 옮긴다. void hanoi_tower(int n, char from, char tmp, char to) {    if (n==1){        from에서 to로 원판을 옮긴다.    }    else{        hanoi_tower(n-1, from, to, tmp);        from에 있는 한 개의 원판을 to로 옮긴다.        hanoi_tower(n-1, tmp, from, to); }

하노이탑 최종 프로그램 가상실습: 하노이탑 n-1개의 원판을 A에서 B로 옮기고 n번째 원판을 A에서 C로 옮긴 다음, n-1개의 원판을 B에서 C로 옮기면 된다. #include <stdio.h> void hanoi_tower(int n, char from, char tmp, char to) { if( n==1 ) printf("원판 1을 %c 에서 %c으로 옮긴다.\n",from,to); else { hanoi_tower(n-1, from, to, tmp); printf("원판 %d을 %c에서 %c으로 옮긴다.\n",n, from, to); hanoi_tower(n-1, tmp, from, to); } main() hanoi_tower(4, 'A', 'B', 'C'); void hanoi_tower(int n, char from, char tmp, char to) { if( n==1 ) printf("원판1을%c 에서%c으로옮긴다.\n",from,to); else { hanoi_tower(n-1, from, to, tmp); printf("원판%d을%c에서%c으로옮긴다.\n",n, from, to); hanoi_tower(n-1, tmp, from, to); } int _tmain(int argc, _TCHAR* argv[]) hanoi_tower(4,'a','b','c');

#include <stdio.h> void hanoi_tower(int n, char from, char tmp, char to) { if( n==1 ) printf("원판 1을 %c 에서 %c으로 옮긴다.\n",from,to); else { hanoi_tower(n-1, from, to, tmp); printf("원판 %d을 %c에서 %c으로 옮긴다.\n",n, from, to); hanoi_tower(n-1, tmp, from, to); } hanoi_tower(3,a,b,c) hanoi_tower(2,a,c,b) hanoi_tower(1,a,b,c) 원판 1을 a에서 c로 옮긴다 원판2를 a에서 b로 옮긴다 hanoi_tower(1,c,a,b) 원판 1을 c에서 b로 옮긴다. 원판3을 a에서 c로 옮긴다 hanoi_tower(2,b,a,c) hanoi_tower(1,b,c,a) 원판 1을 b에서 a로 옮긴다 원판2를 b에서 c로 옮긴다 원판 1을 a에서 c로 옮긴다.