제 1 장 기본 개념 - :::: VRLAB 가상현실연구실

Download Report

Transcript 제 1 장 기본 개념 - :::: VRLAB 가상현실연구실

Chapter 1
Basic Concepts
2009. 3. 16
알고리즘(Algorithm)
 특정한 일을 수행하기 위한 명령어들의 유한 집합
 알고리즘의 요건(criteria)
1. 입력 : (외부) 원인  0
2. 출력 : 결과  1
3. 명확성(definiteness) : 모호하지 않은 명확한 명령
4. 유한성(finiteness) : 반드시 종료
5. 유효성(effectiveness) : 각 명령은 기본적이고 실행 가능해야 함
 알고리즘 기술 방법
– 자연어 기술
– 흐름도(flowchart)
– Pseudo-code
– Programming Language (예: C 언어)
알고리즘의 기술 (예 1.1)
 [선택 정렬] : n  1 개의 서로 다른 정수의 집합을 정렬하
는 프로그램을 작성
– 방법: 정렬되지 않은 정수들 중에서 가장 작은 값을 찾아서 정렬
된 리스트 다음 자리에 놓음
for (i = 0; i < n; i++) [
list[i]에서부터 list[n-1]까지의 정수 값을 검사한 결과
list[j]가 가장 작은 값이라 하자;
list[i]와 list[j]를 서로 교환함;
}
– 예:
3, 8, 1, 4, 7
 1, 8, 3, 4, 7
 1, 3, 8, 4, 7
 1, 3, 4, 8, 7
 1, 3, 4, 7, 8
선택 정렬 프로그램
void SelectionSort(int *list, const int n)
// n개의 정수 list[0]부터 list[n-1]까지 비감소 순으로 정렬
함.
{
for (int i = 0; i < n; i++) {
int temp, min;
0 1 2 3 4
3, 8, 1, 4, 7
// list[min]과 list[n-1] 사이의 가장 작은 정수 찾음
min = i;
for (int k = i + 1; k < n; k++)
if (list[k] < list[min]) min = k;
temp = list[i]; list[i] = list[min]; list[min] = temp; //
교환
}
}
 1, 8, 3, 4, 7
 1, 3, 8, 4, 7
 1, 3, 4, 8, 7
 1, 3, 4, 7, 8
알고리즘의 기술 (예 1.2)
 [이진 탐색] : x = list[j]인 j를 찾음
– 가정: list의 원소들은 이미 정렬된 상태
– left, right: 탐색하고자 하는 원소들의 왼쪽 및 오른쪽 끝 지점
• 초기 값: left = 0, right = n-1
– middle: 탐색할 원소들의 중간 위치, 즉, middle = (left + right) / 2
– list[middle]과 x를 비교할 경우 3가지 경우 중에 하나
⑴ x > list[middle] 일 때: left를 middle+1로 설정
⑵ x < list[middle] 일 때: right를 middle-1로 설정
⑶ x = list[middle] 일 때: middle을 반환
left
index 0 1 2 3
list
middle
4 5
6
right
7 8
9 10
3 5 7 10 15 16 19 20 26 28 31
x = 26
이진 탐색 알고리즘
int BinarySearch(int *list, const int x, const int n)
// 정렬된 배열 list[0], ..., list[n-1]에서 x를 탐색한다.
{
for (left와 right를 초기화한다; 원소가 더 있는 한 계속; )
{
// 가운데 원소를 middle로 설정한다.
switch (compare(x, list[middle])) {
case '>' : left를 middle + 1로 설정; break;
case '<' : right = middle - 1로 설정; break;
case '=' : x를 발견;
}
char compare(int x, int y)
}
{
not found;
if (x > y) return '>';
}
else if (x < y) return '<';
else return '=';
}
이진 탐색 함수 구현
int BinarySearch(int *list, const int x, const int n)
// 정렬된 배열 list[0], ..., list[n-1]에서 x를 탐색한다.
{
for (int left = 0, right = n-1; left <= right; ) {
int middle = (left + right) / 2;
switch (comare(x, list[middle])) {
case '>' : left = middle + 1; break; // x > list[middle]
case '<' : right = middle - 1; break; // x < list[middle]
case '=' : return middle;
// x == list[middle]
}
}
return -1; // x가 list에 존재하지 않음
}
순환 알고리즘(Recursive Algorithms)
 수행이 완료되기 전에 자기 자신을 다시 호출
– 직접 순환 : direct recursion
– 간접 순환 : indirect recursion
 예
– Factorial(!) : N! = N*(N-1)!
int FACT(int N)
{
if (N==0) return (1);
else return (N*FACT(N-1));
}
– 이항 계수(Binomial Coefficient)
– Fibonacci 수열
N*(N-1)*(N-2)*....*2*1*1
순환 알고리즘(Recursive Algorithms)
 특징
– 장점: 실행 과정의 효율적 기술
• 복잡한 계산 과정을 단순하고 명료하게 표현 가능
• 문제 자체가 순환적으로 정의되는 경우에 적합
– 단점: 실행 효율 저하
• 실행 시간에 순환을 위한 별도의 준비 설정이 필요
 공간 및 시간 복잡도 증가
– 일반적으로 배정문(assignment), 조건문(if-else), 반복문(while)으
로 표현되는 반복 알고리즘은 순환 알고리즘으로 변환 가능
예1.3: 이진 탐색의 순환적 구현
int BinarySearch(int *list, const int x, const int left, const int right)
// 정렬된 배열 list[left], ..., list[right]에서 x를 탐색
{
if (left <= right) {
int middle = (left + right) / 2;
switch(compare(x, list[middle])) {
case '>' : return BinarySearch(list, x, middle+1, right);
case '<' : return BinarySearch(list, x, left, middle-1);
case '=' : return middle;
}
}
return -1;
// x가 발견되지 않음
}
예1.4: 순열 생성기
 n1개의 원소로 된 집합으로부터 모든 가능한 순열을 출
력
perm(a, b, c, d);
perm(b, c, d)
perm(c, d)
1) a + perm(b, c, d)
1) b + perm(c, d)
1) c + perm(d)
2) b + perm(a, c, d)
2) c + perm(b, d)
2) d + perm(c)
3) c + perm(b, a, d)
3) d + perm(c, b)
4) d + perm(b, c, a)
예1.4: 순열 생성기
void perm(char *list, const int k, const int n) { // n은 list의 크기
// list[k], ..., list[n-1]에 대한 모든 순열을 생성
if (k == n-1) { // 순열을 출력
for (int i = 0; i < n; i++) printf(“%c”, list[i]);
printf(“\n”);
}
else{ // list[k], ..., list[n-1]에 있는 여러 순열을 순환적으로 생성
for (i = k; i < n; i++) {
// list[k]와 list[i]를 교환
char temp = list[k]; list[k] = list[i]; list[i] = temp;
perm(list, k+1, n); // list[k+1], ..., list[n-1]에 대한 모든 순열
// 원래 상태로 되돌리기 위해 list[k]와 list[i]를 다시 교환
temp = list[k]; list[k] = list[i]; list[i] = temp;
}
}}
perm(list, 0, n) 호출  list[0], list[1], …, list[n-1]에 대한 모든 순열 생성
데이터 타입(Data Type)
 정의
– 객체(object)와 그 객체 위에 작동하는 연산(operation)들의 집합
• 사전 정의(pre-defined) 데이터 타입
• 사용자 정의(user-defined) 데이터 타입
 C/C++의 데이터 타입
– 기본 데이터 타입
• char, int, float, double
• 타입 수식어: short, long, signed, unsigned
– 파생 데이터 타입
• 포인터(pointer) 타입, 참조(reference) 타입(C++)
– 데이터 집단화 구조
• 배열(array), 구조체(struct), 클래스(class)(C++)
데이터 캡슐화 및 추상화
 데이터 캡슐화(Encapsulation)
– 외부에 대해 데이터 객체의 자세한 구현을 은폐시킴
• 객체의 내부 표현을 사용자로부터 은폐 (information hiding)
 데이터 추상화
– 데이터 객체의 명세(specification)와 구현(implementation) 을 분리
• “무엇(what)”과 “어떻게(how)”를 독립화
– 추상 데이터 타입(Abstract Data Type: ADT)
• 객체와 연산에 대한 명세를 객체의 표현 및 연산의 구현으로부터 분리시
킨 데이터 타입
• 예: Ada언어의 Package, C++/Java언어의 Class
• ADT 연산의 명세
 함수 이름, 매개 변수 타입, 결과 타입, 함수가 수행하는 기능에
대한 기술
 내부적 표현이나 구현에 대한 자세한 설명은 제외됨
예: 자연수(Natural Number) 추상 데이터 타입 정의 예
ADT NaturalNumber is
객체: 순서가 있는 정수의 일부분으로 0부터 해당 컴퓨터의 최대 정수
(MAXINT)까지의 값
함수:
x, y  NaturalNumber, TRUE, FALSE  Boolean
+, -, <, ==, = 는 일반 정수 연산자
Zero():NaturalNumber
IsZero(x):Boolean
Add(x,y):NaturalNumber
Subtract(x,y):NaturalNumber
Equal(x,y):Boolean
Successor(x):NaturalNumber
end NaturalNumber
::= 0
::= if (x == 0) IsZero = TRUE
else IsZero = FALSE
::= if (x+y <= MAXINT) Add = x+y
else Add = MAXINT
::= if (x<y) Subtract = 0
else Subtract = x-y
::= if (x == y) Equal = TRUE
else Equal = FALSE
::= if (x == MAXINT) Successor = x
else Successor = x+1
Stack ADT의 예 (C++)
#include <iostream.h>
class stack {
// stack class 정의
private:
int* stackPtr;
int maxLen, topPtr;
public:
stack() {
// constructor
stackPtr = new int [100];
maxLen = 99; topPtr = -1;
}
~stack() { delete[] stackPtr; } //destructor
void push(int number);
void pop();
int top() { return stackPtr[topPtr]; }
int empty() { return (topPtr == -1); }
}
Stack ADT의 예 (C++)
 stack 클래스 이용 예
void main() {
int topOne;
stack stk;
// stack 클래스의 instance 생성
stk.push(42);
stk.push(17);
topOne = stk.top();
stk.pop(); …
}
 표준 C++ 라이브러리(standard C++ library)
– ISO 및 ANSI 표준 (ISO/IEC 14882)
– STL(Standard Template Library)
• 표준 데이터 구조에 대한 클래스 정의와 이러한 데이터 구조에 대한
주요 알고리즘 정의
– http://oopsla.snu.ac.kr/~sjjung/stl/ 참조
데이터 추상화 및 캡슐화의 장점
 소프트웨어 개발의 간소화
– 복잡한 작업을 여러 부분 작업들로 분해
 검사와 디버깅의 단순화
– 부분 작업
 재사용성 향상
– 자료 구조가 소프트웨어 시스템에서 독립적인 객체들로 구현되
어 재사용 가능
 데이터 타입 표현의 수정 용이
– 데이터 타입의 내부 구현을 직접 접근하는 부분만 변경 필요
– 그 이외 부분에는 무관
 프로그램의 신뢰성 증가
– 객체는 ADT에서 제공되는 연산을 통해서만 변경 가능하므로 객
체의 무결성 보장
성능 분석 및 측정
 프로그램에 대한 평가
– 평가 기준
• 명세에 부합 여부
• 코드의 판독 용이성
• 정확히 작동 여부
• 메모리의 효율적 사용
• 문서화
• 적절한 실행 시간
• 논리적 작업 단위를 위한
함수의 효과적 이용
 성능 평가
– 사전 예측  성능 분석(performance analysis)
• 시간과 공간의 추산
• 복잡도 이론(complexity theory) 이용
– 사후 검사  성능 측정(performance measurement)
• 컴퓨터 의존적인 실행 시간 측정
성능 분석: 공간 복잡도
 공간 복잡도(space complexity)
– 프로그램을 완전히 실행하는데 필요한 공간(메모리)의 양
– 고정 부분
• 프로그램 입출력 횟수나 크기와 관계없이 고정됨
• 예: 프로그램 코드, 단순 변수, 고정크기의 구조화 변수, 상수 등
– 가변 부분
• 입/출력의 수, 크기, 값 등 문제의 인스턴스(instance)의 특성에 의존
• 예: 가변 집단 변수, 순환 호출을 위한 스택 공간 등
– 프로그램 P의 공간 요구량
S(P) = c + SP(I)
• c : 상수(고정 부분에 해당)
• SP(I) : P의 인스턴스 I 에 의한 가변 공간 요구량
공간 복잡도 계산 예
예1.6: 단순 산술 함수
float abc(float a, float b, float c)
{
return a+b+b*c + (a+b-c) / (a+b) + 4.00;
}
고정 공간 요구만을 가짐
=> Sabc(I) = 0
예1.7: 수치값 리스트 합산 함수 (반복적 구현)
float sum(float list[], int n)
{
float tempsum = 0;
int i;
for (i = 0; i < n; i++)
tempsum += list[i];
return tempsum;
}
가변 공간 요구 : 배열 (크기 n)
- 함수에 대한 배열의 전달 방식에 따라 다름
- 배열 전체의 복사 전달 (예: Pascal)
. Ssum(I) = Ssum(n) = n
- 배열의 첫번째 요소의 주소 전달 (예: C)
. Ssum(n) = 0
공간 복잡도 계산 예
예1.8: 수치값 리스트 합산 함수 (순환적 구현)
float rsum(float list[], int n)
{
if (n) return rsum(list, n-1) + list[n-1];
return 0;
}
- 함수 호출시 마다 매개 변수, 지역 변수, 복귀 주소를 저장해야 함
- 한번의 순환 호출을 위해 요구되는 공간
• 두 개의 매개 변수와 복귀 주소를 저장하기 위한 바이트 수
↱ list[]의 주소
= 2 + 4 + 4 = 10
↳ sizeof(n)
↳ 복귀주소
• n = MAX_SIZE 일 때,
Srsum(MAX_SIZE) = 10 * MAX_SIZE
성능 분석: 시간 복잡도
 시간 복잡도(time complexity)
– 프로그램을 완전히 실행시키는데 필요한 컴퓨터 시간의 양
– 프로그램에 의한 소요 시간
T(P) = c + TP(I)
• c: 컴파일 시간  고정값
• TP(I): 실행시간
 인스턴스 특성 I에 따라 다름
• 예: 산술 연산 프로그램
TP(n) = Ca*ADD(n) + Cs*SUB(n) + Cm*MUL(n) +
Cd*DIV(n)
 n: 인스턴스 특성
 Ca, Cs, Cm, Cd : +, -, x, / 연산을 위한 상수 시간
 ADD, SUB, MUL, DIV : 인스턴스 특성 n에 대한 연산 실행 횟수
성능 분석: 시간 복잡도
 프로그램이 수행하는 연산의 횟수로 시간 복잡도를 추정
– 프로그램 단계 수 (step count)
 프로그램 단계(step)
– 실행 시간이 인스턴스 특성에 구문적으로나 의미적으로 독립성을
갖는 프로그램의 단위(세그먼트)
• 각 단계의 실행에 필요한 시간이 인스턴스 특성에 독립적이어야 함
 예: 입력의 수 n에 대한 시간 복잡도 계산 시, n개의 정수들의 덧
셈은 한 단계가 될 수 없음
• 각 단계의 실제 계산량은 서로 상이할 수 있음
 예: a = 2;
a = 2*b+c;
예1.9: 수치값 리스트 합산 함수 (반복)
1. Counter 이용
float sum(float *list, const int n) {
float s = 0;
count++; // 배정문
for (int i = 0; i < n; i++) {
count++; // for문
s += list[i];
count++; // 배정문
}
count++; // for문의 마지막 실행
return s;
count++; // return문
}
float sum(float *list, const int n)
for (int i = 0; i < n; i++)
count += 2;
count += 3;
2n+3
예1.9: 수치값 리스트 합산 함수 (반복)
2. 단계수 Table 작성
1
2
3
4
5
6
float sum(float *list, const int n)
{
float s = 0;
for(int i = 0; i < n; i++)
s += list[i];
return s;
}
행번호 s/e
1
0
2
1
3
1
4
1
5
1
6
0
빈도수 총 단계수
1
0
1
1
n+1
n+1
n
n
1
1
1
0
tsum(n) = 2n+3
예1.10: 수치값 리스트 합산 함수 (순환)
1
2
3
4
float rsum(float *list, const int n)
{
if (n <= 0) return 0;
else return (rsum(list, n-1)+list[n-1]);
}
단계 수 계산
trsum(n)
= 2 + trsum(n-1)
= 2 + 2 + trsum(n-2)
= 2 * 2 + trsum(n-2)
…
= 2n + trsum(0)
= 2n + 2
시간 복잡도 계산 예
 행렬의 덧셈
문 장
void add(int a[][MAX_SIZE] ··· )
{
int i, j;
for (i=0, i<rows; i++)
for(j=0, j<cols; j++)
c[i][j] = a[i][j] + b[i][j]
}
합 계
s/e
0
0
0
1
1
1
0
빈도수
총단계수
0
0
0
0
0
0
rows+1
rows+1
rows(cols+1) rows·cols+row
rows·cols
rows·cols
0
0
2rows·cols+2rows+1
성능 분석: 시간 복잡도
 프로그램 단계 수
– 프로그램 인스턴스 특성에 대한 함수
• 여러가지 특성들 중 관심이 있는 특성들에 대한 함수로 표현
 예: 입력의 수의 증가에 대한 시간 복잡도, 특정 입력의 크기가 증가
함에 따른 시간 복잡도 등
– 주어진 인스턴스 특성에 대해 단계 수를 유일하게 결정할 수 없는
경우
• 예: binary search
 원소의 수 n 외에, 탐색 키의 위치에 따라 단계수가 달라짐
• 최악의 경우(worse-case), 최선의 경우(best-case), 평균(average-case)
단계 수 계산
점근 표기법(O, , )
 Motivation
– 동일 기능의 두 프로그램의 시간 복잡도 비교
– 인스턴스 특성의 변화에 따른 실행 시간 증가 예측
 프로그램의 정확한 단계 수 계산 : 어렵고 무의미함
– 예: A의 단계수 = 3n + 3, B의 단계수 = 100n + 10
– A ≃ 3n (or 2n), B ≃ 100n (or 80n, 85n)
 근사치 사용
– C1과 C2가 음이 아닌 상수일 때, C1n2 ≤ Tp(n) ≤ C2n2 또는 TQ(n,m) = C1n +
C2m 등으로 표현 가능
– 충분히 큰 n에 대해 C1n2 + C2n > C3n 성립
• 예) C1= 1, C2 = 2, C3 = 100이고, n ≤ 98일때, C1n2 + C2n ≤ C3n,
n > 98일때, C1n2 + C2n > C3n
즉, 손익분기점(break even point)은 n = 98
• C1, C2, C3 및 손익분기점을 정확히 알아낼 필요는 없음
점근 표기법(O, , )
 정의: [Big "oh"]
f(n) = Ο(g(n))  ∃c,n0 > 0, s.t. f(n) ≤ cg(n) for ∀n, n≥n0
예
– n ≥ 2, 3n + 2 ≤ 4n
⇨ 3n + 2 = Ο(n)
– n ≥ 3, 3n + 3 ≤ 4n
⇨ 3n + 3 = Ο(n)
– n ≥ 10, 100n + 6 ≤ 101n ⇨ 100n + 6 = Ο(n)
– n ≥ 5, 10n2 + 4n + 2 ≤ 11n2
⇨ 10n2 + 4n + 2 = Ο(n2)
– n ≥ 4, 6*2n + n2 ≤ 7*2n
⇨ 6*2n + n2 = Ο(2n)
– n ≥ 2, 3n + 3 ≤ 3n2
⇨ 3n + 3 = Ο(n2)
– n ≥ 2, 10n2 + 4n + 2 ≤ 10n4
⇨ 10n2 + 4n + 2 = Ο(n4)
– n ≥ n0인 모든 n과 임의의 상수 c에 대해, 3n + 2 ≤ c 가 false인 경
우가 존재하므로
⇨ 3n+2≠Ο(1)
– 10n2 + 4n + 2 ≠ Ο(n)
점근 표기법(O, , )
 order of magnitude
– O(1)
: 상수(constant)
– O(log logn)
: log log
– O(logn)
: logarithmic
– O(n)
: 선형(linear)
– O(nlogn)
: log linear
– O(n2)
: 평방형(quadratic)
– O(n3)
: 입방형(cubic)
– O(2n)
: 지수형(exponential)
– O(n!)
: factorial
점근 표기법(O, , )
 f(n) = Ο(g(n))
– n ≥ n0인 모든 n에 대해 g(n) 값은 f(n)의 상한 값이라는 것을 의미
• 예: 3n+3 = O(n), 3n+3 = O(n2), 3n+3 = O(2n)
– 일반적으로 g(n)은 조건을 만족하는 가장 작은 함수여야 함
• 예: 3n+3 = O(n)으로 표현
– 정리: f(n) = amnm + … + a1n1+ a0  f(n) = O(nm)
– f(n) = O(g(n)) 
lim
n  
f ( n)
 c for some c R*
g ( n)
점근 표기법(O, , )
 정의: [Big "Omega"]
f(n) = Ω(g(n))  ∃c,n0 > 0, s.t. f(n) ≥ cg(n) for ∀n, n≥n0
예
– n ≥ 1, 3n + 2 ≥ 3n
⇨ 3n + 2 = Ω(n)
– n ≥ 1, 3n + 3 ≥ 3n
⇨ 3n + 3 = Ω(n)
– n ≥ 1, 100n + 6 ≥ 100n
⇨ 100n + 6 = Ω(n)
– n ≥ 1, 10n2 + 4n + 2 ≥ n2 ⇨ 100n2 + 4n + 2 = Ω(n2)
– n ≥ 1, 6*2n + n2 ≥ 2n
⇨ 6*2n + n2 = Ω(2n)
점근 표기법(O, , )
 f(n) = Ω(g(n))
– n ≥ n0인 모든 n에 대해 g(n) 값은 f(n)의 하한값이라는 것을 의미
• 예: 3n2+3 = Ω(n2), 3n2+3 = Ω(n), 3n2+3 = Ω(1)
– 일반적으로 g(n)은 조건을 만족하는 가장 큰 함수여야 함
• 예: 3n2+3 = Ω(n2)으로 표현
– f(n) = Ω(g(n)) 
lim
n  
f ( n)
  or
g ( n)
lim
n  
f ( n)
c0
g ( n)
점근 표기법(O, , )
 정의: [Theta]
f(n)=Θ(g(n))  ∃C1,C2,n0 > 0, s.t. C1g(n)≤f(n)≤C2g(n) for
∀n, n≥n0
예
– n ≥ 2, 3n ≤ 3n + 2 ≤4n
• c1 = 3, c2 = 4, n0 = 2
– 3n + 3 = Θ(n)
– 10n2 + 4n + 2 = Θ(n2)
– 6*2n + n2 = Θ(2n)
– 10*log n + 4 = Θ(log n)
⇨ 3n + 2 = Θ(n)
점근 표기법(O, , )
 f(n)=Θ(g(n))
– g(n)이 f(n)에 대해 상한 값과 하한 값을 모두 가지는 경우
– 일반적으로 g(n)의 계수는 1
– f(n) = Θ(g(n)) 
lim
n  
f ( n)
c
g ( n)
for some c R+
점근 표기법(O, , )
 점근적 복잡도(asymptotic complexity: Ο,Ω,Θ)
– 정확한 단계수의 계산없이 쉽게 구할 수 있음
– 예: sum(float *a, const int n)
1
2
3
4
5
6
float sum(float *a, const int n)
{
float s = 0;
for(int i = 0; i < n; i++)
s += list[i];
return s;
}
행번호 s/e 빈도수 총 단계
1
0
0
Θ(0)
2
1
1
Θ(1)
3
1
n+1
Θ(n)
4
1
n
Θ(n)
5
1
1
Θ(1)
6
0
1
Θ(0)
tsum(n) = Θ(max{gi(n)}) = Θ(n)
점근 표기법(O, , )
 예제 [이진 탐색]
– Worst-cast: Θ(log n)
– Best-case: Θ(1)
 예제 [순열]
– Θ(n(n!))
 예제 [Magic Square]
– Θ(n2)
매직 스퀘어
15
8
1
24
17
16
14
7
5
23
22
20
13
6
4
3
21
19
12
10
9
2
25
18
11
매직 스퀘어의 예
void magic(int n)
// 크기가 n이고 n은 홀수인 매직 스퀘어를 생성
{
const int MaxSize = 51; // 스퀘어의 최대 크기
int square[Max_Size][Max_Size], k, l;
// n이 올바른 값인지 검사
if((n > (Max_Size)) || (n < 1)){
cerr << "Error! ... n out of range" <<endl; return;
}
else if(!(n%2)) {
cerr << "Error! ... n is even" << endl; return;
}
// n이 홀수. Coxeter의 규칙이 사용됨
for(int i = 0; i < n; i++) // square를 0으로 초기화
for(int j = 0; j < n; j++)
square[i][j] = 0;
square[0][(n-1)/2] = 1; // 첫 행의 중간
// i와 j는 현재 위치
int key = 2; i = 0; int j = (n-1)/2;
while(key <= n * n){
// 왼쪽 위로 이동
if(i - 1 < 0) k = n - 1; else k = i - 1;
if(j - 1 < 0) l = n - 1; else l = j - 1;
if(square[k][l]) i = (i + 1) % n; // square가 채워짐. 아래로 이동
else{ // square[k][l]이 채워지지 않음
i = k;
j = l;
}
square[i][j] = key;
key++;
}
// 매직 스퀘어를 출력
cout << "magic square of size" << n << endl;
for(i = 0; i < n; i++) {
for(j = 0; j < n; j++)
cout << square[i][j] << " ";
cout << endl;
}
}
실용적 복잡도
 매우 큰 n에 대해서는, 작은 시간 복잡도를 가진 프로그램
(알고리즘)만이 실제적으로 사용 가능함
2
60
n
n
2
50
40
30
nlogn
20
f
10
n
logn
0
1
2
3
4
n
5
6
7
8
9
10
실용적 복잡도
time
1
log n
n
n log n
n2
n3
2n
n!
name
constant
logarithmic
linear
log linear
quadratic
cubic
exponential
factorial
1
1
0
1
0
1
1
2
1
instance characteristic n
2 4
8
16
1 1
1
1
1 2
3
4
2 4
8
16
2 8
24
64
4 16
64
256
8 64
512
4096
4 16
256
655536
2 24 40326 20922789888000
32
1
5
32
160
1024
32768
4294967296
263131033
 If a program needs 2n steps for execution,
– n=40 --- number of steps = 1.1*1012
in computer systems performing 1 billion steps/sec --- 18.3 min
– n=50 --- 13 days
– n=60 --- 310.56 years
– n=100 --- 4*1013 years