앞서 포스팅 한 "뉴욕의 프로그래머" 라는 책에서 언급하고 있는 이야기 중 하나이다. 
원래 이것은 조수아 블로흐( Joshua Bloch ) 라는 유명한 소프트웨어 엔지니어가 구글랩 블로그에 올린 글에서 유래하는 이야기이다.  조수아 블로흐는 자바 플랫폼의 유명한 개발자이자 Effective Java (2001년 Jolt Award 수상작)  의 저자이기도 하다. 현재 구글에서 자바 아키텍트로 근무중.

글의 원문 : Extra, Extra - Read All About It: Nearly All Binary Searches and Mergesorts are Broken

저자가 카네기멜론 대학의 박사과정에 입학해서, 알고리즘 수업을 듣던 첫날, 존 벤틀리 교수 (Jon Bentley, Programming Pearl 의 저자, 국내에는 "생각하는 프로그래밍" 이란 책으로 번역. ) 가 학생들에서 이진검색을 구현해 보라고 시킨 후에 그중 몇명을 앞에 나와서 발표하라고 했을 때의 충격을 아직도 기억하고 있다고 쓰고 있다. 물론, 당연하게도 ^^, 대부분의 학생들이 구현한 이진검색은 버그가 있었다.

문제는 자바 라이브러리에 기본으로 포함되어 있던 이진검색 조차도 버그가 숨어있었다는 것이다. 아래 글을 읽어보면 왜 20 여년간이나 이 버그가 쉽게 발견되지 않았는지에 대한 설명이 있다. 수십년간 사람들이 아무 문제없이 써오던 대중적인 라이브러리에도 이와같이 숨어있는 버그를 지적하면서 Bug-Free 소프트웨어가 얼마나 힘든 것인지를 말하고 있다.

최근까지 Java.util.Arrays 에 포함되어 기본 라이브러리로 제공되던 바이너리 서치의 코드는 아래와 같다.


자, 이 코드의 어떤 부분에 버그가 있을까..

정답은 아래 라인이다.

만약 low 와 high 가 충분히 큰 숫자라서 이 두수의 합이 integer 의 범위 ( 32 비트의 경우 2,147,483,648 ) 를 넘어서게 되면 결국 Overflow 가 발생하게 된다. 그래서  이 부분에서
mid 값을 배열 a 의 인덱스로 사용한 부분에서 배열의 잘못된 인덱스로 접근하게 되어 생기는 
ArrayIndexOutOfBoundsException 에러가 나게 된다.

 * 주의사항 : 조수아 벤틀리는 원문에서 두 양수의 합이 Overflow 가 나는 경우 그 결과는 음수가 되어 배열의 인덱스를 음수로 접근하게 되어 ArrayIndexOutOfBoundsException 에러가 난다고 쓰고 있으나, 후에 덧글을 통해서 C99 스펙의 경우 signed integer 의 두 수의 overflow 의 경우 항상 음수가 되는 것은 아닌 undefined 가 된다고 수정하고 있다. 어쨌든 에러는 에러이다... ^^ ;

조수아 벤틀리는 이 라인을 아래와 같이 고치는 것이 좋겠다고 쓰고 있다. ( C / C++ 의 경우 )
 
참고로 비트연산자 >> 1 은 /2 와 같다. >> 1 과 같이 비트연산으로 계산을 할 경우 /2 보다 조금 더 빠르다. ;)

- 그런데, 조수아 벤틀리가 제시한 저 수정안도 low 와 high 의 값의 합이 unsigned int 의 범위를 넘게 된다면 여전히 Overflow 문제가 생길 수 있다... -_-;  결국 ASSERT 등을 사용해서 인덱스의 범위 내에서만 사용이 가능하도록 제약을 걸거나, Overflow 를 피하려면 여유있게 long long 과 같은 64 비트 변수를 쓰는 것이 보다 안전할 것 같다.


저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/382 관련글 쓰기
댓글을 달아주세요!
  1. 김훈동 2010/06/30 12:59  댓글주소  수정/삭제  댓글쓰기

    위 내용하고는 다른 내용인데..
    최근에 발견한 win32 api 버그는 머냐면... 소켓통신 관련된 모듈인데..
    xp 에서는 잘되는 놈인데... vista 랑 windows7 에서는 버그가 있더라고..왜그러나 하고 디버깅을 해보니까...
    ip 를 처리하는 모듈이 vista 이상의 os 에서 default 로 활성화 되어 있는 ipv6 땜에 ipv6 로 ip 를 받았다가
    내부적인 코드에서 문제가 발생하고 있더라고...

    또 하나 위하고는 다른 내용인데...
    아폴로 XX 호 던가... 그 40초만에 로켓트가 터져버린 사건...
    그사건의 원이이 바로.. 저런거 때문이었었다지.... 하드웨어적인 결함이 아닌 숫자형 type 하나를 잘못쓰는것으로 인하여
    로켓트가 폭발해버리는..

    • BlogIcon soyoja 2010/07/08 01:46  댓글주소  수정/삭제

      오호... 그런 일이 있었군...

      근데 그 공중폭팔한 아폴로 호는 내가 듣기로는 연료 계통에서 문제가 있어서 폭팔했다고 하던데.. 소프트웨어 문제도 있었낭..??

  2. BlogIcon hyperdash 2010/07/03 00:37  댓글주소  수정/삭제  댓글쓰기

    포스팅이 프로그래밍 관련 일색이구나....

    일상에서는 포스팅 할 것이 없어진 것이냐...

    • BlogIcon soyoja 2010/07/08 01:47  댓글주소  수정/삭제

      있긴한데 요새 자꾸 게을러져서...

      그리고 그냥 공부하는 거 가끔 블로깅 하는게 나한테 맞는 거 같아서...

      개인적인 이야기 블로그에 올리는 건 별로 안좋아해서..

  3. 2010/07/13 23:36  댓글주소  수정/삭제  댓글쓰기

    비밀댓글 입니다

이름 암호 홈페이지




친구들이 좋은 답변을 해 줬는데..

일단 정답들은 다음과 같다.

1. 임의의 원소들을 갖고있는 A 와 B 의 두 집합 (set) 이 있다. A 가 B 의 부분집합인지 여부를 확인하는 방법에 대해 설명하시오. ( 책에서는 세가지 해법을 소개했다. )

- A 의 원소의 갯수를 m, B 의 원소의 갯수를 n 이라 할 때.

정답 1 ) 가장 무식한 Brute Force 방법.  A 의 모든 원소에 대해서 루프를 돌면서 각각의 원소들이 B 에 존재하는지를 검사한다. A 와 B 에 대해서 이중 루프를 돌기 때문에 시간 복잡도는 O(m*n) 이 된다.

정답 2 ) A 와 B 를 정렬한 다음에 1 과 같은 작업을 수행한다. 정답 1 과 다른 점은 A 의 i 번째 원소에 대해서 B 에서 존재하는 지를 찾았을 경우 ( 이때 발견한 B 의 인덱스를 j 라 하면 ),  그 다음번 검색은 A 의 i+1 번째 원소부터 B 의 j 번째 인덱스에서 검색 작업을 시작하기 때문에 정답 1 처럼 매번 A 와 B 에 대해서 전체 루프를 돌 필요가 없다는 점이다.( 중복이 없을 경우 j+1 번째 인덱스 부터 시작. 이 내용들을 책에서는 그림을 그려가면서 몇페이지에 걸쳐서 설명하고 있는데 여기에서는 이정도만 써도 이해하리라 믿고... )
이 경우 시간복잡도는 A 와 B 를 정렬하는데 소요되는 O(mlongm + nlogn) 이 된다.
사실 이 문제는 도널드 크누스 교수가 "정렬과 검색" 의 관계에 대해서 설명하기 위해서 처음 고안해 낸 문제라 한다. "대부분의 데이터는 정렬을 할 경우 검색 작업이 매우 쉬워진다" 는 것인데, 사실 얼핏 보기에 당연해 보이기도 하지만 나는 저 이야기를 완전히 이해하는데 오랜 시간이 걸렸다. -_-;

정답 3 ) 해쉬를 이용한다. 즉, B 를 모두 해쉬 자료구조에 담아놓은 후, A 가 B 에 속하는지 여부만 검사하면 된다. 해쉬 자체의 시간복잡도는 O(1) 이지만 B 의 원소 전체를 해쉬에 담기 위한 과정 ( 시간복잡도 O(n) ) 과 A 의 모든 원소가 해쉬 내에서 존재하는지를 검사하기 위해서 A 의 원소 전체를 한번 검사하는 과정 ( 시간복잡도 O(m) ) 이 소요되므로 전체 시간복잡도는 O(m+n) 이 된다.
정답 3 의 경우 정답 1, 2 에 비해서 상대적으로 빠르지만 해쉬 테이블의 크기 만큼 메모리를 사용하는 trade off 가 있다는 것도 알아두어야 한다.

이 문제를 굳이 블로그에 포스팅 한 이유는 이상의 세가지 방법 이외에 혹시 다른 방법은 없을까 하는 생각에서 글을 올린 것이었다.
사실 이 문제를 처음 읽었을때 저 세가지 방법 정도만 생각이 났었고 책에서도 세가지의 해법만 제시했었지만 잘 생각해보면 다른 해법도 있지 않을까 하는 생각이 든다. 혹시 다른 아이디어가 있으신 분은 댓글로 달아 주시면 감사하겠다..

2. 물컵안에 물이 들어있다. 다른 어떠한 도구도 사용하지 않고 이 물컵의 물이 절반이 되는지를 확인하는 방법은 무엇일까? 

정답은 컵을 기울여서 컵의 물이 아래와 같이 되는지를 보고 판단하면 된다. 즉, 컵을 기울여서 물이 컵 입구 끝에 도달했을 때 반대편 끝이 컵의 바닥 모서리에 닿는지 여부를 보면 된다.


책에 삽입된 그림에서 가져옴.



저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/379 관련글 쓰기
댓글을 달아주세요!
  1. 김훈동 2010/06/22 17:44  댓글주소  수정/삭제  댓글쓰기

    ㅇㅇ.... 1번의 Brute Force 만 언급 안했고 ,1번 2번 다 맞춘거 같긴 한데.. 1번의 2번째 알고리즘을 나는 정렬은 시간복잡도 계산 빼고..정렬된 데이타에 대한 검색복잡도를 N+M 이라 했는데..이 저자는 m+n 은 빼버리고 검색에 대한 시간복잡도를 써놨네... 검색까지 넣으면 검색후에도
    이중포문은 아니지만 1중 포문 안에서 N 의 인덱스를 scan 해서 M 과 포인터를 맞춰가며 진도가 나가니까
    O( mlongm + nlogn + m + n ) 이어야 하지 않나? 정렬과 스켄을 동시에 하긴 힘들거 같은뎅..

    • BlogIcon soyoja 2010/06/23 08:49  댓글주소  수정/삭제

      Big O 표기법상 시간복잡도는 가장 높은 차수만 표시하니까 O( mlogm + nlogn + m + n ) = O( mlogm + nlogn ) 이 되는거지... ;)

이름 암호 홈페이지



알고리즘 수업시간에 소개받은 문제.

어딘가에서 한번 들어본 적은 있긴 한데... 수업시간에 다시 배우니 아주 새롭다.

1975 년 Steve Selvin 이라는 수학자가 처음 소개한 이래 학자들 사이에서 한동안 많은 논란이 일었던 파라독스 문제라 한다.

문제는 다음과 같다.
당신은 어느 게임쇼에 참가하였다. 이 게임쇼에서는 3 개의 방이 있고 각각의 방 뒤에 임의의 2 곳에는 염소가 있고 나머지 한 곳에는 자동차가 있다. 참가자는 이중 어느 한 방을 선택해서 열어볼 수 있다. 게임쇼의 룰에서는 참가자가 자동차가 있는 문을 선택해서 연다면 이 자동차를 상품으로 받을 수 있고, 염소가 있는 방을 선택해서 연다면 "꽝" 이 된다. 당신이 어떤 방을 열지 결정한 상태에서, 사회자가 당신이 선택하지 않은, 염소가 있는 어느 한 방을 열어서 보여 주면서 당신이 결정한 방을 바꿀 수 있는 기회를 준다고 하자. 이때 처음의 결정을 번복하고 다른 방을 선택하는 것이 좋을까? 아니면 그냥 처음 결정했던 방을 그대로 선택하는 것이 좋을까?


"Suppose you're on a game show, and you're given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what's behind the doors, opens another door, say No. 3, which has a goat. He then says to you, "Do you want to pick door No. 2?" Is it to your advantage to switch your choice?"

사용자 삽입 이미지


얼핏 생각할 때는 선택을 번복하던 번복하지 않던 별 차이가 없어 보인다. 그래서 이 문제가 소개된 이후 한동안 학자들 사이에서도 논란이 있었단다...
.
.
.
.
.
.
.
.
.
.










답은, 선택을 바꾸어서 다른 방을 열어보는 것이 66% 의 확률로 자동차가 있는 방을 선택할 수 있다.  그 이유는 아래와 같다.

참가자는 1, 2, 3 번과 같이 방을 선택할 수 있다. ( 이 경우 차를 선택할 확률은 33% ) 그가 자신의 결정을 번복하지 않는 다면 어쨌든 차를 선택할 확률은 33.3% 가 될 것이다.

그런데 결정을 번복하는 경우는 아래와 같이 된다.
사용자 삽입 이미지
 그림 출처 : Wikipedia


위 그림은 참가자가 처음에 어느 방을 선택했는데, 게임 쇼 진행자가 염소가 있는 방을 보여주었을 때, 선택한 방을 바꾼 경우를 설명하고 있다. 1 번과 같이 처음부터 차가 있는 방을 선택했다면, 선택을 바꿀 경우에는 차를 얻을 수 없다. 반면에 2 와 3 처럼 처음에 염소가 있는 방을 선택했다면, 선택을 바꿀 경우 차를 얻을 수 있다. 결국 선택을 바꿀 경우에는 위의 그림처럼 차를 얻을 확률이 2/3 = 66.6% 가 된다.

교수님의 설명은, 게임 쇼 진행자가 염소가 있는 방을 보여주고, 참가자가 이 것을 본 상태에서 자신의 결정을 번복한 것은 결국 참가자가 방을 두 번 선택한 것과 같은 효과가 있다고 볼 수 있다는 것이다. 그러므로 이 경우 확률은 2/3 이 되는 것이다.


ps ) 이번 학기에 수강중인 "알고리즘 분석" 이란 과목이 있는데... 이제 2 번 들었을 뿐 이지만 아주 대만족이다. 금년에 새로 부임하신
교수님 수업인데...  매 수업시간마다 알고리즘으로 해결 가능한 문제들도 많이 소개를 해 주시고, 각 알고리즘에 대한 증명과 문제점들에 대해서 생각해 보는 시간을 가지면서 명쾌하게 진행하신다. 수강인원도 단 3 명 뿐이라서 거의 개인과외 수준으로 배우는 느낌이다 ㅋㅋ


http://en.wikipedia.org/wiki/Monty_Hall_problem
크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/331 관련글 쓰기
댓글을 달아주세요!
  1. 2009/09/15 23:32  댓글주소  수정/삭제  댓글쓰기

    비밀댓글 입니다

  2. 고글 2009/09/15 23:50  댓글주소  수정/삭제  댓글쓰기

    영화 21에서도 나왔던 문제~

  3. 2009/09/16 00:13  댓글주소  수정/삭제  댓글쓰기

    요즘 한요섭교수님 오토메타듣는데 수업 재밌네요 :) 근데 막 오셨으니 학기 후반부에 소위 빡-_-세지 않을까 무서워하고 있습니다 헤헤[...]

  4. BlogIcon hyperdash 2009/09/17 17:07  댓글주소  수정/삭제  댓글쓰기

    그림으로 보니까 명쾌하구나... 예전에 글로 봤었을 때는 잘 이해가 안되었었는데

    걍 이해가 팍팍 되네~

이름 암호 홈페이지



Problem Solving 의 정수론 분야 중 두 수의 최대공약수최소공배수를 이용한 문제들은 매우 자주 등장하는 편이다.
구하는 방법은 여러가지가 있는데...


방법 1) 소인수 분해 (Prime Factorization)
중학교 때 배우게 되는 소인수 분해를 이용한다
어떤 수의 모든 소인수를 구하는 방법은 소수구하기 알고리즘을 이용한다.

소인수분해는, a 와 b 를 소수로 나눌 수 있는 만큼 인수분해하여, 최대공약수는 두 수의 공통된 소인수들 중에서 차수가 최소인 값들의 곱이 되며, 최소공배수는 두 수의 모든 소인수들 중에서 차수가 최대인 값들의 곱이 된다.


이 방법은 수학시간에 배운 직관적인 방법이고, 많은 이들에게 익숙하지만 위에서 보듯이 코딩으로 옮기기에는 꽤나 귀찮다는 단점이 있다. 또한 두 수 a, b 의 모든 소인수를 구해서 저장해 놓아야 하므로 별도의 메모리를 필요로 한다. ( 위의 코드는 10000 까지의 소인수에 대해서 구현한 코드 )


방법 2) Brute force
최대공약수의 정의를 이용하여, brute force 방법으로 직접 구한다.
즉, 두 수 a, b 를 나누어서 나머지가 0 이 되는 수들 중에서 가장 큰 숫자를 구하면 이 숫자가 최대공약수가 되며, 두 수 a, b 의 배수를 구하여 공통된 배수 중 가장 작은 숫자를 구하면 이 숫자가 최소공배수가 된다.

위의 코드에서 만약 a, b 간의 공약수가 존재하지 않는 경우는 두 수의 최대공약수는 자연스럽게 1이 되며, a, b 간의 최소공배수 중 가장 클 가능성이 있는 수는 a*b 가 되므로 a*b 까지만 검사를 하면 된다.

이 방법은 최대 a*b 번의 연산을 해야 하므로,  a 와 b 가 커질수록 수행시간이 급격히 늘어난다.


방법 3)
Euclid's algorithm - Using Recursion
사실 최대공약수와 최소공배수를 구하는 것은 고대 그리스의 수학자, 유클리드가 고안한 "유클리드 알고리즘"(Euclid's algorithm) 을 쓰면 간결하게 해결할 수 있다. 이 방법은 The Art Of Computer Programming 의 제 1장에도 소개된 바 있다.
a, b 중 큰 수를 a, 작은 수를 b 라 하자. 이 두 수의 최대공약수를 구하려면 a 를 b 로 나눈다. 이 연산을 한 후에 b 와 a%b 를 갖고 어느 한쪽이 0 이 될때까지 이 과정을 계속 반복한다. a%b 가 0 이 될 때의 b 가 바로 최대공약수가 된다. 이를 코드로 옮기면 다음과 같다.


최대공약수와 최소공배수의 성질을 이용하면 아래와 같이
최소공배수 = (a*b) / 최대공약수
최대공약수 = (a*b) / 최소공배수
가 되므로, 최대공약수나 최소공배수 둘 중 하나만 구하면 나머지 하나는 쉽게 구할 수 있다.


방법 4) Euclid's algorithm - Using Iteration
방법 3 은 매우 간결한 코딩이 가능하지만, 재귀호출을 이용한다는 단점이 있다. 재귀호출은 상당히 비싼 작업이므로 이를 좀더 효율적인 iteration 버전으로 바꿀 수 있다. 실제로 방법 3은 a, b 가 매우 큰 서로소일 경우 답을 얻는데 꽤 오랜 시간이 걸리게 된다.



방법 5) Euclid's algorithm - Using Iteration, Original Version
유클리드가 처음 이 알고리즘을 제안했을때 사실은 modular 연산을 사용하지 않고, 아래와 같이 뺄셈 연산을 이용했다 한다.
 
방법5 는 방법 4와 비교하여, tmp 변수를 사용하지 않아도 되므로 메모리를 약간 절약한다는 장점이 잇다 ^^

유클리드 알고리즘의 증명 = 자세한 설명은 생략한다 Wikipedia 참고
유클리드 알고리즘의 시간복잡도 = O(n^2), n = length of integer bits, 그 이유는 n-bit 숫자 나눗셈 연산의 시간복잡도가 O(n(m+1)) 이기 때문이다 ( m 은 몫의 길이 )

3개 이상의 수에 대해 최소공배수와 최대공약수를 구할 때는, 두 수의 최소공배수와 최대공약수를 구한 후, 이 수와 나머지 수들에 대해 최소공배수와 최대공약수를 구하면 된다.

유클리드 알고리즘을 처음 보았을 때 무릎을 쳤던 기억이 난다. 그래서 최대공약수와 최소공배수에 대해서 글을 반드시 함 써보려 했는데... 위키피디아에 유클리드 알고리즘이 깔끔하고 멋지게 정리가 되어 있었다 ㅋ

크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/261 관련글 쓰기
댓글을 달아주세요!
  1. BlogIcon 김경태 2008/10/19 00:04  댓글주소  수정/삭제  댓글쓰기

    오 알고리즘 정리 해놓으시는 카테고리도 있네요. 가져가도 될까요? 후배들 교육용으로? 발행 안하고 내부 문서로만 쓸 예정입니다.

  2. BlogIcon hyperdash 2008/10/21 01:19  댓글주소  수정/삭제  댓글쓰기

    출처 안밝히고 막 써도 되는 자료지? ㅋㅋㅋ

  3. mosaick 2009/08/11 16:55  댓글주소  수정/삭제  댓글쓰기

    지나가다 하나 추가하면 좋을 것 같아서, ^^a


    방법5 는 방법 4와 비교하여, tmp 변수를 사용하지 않아도 되므로 메모리를 약간 절약한다는 장점이 잇다 ^^
    => 방법4는 modular연산을 통해 방법5에 비해서 순회횟수를 줄인다.

  4. BlogIcon 연꿈 2009/11/20 16:30  댓글주소  수정/삭제  댓글쓰기

    와 정말 잘 정리해놓으셨네요
    출처밝히고 참고해도 괜찮을까요

이름 암호 홈페이지



오늘 알고리즘 수업을 듣다가 Time Complexity 계산방법에 대한 강의 중에 누군가 수업시간에 한 질문,
"우리가 흔히 nlogn 정렬이라고 말하는 퀵 소트의 경우 Worst Case 는 O(n^2) 이 된다. 교수님이 설명하시길 알고리즘의 시작복잡도는 Worst Case 를 기준으로 측정한다고 얘기했는데 퀵소트는 Average Case 를 기준으로 할 때만 O(nlogn) 이 되는 것인데, 그렇다면 과연 퀵 소트를 O(nlogn) 정렬이라고 정렬이라고 부르는 것이 맞는가?"

참고로, 머지 소트(Merge Sort) 와 힙 소트(Heap Sort) 의 경우 Worst Case 에서도 O(nlogn) 에 수행된다.

당시 알고리즘의 Time Complexity 는 Worst Case 를 기준으로 측정한다고 설명하던 강사도 이 질문에 살짝 당황해서 좀 버벅이다가 실제 상황에서는 Average Case 를 대상으로 적용하는 경우가 많기 때문에 퀵 소트를 nlogn 정렬이라고 본다고 설명했다.

평소에 당연히 퀵 소트는 O(nlogn) 이라고만 생각하던 차, 이 질문을 듣고 나도 의문이 생겨서 집에 와서 CLRS 책을 다시 뒤져보았다.

"Quicksort is a sorting algorithm whose worst-case running time is O(N^2) on an input array of n numbers, In spite of this slow worst-case running time, quicksort is often the best practical choice for sorting because it is remarkably efficient on the average: it's expected running time is O(nlogn), and the constant factors hidden in the O(nlon) notation are quite small. It also has the advantage of sorting in place and it works well even in memory environments.

그러니까, Quicksort 는 O(nlong) 에 숨어있는 상수 계수의 크기도 작고, 내부 정렬이며, 평균적으로 가장 빠르게 수행되는 정렬이며, 가상메모리 상에서도 잘 동작하는 일반적으로 가장 좋은 정렬이다는 이야기. partition 의 깊이 logn 과 각 partition 에서의 비교횟수 n 번이 수행되어 Average Case 는 O(nlogn), n-1 과 0 으로 partition 되는 경우 Worst case O(N^2) 이 된다.

결론적으로 Worst-case 가 알고리즘의 시간복잡도를 평가하는 절대적인 기준은 아니란거네..

Average Case 는 대개 계산하기도 힘들고, 현실에서는 입력되는 값의 정확한 분포를 알기 어려우므로 쓰기 힘들다. 
Worst Case 는 알고리즘의 성능을 보여주는 척도가 될 뿐만 아니라 경험적으로 알고리즘의 수행시간은 Worst Case 인 경우의 수행시간과 큰 차이가 나지 않기때문에  일반적으로 알고리즘의 시간복잡도를 말할때는 Worst-Case 기준으로 평가한다.
"어떤 알고리즘의 시간복잡도가 O(n^2) 이라면 이 알고리즘은 최악의 경우 n^2 만큼 수행한다." 는 의미가 된다.

결론적으로 강사가 이런 부분을 명확하게 설명을 안해준거네 -0-
크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/248 관련글 쓰기
댓글을 달아주세요!
  1. BlogIcon rubyeye 2008/09/16 09:29  댓글주소  수정/삭제  댓글쓰기

    지나가다가 글 하나 올립니다 ㅋ
    quick sort도 worst case O(nlogn)알고리즘이 맞습니다.
    문제가 되는 것이 quick sort에서 각 단계마다 최적의 key값(중앙값)을 O(n)안에 찾을 수 있느냐인데
    k번째 큰 값을 찾는 알고리즘이 O(n)이므로 가능합니다. 따라서 quicksort도 worst case O(nlogn)이 됩니다.
    문제는 이런 식으로 key값을 찾으면 앞에 붙는 상수값이 커져서 practical하게는 더 느려진다는 점이죠...
    즉, quicksort는 이론적으로 worst-case O(nlogn)입니다만, 실제로 사용할 때는 빠른 속도를 위해서
    worst-case O(n^2)이 되더라도 대충(-_-; ) 키값을 찾게 된다는 거죠... 참고하시기 바랍니다 ^^

  2. 고글 2008/09/25 23:45  댓글주소  수정/삭제  댓글쓰기

    형 저도 지나가다가.. 글 씁니다.

    요점을 다 빗나게가 쓴 것 같아서..
    Quick sort의 worst case는 O(N^2) 입니다. 오름차순 혹은 내림차순으로 정렬 되어 있을때 그렇게 되겠죠.
    데이타를 많이 돌려보았을때 최악의 경우에서 Mergesort와 Heapsort 다 O(n log n)을 보장하지만
    실제로 데이타를 돌려보면 Quick sort가 가장 빠른 결과값을 나오게 합니다.

    Notation상 분명 Quicksort가 Mergesort나 Heapsort보다 확실히 느려야 하는데 그렇지 않는것은 하드웨어레벨에서 그문제가 발생하는거지요.

    이유는 지역성(Locality) 특성 때문입니다.
    "가상메모리 상에서도 잘 동작하는 일반적으로 가장 좋은 정렬이다는 이야기. "
    이게 locality를 잘 반영했다는 얘긴데.
    지역성에는 locality는 두가지가 있습니다.
    시간지역성과 공간지역성이 있는데 여기에서는 공간지역성을 잘 반영했다라고 이해하시면됩니다.
    시간지역성은 한번 나왔던 애가 다시 또 나올 가능성이 높다는것이고,
    공간 지역성은 한번 발생한곳 주위에서 또 발생할 가능성이 높다는것입니다

    Quicksort를 시뮬레이션 해서 잘 관찰해보면 pivot 주변에서 데이타의 위치 이동이 빈번히 발생하게 됩니다.
    메모리에 데이타를 올리고, 캐쉬에 저장해놓고 쓸 때에, 메모리와 캐쉬 hit rate를 생각해보면 Quicksort가 빠릅니다.

    - 컴구조책 메모리파트에서 이렇게 이유를 밝히고 있습니다.

    P.S. 대학원준비한다고 책 처음부터 끝까지 다 봤더니, 몰랐었던 내용도 많이 알게 됐습니다.

    • BlogIcon soyoja 2008/09/26 14:33  댓글주소  수정/삭제

      오.. 그렇구나.
      지난 학기에 OS 수업들으면서 Locality 와 cache hit ratio, Page fault 에 대해서 열심히 공부한 기억이 나는구낭.. ㅋ

  3. 김대현 2008/10/22 22:01  댓글주소  수정/삭제  댓글쓰기

    저기..질문좀 할께요

    다항시간(다차시간: Polynomial-time) 라고 나오는데요

    정의를 확실하게 잘 모르겠네요...

    식으로 O(n^2)에서요 O는 뭐고 n은 무엇을 뜻하는 건가요??

    정말 어렵네요..ㅡㅡ;;

    답해주시면 정말 감사하겠습니다.

    kdhenjoy@naver.com

  4. 김대현 2008/10/24 12:33  댓글주소  수정/삭제  댓글쓰기

    감사합니다...근데요 또 궁금점이 있는데요~

    O가 문제 걸리는데 최악의 시간이라는데

    그 시간의 결정은 어떻게 하나요?

    O표기법도 다양하던데..어떤때에 어떤걸 써야 할지...잘 모르겠네요...

    NP Problem을 공부하다가 다항시간이 나와서 이리저리 알아보고 있는데

    정말 어렵네요..ㅠㅠ

    여기 BIG-O표기법은 식이 주어져 있을때 푸는게 맞나요??

    아니면 문제가 주어졌을 때 식으로 전환해서 푸시나요?

    학과 과목도 아니고 처음 배우는 거라서요..ㅠㅠ

    쉽게 기초부터 설명 되어있는 책좀 추천좀 해주세요..ㅠㅠ

    감사합니다

    • BlogIcon soyoja 2008/10/25 10:06  댓글주소  수정/삭제

      시간복잡도 계산하는 점근적 표기법은 여러가지가 있는데 언제 어떤걸 쓸지는 저도 잘 모르겠네요 -0-
      일반적으로 알고리즘 책에서 Big-O 기준으로 많이 설명하고 저 강의에서도 Big-O 기준으로 설명해서 Big-O 를 예로 든 것 뿐입니다.
      NP 문제나 점근적 표기법을 이해하는데는 제가 읽어본 책들 중에서는 "쉽게 배우는 알고리즘" (문병로 저) 가 매우 좋았습니다.

  5. BlogIcon JM 2008/11/22 15:13  댓글주소  수정/삭제  댓글쓰기

    제가 요즘 시간 복잡도 관련 챕터를 쓰면서 깨닫게 되었는데요, Worst Case 와 Big-O notation 이 밀접하게 연관되어 있어서 사람들이 착각하기 쉬울 뿐, 알고리즘의 시간 복잡도를 평가할 때 항상 최악의 경우를 기준으로 삼는 것은 아닙니다.

    일단 잘 아시다시피 Big-O notation 은 어떤 함수가 주어질 때 그 함수의 상한을 표기하는 방법이죠. (상수 범위 내에서..) 그래서, 크기가 n 인 입력을 해결하기 위한 알고리즘의 수행 시간을 f(n) 라 할 때, f(n) = O(n^2) 이라고 쓰면 실제로 최악의 경우 알고리즘의 시간 복잡도를 나타내게 됩니다. 때문에, 사람들이 알고리즘의 수행 시간을 최악의 경우로 평가한다고 생각하기 쉽죠.

    하지만, 실제로 Big-O notation 은 시간 복잡도의 표기를 간단하게 하기 위해 도입한 방법일 뿐, 알고리즘의 최악의 경우에 대한 시간을 측정하기 위해 도입한 방법은 아니죠. 따라서, Big-O 가 항상 '최악의 경우' 와 연관되어 있다는 직관은 잘못된 것이라고 생각해요.

    퀵소트의 경우, 실제 실행 시간 f(n) 을 보는 것이 아니라 수행 시간의 기대값 (expected running time) g(n) 을 대신 보게 되지요. 이 때 f(n) 의 최고차항과 g(n) 의 최고차항이 각각 n^2 이고 nlgn 이기 때문에 최악의 경우엔 O(n^2), 기대값은 O(nlgn) 이라는 말은 완전히 정당한 것이라는... ^^

    저도 한 때 "Big O 표기법은 최악의 경우를 표기하는 거라는데 왜 퀵소트가 nlgn 이지 -_-" 헷갈려 하던 때가 있었어서.. 사족을 덧붙여 봅니다.

    덧) 구글에 worst case time complexity 를 쳐보니 이 블로그가 2번에 뜨더라고요. 그래서 와서 리플 달아 봤습니다. ^^;

    덧2) 그리고 실제로 대부분 많은 알고리즘의 경우에는 expected running time 과 worst running time 의 order 가 같습니다. 머지소트나 힙소트의 경우에는 n 이 주어졌을 때 실행 시간 함수 f(n) 이 deterministic 하게 결정되기 때문이고.. 선형검색 같은 경우에도, 중간에 찾았을 경우 곧장 리턴하는 방법을 쓰더라도, expected time 은 n/2 이고 worst time 은 n 이니까, O(n) = O(n/2) 죠? ㅎㅎ

    지하철에서 원고하다 장문의 리플을 달아 봤습니다 -_-;

  6. 짱강 2008/12/27 01:11  댓글주소  수정/삭제  댓글쓰기

    전산에서 최악의 경우인 빅O는 굉장히 중요한 의미를 시사한다.
    빅O는 특정 행위를 수행하는데 특정 시간안에 수행됨을 보장함을 의미한다.
    즉, 평균적인 타임이 N에 비례하는 것과 NlogN에 비례하는 두개의 알고리즘이 있다고 하자.
    이로써는 해당 행위가 언제 끝날 수 있는지 보장되는 것은 아니다.
    반면 최악의 경우 N^2과 NlogN에 비례한다고 하면
    이는 해당 행위가 언제 끝날 수 있는지가 보장이 된다.
    즉, 퀵 소트는 평균적으로 NlogN에 비례하는 다른 소트(힙, 병합, 기수)보다 상대적으로 빠를 수 있지만
    최악의 경우(어느 정도 정렬되어 있는 자료 혹은 어느 정도 역순 정렬되어 있는 자료)의 경우 N^2에 비례하는 속도를 낸다.
    즉, 자료가 어떤 성향이냐에 따라 수행 속도의 차이가 크다는 것이다.
    결론적으로 퀵소트는 최악의 경우 NlogN에 비례하는 (힙, 병합,기수)정렬보다 빠르다고 얘기할 수 없다.

이름 암호 홈페이지


어떤 자연수 n 이 소수인지 구할때, n 이 작을 경우에는 다음과 같은 방법을 사용한다.

1. Trial Division
소수의 성질을 이용, 어떤 수 n 이 소수인지 판별하기 위해 n 을 2 부터 n-1 까지 하나씩 나누어보아 나누어 떨어지는 수가 존재하지 않으면 n 은 소수가 된다.

Notes
- 2를 제외한 모든 소수는 홀수이므로, 어떤 수 n 이 짝수이면 소수가 아니다.
- 만약 N 이 합성수라면 N = a*b 형태가 되므로 ( a > 1 && b > 1 ), a 와 b 둘 중 하나는 sqrt(N) 보다 작거나 같음을 알 수 있다. 그러므로 N 을 2부터 sqrt(N) 까지 나눠보아 나누어지지 않으면 N 은 소수가 된다.

void trial(int end)
{
 for( int a = 2; a <= end; a++ )
 {
  if( a%2 == 0 && a != 2 )
  {
   prime[a] = 0;
   continue;
  }
  bool isprime = true;
  for( int b = 2; b*b <= a; b++ )
  {
   if( a%b == 0 )
   {
    isprime = false;
    break;
   }
  }
  if( isprime ) prime[a] = 1;
 }
}

2. 에라토스테네스의 체(Sieve of Eratosthenes)
n 이 작을 때 ( 일반적으로 n <= 10,000,000 ) 2 부터 n 까지의 소수를 찾는 가장 효율적인 방법으로 알려져 있다. 어떤 수 P 가 소수이면 P 로 나눌 수 있는 P 의 배수들은 소수가 아닌 것으로 체크한다. 이러한 과정을 반복하면서 주어진 [2,n] 구간 내에서 소수를 찾아낸다.

void sieve(int end)
{
 for( int a = 2; a*a <= end; a++ )
 {
  if( prime[a] == 0 )   // 'a' is a prime
  {
   for( int b = a*2; b <= end; b += a )
    prime[b] = 1;
  }
 }
}

소수일 것 같은 수 (Probable Prime) 판별법
어떤 자연수 n 이 소수인지 아닌지 판별할 때, n 이 매우 커지면 유한시간내에 n 이 소수인지 판별하기 곤란해지게 된다. 이 때문에 n 의 소수 여부를 판별하는 근사해를 구하는 검사법이 여럿 개발되었는데, 이 검사법에 의해 n 이 합성수가 아님이 판별되는 경우에 n 을 소수일 것 같다 (Probable Prime) 고 한다.
Probable Prime (PRP) 의 정의는, 소수가 갖는 특정 상태를 만족하는 자연수이다.

1. Wheel Factorization
소수의 특징 중 하나인 [2,n] 구간내에 존재하는 소수의 갯수는 n 이 커질 수록 분포도가 희박해진다는 점에서 착안한 방법. 에라토스테네스의 체의 특수한 응용방법이다. 기본 원리는 에라토스테네스의 체와 동일하나, 구간내의 모든 소수에 대해 배수를 찾아 검사하는 것이 아니라 몇개의 소수를 가지고 이 수들로 나누어지는 수들을 걸러내는 방식으로 구하는 것이 차이점이다. 소수 2, 3을 가지고 Wheel Factorization 을 한다면, 우선 2와 3 의 배수를 모두 소수가 아닌 것으로 판별한다. 그리고 그 다음 소수인 5, 7 를 이용해 또다시 이 작업을 반복한다.
일반적으로 많이 쓰는 factorization 은 6N+1, 6N+5 를 사용하여 주어진 구간 [1,n] 내에서 나누어 지지 않는 수를 소수일 것 같은 수로 판별한다. ( Wheel Factorization 은 n 이 커질수록 정확도가 떨어지므로 이를 위해 n 이 커질수록 더 많은 소수를 이용해 나누어보아야 한다. )

2. 페르마의 작은 정리 (Fermat's Little Theorem)
페르마의 마지막 정리로 유명한 수학자 페르마가 제안한 정리.
p 가 a로 나눠지지 않는 소수라면 ap-1 = 1 (mod p) 를 만족해야 한다.
이때 상기 정리를 만족하지 않는 수는 합성수이며, 상기 정리를 만족하는 수는 소수일 가능성이 매우 높은 수(Probable Prime) 가 된다.
증명은 상기 링크 참조.

3. 밀러-라빈 소수판별법 (Miller-Rabin Primality Test)
n 이 소수인지를 검사하려 할 때, 다음 두 식이 성립한다면 a 는 n 이 합성수라는 강한 증거가 된다. ( 이 식이 성립한다면 n 은 소수일 가능성이 매우 높은 수가 된다.)

 a^{d} \not\equiv 1\pmod{n}  
 a^{2^rd} \not\equiv -1\pmod{n} for all 0 \le r \le s-1

Probable Prime 판별법은 이외에도 여러가지가 개발되어 있다.


참고1
임의의 큰 자연수를 소인수분해하는 문제는 NP 문제로 알려져 있다.
거대한 합성수의 소인수분해는 암호문 해독에 응용되어, 기존의 암호문 전달 체계와 다른 암호학에 공공열쇠 개념을 도입하여 리베스트(Ron Rivest)와, 샤미르(Adi shamir), 아들만(Len Adleman)의 이름을 딴 RSA방식이라는 암호이론이 개발되기도 했다.
http://mathworld.wolfram.com/PrimalityTest.html


참고2 - 관련 사이트
TopCoder Tutorial : Primality Test - Non Deterministic Algorithms
Wikipedia

크리에이티브 커먼즈 라이선스
Creative Commons License
http://soyoja.com/trackback/160 관련글 쓰기
댓글을 달아주세요!
  1. BlogIcon helloneo 2008/10/19 00:29  댓글주소  수정/삭제  댓글쓰기

    앗.. 문득 제목에 오타가 보이네요.. finding primes 이네요..

  2. BlogIcon eclipse 2010/04/01 15:49  댓글주소  수정/삭제  댓글쓰기

    감사합니다. 좋은 정보 얻고 갑니다.

이름 암호 홈페이지