알고리즘을 설명하고 입증하고 분석하는 방법?


20

TAOCP (Art of Computer Programming)를 읽기 전에는 이러한 질문을 깊이 고려하지 않았습니다. 나는 의사 코드를 사용하여 알고리즘을 설명하고 알고리즘을 이해하고 성장 순서에 대해서만 실행 시간을 추정합니다. TAOCP는 철저하게 내 마음을 변경합니다.

TAOCP 는 단계와 goto 가 혼합 된 영어를 사용 하여 알고리즘을 설명하고 순서도를 사용하여 알고리즘을보다 쉽게 ​​그림으로 표현합니다. 저수준으로 보이지만, 특히 많은 차트에서 무시했던 몇 가지 장점이 있습니다. 우리는 계산이 그 화살표를 가로 지르는 시점에서 현재 상황에 대한 주장으로 각 화살표에 레이블을 붙일 수 있고 알고리즘에 대한 귀납적 증거를 만들 수 있습니다. 저자는 말합니다 :

저자의 주장은 그림 4에서와 같이 우리의 마음이 모든 주장을 암묵적으로 채운 시점에 도달했을 때만 알고리즘이 유효한 이유를 실제로 이해한다는 것입니다.

나는 그런 것들을 경험하지 않았다. 또 다른 장점은 각 단계가 실행 된 횟수를 계산할 수 있다는 것입니다. Kirchhoff의 첫 번째 법칙으로 쉽게 확인할 수 있습니다. 실행 시간을 정확하게 분석하지 않았으므로 실행 시간을 추정 할 때 일부 이 생략되었을 수 있습니다.±1

성장 순서 분석은 때때로 쓸모가 없습니다. 예를 들어, quicksort와 heapsort를 구분할 수 없습니다. 이들은 모두 이므로 는 임의의 변수 의 예상 개수 이므로 상수를 분석해야합니다. 및 이므로 과 더 나은 . 또한 때때로 분산과 같은 다른 수량을 비교해야합니다. 실행 시간의 증가 순서에 대한 대략적인 분석만으로는 충분하지 않습니다. 로 TAOCP이자형(())=Θ(로그)이자형엑스엑스E(T1(n))=A1nlgn+B1n+O(logn)E(T2(n))=A2lgn+B2n+O(logn)T1T2 알고리즘을 어셈블리 언어로 변환하고 실행 시간을 계산합니다. 너무 어렵 기 때문에 실행 시간을 조금 더 대략적으로 분석하는 기술을 알고 싶습니다 .C, C ++와 같은 고급 언어의 경우 유용합니다. 또는 의사 코드.

연구 작업에 주로 사용되는 설명 스타일과 이러한 문제를 처리하는 방법을 알고 싶습니다.


6
알고리즘의 실행 시간을 이와 밀접하게 비교할 때는 매우주의해야합니다. 실제 컴퓨터에는 캐시, 레지스터 및 파이프 라인이있어 실행 시간을 크게 변경할 수 있습니다. 어떤 알고리즘이 실제로 더 빠른지 알아 보려면 실제로 컴퓨터에서 알고리즘을 실행해야합니다.
svick 2016 년

1
실제로 Knuth와 같은 어셈블러를 분석하는 것은 숨겨진 코드가없고 제어 흐름이 쉬워 실제 코드를 분석하는 보다 훨씬 쉽습니다. 당신은 연습을 요구하고 있습니다; 내 생각 데이브의 댓글이 적용됩니다. 실무자는 엄격한 분석을 수행하는 것보다 런타임 측정을 사용하여 알고리즘을 엔지니어링 할 가능성이 높습니다. 그러나 나는 개업의가 아니므로 소금 한알로 말한 것을 취하십시오.
Raphael

1
@Raphael My 는 실제로 프로그래밍이 아니라 연구 작업 을하는 것을 의미합니다 .
Yai0Phah

@ 프랭크, 분산 이란 무엇입니까? 내 성능 테스트는 타이밍 차이를 제공합니다.
edA-qa mort-ora-y

@Raphael, 첫 번째 요점은 더 이상 사실이 아닙니다. 최신 칩은 어셈블리를 재정렬하고, 비 순차적으로 저장 /로드하고, 예측 실행 및 로딩을 수행합니다. 동시성 및 이전 문제의 경우 실제로 철저한 분석이 필요하지만 공식적인 형식으로는 수행하지 않습니다.
edA-qa mort-ora-y

답변:


18

가능한 다양한 접근 방식이 있습니다. 가장 적합한 것은

  • 무엇 당신이 보여주기 위해 노력하고있다,
  • 얼마나 많은 세부 사항 당신이 원하는 또는 필요.

알고리즘이 서브 루틴으로 사용하는 것으로 널리 알려진 알고리즘 인 경우 종종 상위 레벨로 유지됩니다. 알고리즘이 조사중인 주요 객체 인 경우 자세한 정보를 원할 수 있습니다. 분석에 대해서도 마찬가지입니다. 거친 상한 런타임 경계가 필요한 경우 정확한 수의 명령문을 원할 때와 다르게 진행합니다.

잘 알려진 알고리즘 Mergesort에 대한 세 가지 예를 제시하겠습니다.

높은 레벨

Mergesort 알고리즘은 목록을 가져와 두 개의 (약) 동일하게 긴 부분으로 나누고 부분 목록에서 되풀이하여 (정렬 된) 결과를 병합하여 최종 결과를 정렬합니다. 싱글 톤 또는 빈 목록에서는 입력을 반환합니다.

이 알고리즘은 분명히 올바른 정렬 알고리즘입니다. 리스트를 나누고 병합하는 것은 각각 시간 구현 될 수 있으며 , 최악의 경우 런타임 . 마스터 정리에 의해, 이것은 됩니다.T ( N은 ) = 2 T ( N을Θ()T(n)Θ(nlogn)()=2(2)+Θ()()Θ(로그)

중간 수준

Mergesort 알고리즘은 다음 의사 코드로 제공됩니다.

procedure mergesort(l : List) {
  if ( l.length < 2 ) {
    return l
  }

  left  = mergesort(l.take(l.length / 2)
  right = mergesort(l.drop(l.length / 2)
  result = []

  while ( left.length > 0 || right.length > 0 ) {
    if ( right.length == 0 || (left.length > 0 && left.head <= right.head) ) {
      result = left.head :: result
      left = left.tail
    }
    else {
      result = right.head :: result
      right = right.tail
    }
  }

  return result.reverse
}

유도에 의한 정확성을 증명합니다. 길이가 0 또는 1 인 목록의 경우 알고리즘이 간단합니다. 유도 가설로서, 임의의 고정 된 자연 대해 mergesort최대 길이의 목록에서 올바르게 수행 한다고 가정 합니다 . 이제 을 길이 의 목록으로 만드십시오 . 인덕션 가설에 의해, 및 홀드 (비 점감) 제 RESP 버전의 정렬. 재귀 호출 후 후반 . 따라서 루프는 모든 반복에서 아직 조사되지 않은 가장 작은 요소를 선택하여 추가합니다 . 따라서 and의 모든 요소를 ​​포함하는 점점 정렬되지 않은 목록입니다.n > 1 L n + 1 L L>1+1leftrightwhileresultresultleftright. 그 반대는 감소하는 정렬되지 않은 버전으로 , 반환 된 원하는 결과입니다.

런타임과 관련하여 요소 비교 및 ​​목록 작업을 계산합니다 (런타임을 무조건적으로 지배 함). 길이가 2보다 작은 목록도 원인이되지 않습니다. 길이가 인 리스트의 경우, 재귀 호출을위한 입력 준비, 재귀 호출 자체의 루프 및 하나에 대한 오퍼레이션이 있습니다. 두 재귀 매개 변수는 각각 최대 목록 작업 으로 계산할 수 있습니다 . 루프는 정확히 실행 많아야 하나의 소자에 비해 시간마다 반복 원인을 정확히 두리스트 연산. 을 사용하도록 최종 구현 가능n n 2 n>1whilereversewhilereverse2목록 작업-모든 요소가 입력에서 제거되어 출력 목록에 추가됩니다. 따라서 작업 횟수는 다음과 같은 반복을 충족합니다.

(0)=(1)=0()(2)+(2)+7

마찬가지로 명확 비 감소, 그것을 고려하기에 충분한 점근 성장한다. 이 경우 반복은 다음과 같이 단순화됩니다.N = 2 K=2케이

(0)=(1)=0()2(2)+7

마스터 정리에 의해의 실행 시간으로 확장되는 을 얻습니다 .Θ(로그)mergesort

초 저레벨

Isabelle / HOL 에서 Mergesort의 (일반화 된) 구현을 고려하십시오 .

types dataset  =  "nat * string"

fun leq :: "dataset \<Rightarrow> dataset \<Rightarrow> bool" where
   "leq (kx::nat, dx) (ky, dy) = (kx \<le> ky)"

fun merge :: "dataset list \<Rightarrow> dataset list \<Rightarrow> dataset list" where
"merge [] b = b" |
"merge a [] = a" |
"merge (a # as) (b # bs) = (if leq a b then a # merge as (b # bs) else b # merge (a # as) bs)"

function (sequential) msort :: "dataset list \<Rightarrow> dataset list" where
  "msort []  = []" |
  "msort [x] = [x]" |
  "msort l   = (let mid = length l div 2 in merge (msort (take mid l)) (msort (drop mid l)))"
by pat_completeness auto
  termination
  apply (relation "measure length")
by simp+

여기에는 이미 명확하고 종료 된 증거가 포함되어 있습니다. 여기 에서 (거의) 완전한 정확성 증명을 찾으 십시오 .

"런타임", 즉 비교 횟수의 경우, 이전 섹션과 유사한 반복이 설정 될 수 있습니다. 마스터 정리를 사용하고 상수를 잊어 버리는 대신이를 분석하여 실제 양과 같은 점에서 근사치를 구할 수 있습니다. 전체 분석은 [1]에서 찾을 수 있습니다. 대략적인 개요는 다음과 같습니다 (Isabelle / HOL 코드에 반드시 맞지는 않습니다).

위와 같이 비교 횟수의 재발은

에프0=에프1=0에프=에프2+에프2+이자형

여기서 은 부분 결과를 병합하는 데 필요한 비교 횟수입니다 ². 바닥과 천장을 제거하기 위해 이 짝수 인지 여부에 대해 대소 문자 구분을 수행합니다 . n이자형

{f2m=2fm+e2mf2m+1=fm+fm+1+e2m+1

중첩 사용하여 역방향 / 정방향으로 차이 의 하고 우리가 얻을e nfnen

k=1n1(nk)Δfk=fnnf1 .

이 합계는 Perron 공식 의 오른쪽과 일치합니다 . 우리는 정의 디리클레 발생 시리즈 의 로Δfk

W(s)=k1Δfkks=112sk1Δekks=: (s)

Perron의 공식과 함께

fn=nf1+n2πi3i3+i(s)ns(12s)s(s+1)ds 입니다.

평가 는 분석되는 사례에 따라 다릅니다. 그 외에는, 약간의 속임수 후에 잔류 물 정리 를 적용하여(s)

에프로그2()+에이(로그2())+1

여기서 는 값을 갖는 주기적 함수입니다 .에이[1,0.9]


  1. Mellin 변형 및 무증상 : Flajolet and Golin 의 합병 재발 (1992)
  2. 가장 좋은 경우 : 최악의 경우 : 평균 사례 :이자형=2
    이자형=1
    이자형=22+122+1

실행 시간 분석에 대한 내 질문은 및 정확하게 결정하는 방법 정확히 연습하기에 가까운 (예를 들어, merge-sort와 qsort를 비교하는 것이 가능합니다). αβ()=(/2)+(/2)+α+β
Yai0Phah

@ 프랭크 : 짧은 대답은 당신이 할 수 없습니다 ; 상수는 기본 알고리즘에 중요하지 않은 머신 아키텍처, 언어 및 컴파일러를 포함한 구현 세부 사항에 따라 다릅니다.
JeffE

@JeffE 나는 와 가 약간의 비교 를 할 정도로 정확해야한다고 주장해야합니다 . 간단히 말해, 기계 언어없이 많은 작업을 수행 하여 상수를 결정할 수있는 수학적 모델입니다 . αβ
Yai0Phah

예를 들어 @JeffE는 taocp의 MIX / MMIX이지만 알고리즘을 기계 언어로 번역하기는 너무 어렵습니다.
Yai0Phah

@FrankScience : 실습에 가까워 지려면 모든 작업 을 계산 해야 합니다 (Knuth처럼). 그런 다음 머신 별 운영 비용으로 결과를 인스턴스화하여 실제 런타임을 얻을 수 있습니다 (작업 순서, 캐싱, 파이프 라인 등의 영향 무시). 일반적으로 사람들은 일부 작업 만 계산하므로이 경우 및 수정 하면 많은 것을 알 수 없습니다. αβ
Raphael

3

Dijkstra의 "프로그래밍 원칙" 은 알고리즘 분석 및 증명과 확률 설계에 관한 것입니다. 이 책의 서문에서 Dijkstra는 적절하게 분석되도록 설계된 매우 간단한 구성의 미니 언어가 많은 알고리즘을 공식적으로 설명하기에 어떻게 충분한지를 설명합니다.

,이는 "어떤 프로그래밍 언어 내가 사용에 갈거야?"같은 책을 시작할 때, 하나는 즉시 문제에 직면 하지프레젠테이션의 단순한 질문! 모든 도구의 가장 중요하지만 가장 애매 모호한 측면은 도구 사용에 익숙해지는 습관에 대한 영향입니다. 도구가 프로그래밍 언어 인 경우,이 영향은 우리가 좋아하든 그렇지 않든 사고 습관에 영향을 미칩니다. 내가 아는 한 그 영향을 분석 한 결과, 기존 프로그래밍 언어 나 그 일부가 내 목적에 맞지 않을 것이라는 결론에 도달했다. 반면에 나는 5 년 동안 그렇게하지 않겠다고 맹세 한 새로운 프로그래밍 언어를 설계 할 준비가되어 있지 않다는 것을 알고 있었고 그 기간이 아직 지나지 않았다는 가장 뚜렷한 느낌을 받았습니다! (그 전에 많은 것들 중에서이 논문이 쓰여 져야했다.

나중에 그는 자신의 미니 언어를 구사할 수있는 규모가 얼마나 작은지를 설명합니다.

나는 독자들에게 왜 미니 언어가 너무 작아서 절차와 재귀조차 포함하지 않는지 설명했다. ... 요점은 내 메시지를 전달하기 위해 필요하지 않다는 것입니다. 모든면에서 고품질 프로그램을 설계하기 위해 신중하게 선택된 관심의 분리가 어떻게 필요한가; 미니 언어의 겸손한 도구는 이미 사소하지만 매우 만족스러운 디자인에 충분한 위도를 제공했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.