Basic of Algorithm



알고리즘의 기초

주어진 문제를 풀기 위한 명령어들의 단계적 나열

  • 입출력
    • 0개 이상의 외부 입력 → 1개 이상의 출력
  • 명확성 - 각 명령은 모호하지 않고 단순 명확해야 함
  • 유한성 - 한정된 수의 단계를 거친 후에는 반드시 종료
  • 유효성 - 모든 명령은 컴퓨터에서 수행 가능해야 함

⇒ 하나 이상의 결과를 생성하기 위해 모호하지 않고 간단하며 컴퓨터가 수행 가능한 일련의 유한 개의 명령들을 순서적으로 구성한 것

  • 실용적 관점: 효율적이어야 함

알고리즘 생성 단계

설계 → 표현/기술 → 정확성 검증 → 효율성 분석

알고리즘의 설계

ex) 최댓값 찾기

1) 값들을 하나씩 모두 비교해가면서 찾는 방법

2) 토너먼트식 방법

→ 1): 순차탐색 2): 이진탐색

⇒ 무엇이 더 효율적인가? 둘은 조건이 다름. 1)의 경우 랜덤식 배열, 2)의 경우 순차적 배열이라는 조건이 있음

따라서, 주어지는 문제 속성 조건 등이 매우 다양하기 때문에 범용적인 설계 기법은 미존재

but, 대표적인 알고리즘 설계 기법

  • 분할정복 방법(divied-and-conqure)
  • 동적 프로그래밍 방법(dynamic programming)
  • 욕심쟁이 방법(greedy)

알고리즘 분석

  • 정확성 분석

    유효한 입력, 유한시간 내에 정확한 결과 생성 여부

    • 다양한 수학적 기법을 사용, 이론적 증명이 필요
  • 효율성 분석

    • 알고리즘 수행에 필요한 컴퓨터 자원의 양을 측정
    • 메모리 양 → 공간 복잡도(space complexity)
      • 정적 공간 + 동적 공간(컴파일 할 때의 필요 공간 + 실행할 때의 필요 공간)
    • 수행 시간 → 시간 복잡도(time complexity)

시간 복잡도

구현한 알고리즘을 컴퓨터에서 실행시켜 실제 수행 시간을 측정하는 방식이 아님.

⇒ 알고리즘의 단위 연산의 수행 횟수의 합

  • 시간 복잡도에 영향을 미치는 요인
    • 입력 크기

      → 입력으로 제공되는 데이터의 크기, 문제가 해결하려는 대상이 되는 개체의 개수

      (행렬의 크기, 리스트 원소의 수, 그래프 정점의 수 등)

    • 입력 데이터의 상태

      → 입력 크기 n이 증가하면 수행 시간도 증가

      ⇒ 단순히 수행되는 단위 연산의 개수의 합으로 표현하는 것은 부적절

      ⇒ 입력 크기 n에 대한 함수 f(n)으로 표현

      → 입력 데이터의 상태에 종속적

      • 평균 수행 시간
      • 최선 수행 시간
      • 최악 수행 시간 (수행 시간이 가장 안 좋은 경우)

        : 최악 수행 시간을 시간 복잡도로 사용

시간 복잡도 구하기

스크린샷 2022-03-08 오전 11.18.39.png

f(n) = 3n + 5 ——(점근성능)→ O(n)

점근성능

입력크기 n이 무한대로 커짐에 따라 결정되는 성능

ex) f(n) = 10n+9 f(n)=n2/2+3n

  • 데이터가 15개 이하인 경우 왼쪽이 성능이 더 좋음(합이 더 작음), 반면 16개 이상인 경우 오른쪽이 성능이 더 좋음

ex) f(n) = 2$n2$ + 5n + 200

  • n이 커지면 커질수록 1번 항(최고차항)이 최고항이 됨

⇒ 점근성능의 결정방법: 수행 시간의 다항식 함수에서 최고차항($n2$)만을 계수 없이 취해서 표현

  • 수행 시간의 정확한 값이 아닌 어림값 → 수행시간의 증가 추세를 파악하기 용이하다.

    → 알고리즘의 우열 표현에 용이함

점금성능의 표기법

  • 점근적 상한(최악의 경우 - Big-O)

함수 f와 g를 각각 양의 정수를 갖는 함수라고 한다면,

어떤 양의 상수 c와 $n0$이 존재하여 모든 n≥$n0$에 대하여 f(n)≤cg(n)이면 f(n)=O(g(n))이다.

(구한 알고리즘 f(n)이 최악의 경우에도 cg(n)보다 안 좋아질 수 없을 때, f(n)=O(g(n)))

  • 점근적 하한(최선의 경우 - Big-omega)

함수 f와 g를 각각 양의 정수를 갖는 함수라고 한다면,

어떤 양의 상수 c와 $n0$이 존재하여 모든 n≥$n0$에 대하여 f(n)≥cg(n)이면fn(n)=****Big-Ω****이다.

(구한 알고리즘 f(n)이 최선의 경우에도 cg(n)보다 더 좋아질 수 없을 때, f(n)=****Big-Ω****)

  • 점근적 상하한(상한과 하한을 모두 갖고 있는 경우 - Big-theta)

함수 f와 g를 각각 양의 정수를 갖는 함수라고 한다면,

어떤 양의 상수 c1, c2와 $n0$이 존재하여 모든 n≥$n0$에 대하여 c1g(n)≤f(n)≤c2g(n)이면fn(n)=****Big-θ****이다.

(구한 알고리즘 f(n)이 최선의 경우에도 cg(n)보다 더 좋아질 수 없을 때, f(n)=****Big-θ****)

⇒ 최고차항(상한), 최저차항(하한) n을 가져와 해당 기호 표기에 넣어주면 된다.

10n+9 → O(n)

n2/2+3n → O(n2)

O-표기 간의 연산 시간의 크기 관계

빅오표기법

시간 복잡도를 계산할때 상대적으로 불필요한 연산은 제거하여 알고리즘을 분석하여 좀더 간편하게 시간 복잡도를 표기하는 방법

양의 정수 n n 곱하는 경우
n*n = 2t = O(n)
n n 더함 = (2n+1)t = O(n)
n*n 1 더함 = (2n2+1)t = O(n2)

빅오표기법

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3)

Better O(1) < O(log n) < O(n) < O(n×log n) < O(n2) < O(2n) < O(n!) Worse

상수 함수 < 로그 함수 < 선형 함수 < 다항 함수 < 지수 함수 < 재귀 함수

  • 👇더 자세한 설명

    O(n) :

    • Linear 한 그래프가 그려진다.
    • 데이터 양에 시간이 정비례한다.
    • for문을 통한 탐색

    O(n log n) :

    • 데이터 양이 n배 많아진다면 실행시간은 n배보다 log n 배 더 많아진다.
    • Quick sort, merge sort

    O(n^2) :

    • 데이터 양에 따라 걸리는 시간은 n의 제곱에 비례하게 된다.
    • 2중 for문
    • bubble sort, insertion sort
    • 대개 이와 같은 경우에는 시간 초과가 발생한다.

        void re(){
            for (int i=0;i<N;i++){
            	for (int j=0;j<N;j++){
                	i++;
                }
            }
        }
      

    O(2^n) :

    • 급격하게 복잡도가 증가한다
    • 피보나치 수열이 그 예

        void fibonacci(int n, int r){
            if (n<=0)
            	return 0;
            else if (n==1)
            	return r[n] = 1;
            return r[n] = fibonacci(n-1,r) + fibonacci(n-2,r);
        }
      

알고리즘의 시간복잡도 구하기

  • 알고리즘의 수행시간 f(n)을 구한 후, f(n) = O(g(n))을 만족하는 최소 차수의 함수 g(n)을 찾는다.

    알고리즘에 나타난 루프의 반복횟수를 조사하여 시간복잡도를 취한다.

      i = 1; // 1
      while(i <= n) { // n + 1
      	x = x+1; // n
      	i = i+1; // n
      }
    

    ⇒ 3n + 2 → O(n)

      int i, j;
      for(i = 1; i <= n; i++) // n
      	for (j = i; j <= n; j++) // n
      		if (j > i) 
      			C[ij] = A[i]*B[j];
    

    ⇒ O(n2)

순환 알고리즘의 성능

순환(recursion, 재귀)

알고리즘의 수행 과정에서 자기 자신의 알고리즘을 다시 수행하는 형태

점화식 표현

T(n) = T(n/2) + O(1), T(1)=c1

(*T(n) Time Complexity: 알고리즘을 수행하는데 이루어지는 연산 (산술, 대입, 비교, 이동)수)

data n개를 찾는데 걸리는 시간 = n/2개를 찾는 데 걸리는 시간 + 상수시간(무조건 걸리는 시간)

  • 기본 점화식과 폐쇄형

    • 2) 퀵 정렬의 최악의 수행 시간
    • 3) 이진 탐색의 수행 시간
    • 6) 합병 정렬의 수행시간, 퀵 정렬의 최선의 수행시간