Page 29 -
P. 29

수학적 분석 프로그램 실행 시간을 설명하는 커누스 교수의 수학적 모델                      दп
               의 기본적인 개념은 간단하다. 전체 실행 시간은 다음과 같은 두 가지 요                   1024T
               소에 의해 결정된다.                                                 512T

                 • 각 문장을 실행하는 비용
                 • 각 문장을 실행하는 빈도
                                                                            64T
               여기서 첫 번째는 시스템의 속성, 두 번째는 알고리즘의 속성이다. 프로
               그램의 모든 명령에 대해 이 두 가지 속성을 모두 알면 각 명령을 실행하
               는 횟수를 곱해 모두 더함으로써 프로그램의 실행 시간을 구할 수 있다.                       8T
                                                                             4T
               이때 가장 힘든 것은 문장의 실행 빈도를 결정하는 것이다. 어떤 문장
                                                                             2T
               은 간단히 결정할 수 있다. 예를 들어 threesum.countTriples() 안에서
                                                                             T
               count를 0으로 초기화하는 문장은 딱 한 번만 실행된다. 다른 문장은 조
               금 더 깊이 생각해야 한다. 예를 들어 threesum.countTriples() 안에 있          ௼ӝ     1K  2K 4K  8K
               는 if 조건문은 정확히 n(n - 1)(n - 2)/6번 실행된다(입력 배열에서 세                  그림 4.1.3 로그-로그 그래프
               개의 숫자를 고르는 경우의 수와 일치한다. [연습문제 4.1.5] 참조).

               이와 같은 방식으로 빈도수를 분석하면 복잡하고 긴 수학 표현식이 만들어질 수 있다. 수학적 분석
               에서 문제를 단순화하기 위해 다음과 같은 두 가지 간단한 근사식(approximate expressions)을 만든다.
                                                             첫째, 물결식(tilde notation)이라고 하는
           import stdarray
           import stdio
                                                             수학 장치를 이용해 수식의 최고차항
           def writeTriples(a):
               # 연습문제 4.1.1 참조                               (leading term)을 이용한다. n이 커지면서
           def countTriples(a):                              f(n)으로 나누면 1에 수렴하는 모든 값
               n = len(a)                             1
               count = 0                                     을 ~f(n)으로 표기한다. 그리고 n이 커
               for i in range(n):
                   for j in range(i+1, n):            n      지면서 g(n)/f(n)이 1에 수렴하는 것을
                                                        2
             ղࠗ
                       for k in range(j+1, n):        ~n / 2  나타내기 위해 g(n)~f(n)으로 표기한다.
             ܖ೐
                                                        3
                           if (a[i] + a[j] + a[k]) == 0:  ~n / 6
                               count += 1                    이 표기법을 이용할 때는 작은 값을 나
               return count
                                                             타내는 복잡한 부분을 무시할 수 있다.
           def main():              ੑ۱ ؘ੉ఠী ٮۄ ߄Շח ࠗ࠙
               a = stdarray.readInt1D()                      예를 들어 threesum.py에 있는 if 조건
               count = countTriples(a)
                                                                    3
               stdio.writeln(count)                          문은 ~n /6번 실행된다고 할 수 있다.
               if count < 10:
                                                                                   2
                   writeTriples(a)                           n(n-1)(n-2)/6 = n /6 - n /2 + n/3
                                                                             3
           if __name__ == '__main__': main()
                                                                         3
                                                             인데, 이 식을 n /6으로 나눌 때 n이 커
                                      그림 4.1.4 프로그램 실행 빈도수 분석
                                                             지면서 이 식이 1에 수렴하기 때문이다.
               이 표기법은 최고차항 이외의 항들의 중요성이 상대적으로 적을 때 유용하다. 예를 들어 앞의 식에서
                            3
                                                    2
               n=1,000일 때, n /6 ≈ 166,666,667에 비해 - n /2 + n/3 ≈ -499.667의 중요성이 상대적으로 떨
               어진다. 둘째, 우리는 가장 자주 실행되는 명령에 집중한다. 이 명령들은 종종 프로그램의 내부 루프
               라고 한다. 이런 프로그램에서는 내부 루프 외부의 명령들이 실행하는 시간은 상대적으로 중요성이
               떨어진다고 간주한다.
               482
   24   25   26   27   28   29   30   31   32   33   34