알고리즘이 O (log n) 복잡성을 갖게하는 원인은 무엇입니까?


106

big-O에 대한 나의 지식은 제한적이며 로그 용어가 방정식에 나타날 때 나를 더 많이 버립니다.

누군가가 O(log n)알고리즘이 무엇인지 간단한 용어로 설명 할 수 있습니까 ? 로그는 어디에서 왔습니까?

이것은 제가 중간 고사 문제를 풀려고 할 때 특별히 나왔습니다.

X (1..n) 및 Y (1..n)에 각각 감소하지 않는 순서로 정렬 된 두 개의 정수 목록이 포함되도록합니다. 결합 된 모든 2n 요소의 중앙값 (또는 n 번째로 작은 정수)을 찾기 위해 O (log n)-시간 알고리즘을 제공합니다. 예를 들어 X = (4, 5, 7, 8, 9) 및 Y = (3, 5, 8, 9, 10)이면 7은 결합 된 목록의 중앙값입니다 (3, 4, 5, 5, 7 , 8, 8, 9, 9, 10). [힌트 : 이진 검색 개념 사용]


29
O(log n)다음과 같이 볼 수 있습니다. 문제 크기를 두 배로 n늘리면 알고리즘에 일정한 수의 단계 만 더 필요합니다.
phimuemue 2012

3
이 웹 사이트는 Big O 표기법을 이해하는 데 도움이되었습니다 : recursive-design.com/blog/2010/12/07/…
Brad

1
7이 위의 예의 중앙값 인 이유가 궁금합니다. fwiw도 8 일 수 있습니다. 예가별로 좋지 않습니까?
stryba 2012

13
O (log (n)) 알고리즘에 대해 생각하는 좋은 방법은 각 단계에서 문제의 크기를 절반으로 줄이는 것입니다. 이진 검색 예제를 사용하십시오. 각 단계에서 검색 범위 중간에있는 값을 확인하여 범위를 반으로 나눕니다. 그 후 검색 범위에서 절반 중 하나를 제거하고 나머지 절반은 다음 단계의 검색 범위가됩니다. 따라서 각 단계에서 검색 범위의 크기가 절반으로 줄어들어 알고리즘의 복잡성이 O (log (n))됩니다. (감소는 25 %, 임의의 일정한 비율에 의해, 그 1/3이 될 수의 정확히 절반으로 할 필요는 없다; 절반 가장 일반적)
크 르지 Kozielczyk

감사합니다, 이전 문제를 해결하고 곧이 문제를 해결할 것입니다. 답변에 감사드립니다! 나중에 다시 될 것입니다 것은이 공부
user1189352

답변:


290

나는 당신이 O (log n) 알고리즘을 처음 볼 때 꽤 이상하다는 것에 동의해야합니다 ... 도대체 그 로그는 어디에서 왔습니까? 그러나 로그 용어를 big-O 표기법으로 표시하는 방법에는 여러 가지가 있습니다. 다음은 몇 가지입니다.

상수로 반복적으로 나누기

임의의 수 n을 취하십시오. 16. 1보다 작거나 같은 숫자를 얻기 전에 n을 2로 몇 번 나눌 수 있습니까? 16 명의 경우

16 / 2 = 8
 8 / 2 = 4
 4 / 2 = 2
 2 / 2 = 1

완료하는 데 4 단계가 필요합니다. 흥미롭게도 log 2 16 = 4 도 있습니다 . 음 ... 128은 어떻습니까?

128 / 2 = 64
 64 / 2 = 32
 32 / 2 = 16
 16 / 2 = 8
  8 / 2 = 4
  4 / 2 = 2
  2 / 2 = 1

이 일곱 개 조치를 취했다 및 로그 2 128 = 7이 우연의 일치인가를? 아니! 이것에 대한 좋은 이유가 있습니다. 숫자 n을 2 i로 나눈다 고 가정합니다. 그런 다음 n / 2 i를 얻습니다 . 이 값이 최대 1 인 i의 값을 구하려면

n / 2 나는 ≤ 1

n ≤ 2 나는

로그 2 N ≤를 난

즉, i ≥ log 2 n 인 정수 i를 선택하면 n을 i로 반으로 나눈 값은 최대 1이됩니다. 이것이 보장되는 가장 작은 i는 대략 log 2입니다. n이므로 숫자가 충분히 작아 질 때까지 2로 나누는 알고리즘이 있으면 O (log n) 단계에서 종료된다고 말할 수 있습니다.

중요한 세부 사항은 n을 (1보다 큰 한) 어떤 상수로 나눌 것인지는 중요하지 않다는 것입니다. 상수 k로 나누면 log k가됩니다. 1에 도달하는 n 단계가 필요합니다. 따라서 입력 크기를 일부 분수로 반복적으로 나누는 알고리즘은 종료하려면 O (log n) 반복이 필요합니다. 이러한 반복은 많은 시간이 걸릴 수 있으므로 순 런타임은 O (log n) 일 필요는 없지만 단계 수는 대수입니다.

그래서 이것은 어디에서 나옵니까? 한 가지 고전적인 예는 정렬 된 배열에서 값을 검색하는 빠른 알고리즘 인 이진 검색 입니다. 알고리즘은 다음과 같이 작동합니다.

  • 배열이 비어 있으면 해당 요소가 배열에 없음을 반환합니다.
  • 그렇지 않으면:
    • 배열의 중간 요소를보십시오.
    • 우리가 찾고있는 요소와 같으면 성공을 반환합니다.
    • 우리가 찾고있는 요소보다 큰 경우 :
      • 배열의 두 번째 절반을 버립니다.
      • 반복
    • 우리가 찾고있는 요소보다 작은 경우 :
      • 배열의 전반부를 버립니다.
      • 반복

예를 들어 배열에서 5를 검색하려면

1   3   5   7   9   11   13

먼저 중간 요소를 살펴 보겠습니다.

1   3   5   7   9   11   13
            ^

7> 5이고 배열이 정렬 되었기 때문에 숫자 5가 배열의 뒷면에있을 수 없다는 사실을 알고 있으므로 그냥 버릴 수 있습니다. 이것은 떠난다

1   3   5

이제 여기서 중간 요소를 살펴 보겠습니다.

1   3   5
    ^

3 <5이므로 배열의 전반부에 5가 나타날 수 없다는 것을 알고 있으므로 전반 배열을 던져서 떠날 수 있습니다.

        5

다시이 배열의 중간을 살펴 봅니다.

        5
        ^

이것은 정확히 우리가 찾고있는 숫자이기 때문에 5가 실제로 배열에 있다고보고 할 수 있습니다.

그렇다면 이것이 얼마나 효율적입니까? 음, 반복 할 때마다 나머지 배열 요소의 절반 이상을 버립니다. 알고리즘은 배열이 비어 있거나 원하는 값을 찾는 즉시 중지됩니다. 최악의 경우 요소가 없기 때문에 요소가 부족할 때까지 배열 크기를 계속 절반으로 줄입니다. 얼마나 걸리나요? 음, 배열을 계속해서 반으로 자르기 때문에, 실행하기 전에 배열을 O (log n) 번보다 절반으로 잘라낼 수 없기 때문에 최대 O (log n) 반복으로 끝날 것입니다. 배열 요소에서.

나누고 정복 하는 일반적인 기법 (문제를 조각으로 자르고, 그 조각을 풀고, 문제를 다시 합치는 것)을 따르는 알고리즘 은 동일한 이유로 로그 용어를 갖는 경향이 있습니다. O (log n) 배의 절반 이상. 이것의 좋은 예로서 병합 정렬 을보고 싶을 것 입니다.

값을 한 번에 한 자리 씩 처리

10 진수 n은 몇 자리입니까? 음, 숫자에 k 자리가 있다면 가장 큰 자리는 10 k의 배수입니다 . 가장 큰 k 자리 수는 999 ... 9, k 번이고 이것은 10 k + 1-1 과 같습니다 . 따라서 n에 k 자리가 있다는 것을 알면 n의 값이 최대 10 k + 1-1. k를 n으로 풀고 싶다면

N ≤ 10 K + 1 - 1

n + 1 ≤ 10 k + 1

로그 10 (n + 1) ≤ k + 1

(로그 10 (n + 1))-1 ≤ k

여기서 k는 n의 밑이 10 인 로그라는 것을 알 수 있습니다. 즉, n의 자릿수는 O (log n)입니다.

예를 들어 너무 커서 기계어에 맞지 않는 두 개의 큰 숫자를 더하는 복잡성에 대해 생각해 봅시다. 10 진수로 표시된 숫자가 있고 숫자 m과 n을 호출한다고 가정합니다. 그것들을 추가하는 한 가지 방법은 초등학교 방법을 사용하는 것입니다. 한 번에 한 자리 씩 숫자를 쓰고 오른쪽에서 왼쪽으로 작업하십시오. 예를 들어 1337과 2065를 더하려면 먼저 숫자를 다음과 같이 작성합니다.

    1  3  3  7
+   2  0  6  5
==============

마지막 숫자를 더하고 1 :

          1
    1  3  3  7
+   2  0  6  5
==============
             2

그런 다음 두 번째에서 마지막 ( "두 번째") 숫자를 추가하고 1을 수행합니다.

       1  1
    1  3  3  7
+   2  0  6  5
==============
          0  2

다음으로 마지막에서 세 번째 ( "antepenultimate") 숫자를 추가합니다.

       1  1
    1  3  3  7
+   2  0  6  5
==============
       4  0  2

마지막으로 마지막에서 네 번째 숫자 ( "preantepenultimate"... I love English)를 추가합니다.

       1  1
    1  3  3  7
+   2  0  6  5
==============
    3  4  0  2

자, 우리는 얼마나 많은 일을 했습니까? 숫자 당 총 O (1) 작업 (즉, 일정한 작업량)을 수행하고 처리해야하는 총 숫자는 O (max {log n, log m})입니다. 두 숫자의 각 숫자를 방문해야하므로 총 O (max {log n, log m}) 복잡도를 제공합니다.

많은 알고리즘은 어떤 기준에서 한 번에 한 자릿수를 작동하여 O (log n) 항을 얻습니다. 고전적인 예는 정수를 한 번에 한 자리 씩 정렬하는 radix sort 입니다. 기수 정렬에는 많은 종류가 있지만 일반적으로 시간 O (n log U)에서 실행됩니다. 여기서 U는 정렬되는 가능한 가장 큰 정수입니다. 그 이유는 정렬의 각 패스에 O (n) 시간이 걸리고 정렬되는 가장 큰 숫자의 각 O (log U) 자릿수를 처리하는 데 총 O (log U) 반복이 필요하기 때문입니다. Gabow의 최단 경로 알고리즘 또는 Ford-Fulkerson max-flow 알고리즘 의 스케일링 버전 과 같은 많은 고급 알고리즘 은 한 번에 한 자릿수로 작동하기 때문에 복잡성에 로그 용어가 있습니다.


이 문제를 어떻게 해결하는지에 대한 두 번째 질문에 대해서는 더 고급 응용 프로그램을 탐색하는 이 관련 질문 을 살펴볼 수 있습니다 . 여기에 설명 된 문제의 일반적인 구조를 감안할 때 결과에 ​​로그 용어가 있음을 알 때 문제에 대해 생각하는 방법을 더 잘 이해할 수 있으므로 답변을 제공 할 때까지 답을 보지 않는 것이 좋습니다. 약간의 생각.

도움이 되었기를 바랍니다!


8

큰 오 설명에 대해 이야기 할 때 일반적으로 주어진 크기의 문제를 해결하는 데 걸리는 시간 에 대해 이야기 합니다. . 그리고 일반적으로 간단한 문제의 경우 해당 크기는 입력 요소의 수로 특성화되며 일반적으로 n 또는 N이라고합니다. (분명히 항상 사실은 아닙니다. 그래프 문제는 종종 정점 수, V, 가장자리의 수, E;하지만 지금은 목록에 N 개의 개체가있는 개체 목록에 대해 이야기하겠습니다.)

다음 과 같은 경우에만 문제가 "(N의 일부 기능) 큰 오"라고 말합니다 .

모든 N> 일부 임의의 N_0에 대해 일부 상수 c가 있으므로 알고리즘의 실행 시간이 상수 c 배 (N의 일부 함수) 보다 작습니다 .

즉, 문제를 설정하는 "일정한 오버 헤드"가 중요한 작은 문제에 대해 생각하지 말고 큰 문제에 대해 생각하십시오. 그리고 큰 문제에 대해 런타임인지의 큰 오 (N의 일부 기능) 수단 생각할 때 여전히 항상 적은 일부 일정 시간 이상 해당 기능을. 항상.

요컨대, 그 함수는 상수 인자까지의 상한입니다.

그래서, "big-Oh of log (n)"은 "N의 일부 함수"가 "log (n)"로 대체된다는 점을 제외하면 위에서 말한 것과 동일한 의미입니다.

그래서, 당신의 문제는 당신에게 이진 검색에 대해 생각하라고 말하고 있으므로 그것에 대해 생각해 봅시다. 예를 들어 오름차순으로 정렬 된 N 개의 요소 목록이 있다고 가정 해 보겠습니다. 해당 목록에 주어진 번호가 있는지 확인하고 싶습니다. 이진 검색 이 아닌 이를 수행하는 한 가지 방법 은 목록의 각 요소를 스캔하고 대상 번호인지 확인하는 것입니다. 운이 좋으면 첫 번째 시도에서 찾을 수 있습니다. 그러나 최악의 경우에는 N 개의 다른 시간을 확인합니다. 이것은 이진 검색이 아니며, 위에서 스케치 한 기준에 강제로 적용 할 방법이 없기 때문에 log (N)의 크지 않습니다.

임의의 상수를 c = 10으로 선택할 수 있으며 목록에 N = 32 요소가 있으면 괜찮습니다. 10 * log (32) = 50, 이는 런타임 32보다 큽니다. 그러나 N = 64이면 , 10 * log (64) = 60, 이는 64의 런타임보다 작습니다. c = 100, 1000 또는 gazillion을 선택할 수 있으며 여전히 해당 요구 사항을 위반하는 일부 N을 찾을 수 있습니다. 즉, N_0이 없습니다.

이진 검색을 수행하면 중간 요소를 선택하고 비교합니다. 그런 다음 우리는 숫자의 절반을 버리고 다시 반복합니다. N = 32이면 log (32) 인 5 번 정도만 할 수 있습니다. N = 64이면이 작업을 약 6 번만 수행 할 수 있습니다. 이제는 N의 큰 값에 대해 항상 요구 사항이 충족되는 방식으로 임의의 상수 c를 선택할 수 있습니다 .

이러한 모든 배경에서 O (log (N))는 일반적으로 간단한 작업을 수행 할 수있는 방법이있어 문제 크기를 절반으로 줄일 수 있다는 의미입니다. 이진 검색이 위에서 수행하는 것과 같습니다. 문제를 반으로 자르면 다시 반으로자를 수 있습니다. 하지만 결정적으로 할 수없는 것은 O (log (N)) 시간보다 오래 걸리는 전처리 단계입니다. 예를 들어, O (log (N)) 시간에도 그렇게하는 방법을 찾을 수 없다면 두 목록을 하나의 큰 목록으로 섞을 수 없습니다.

(참고 : 거의 항상 Log (N)는 위에서 가정 한 log-base-two를 의미합니다.)


4

다음 솔루션에서 재귀 호출이있는 모든 라인은 X 및 Y 하위 배열의 주어진 크기의 절반에서 수행됩니다. 다른 라인은 일정한 시간에 수행됩니다. 재귀 함수는 T (2n) = T (2n / 2) + c = T (n) + c = O (lg (2n)) = O (lgn)입니다.

MEDIAN (X, 1, n, Y, 1, n)으로 시작합니다.

MEDIAN(X, p, r, Y, i, k) 
if X[r]<Y[i]
    return X[r]
if Y[k]<X[p]
    return Y[k]
q=floor((p+r)/2)
j=floor((i+k)/2)
if r-p+1 is even
    if X[q+1]>Y[j] and Y[j+1]>X[q]
        if X[q]>Y[j]
            return X[q]
        else
            return Y[j]
    if X[q+1]<Y[j-1]
        return MEDIAN(X, q+1, r, Y, i, j)
    else
        return MEDIAN(X, p, q, Y, j+1, k)
else
    if X[q]>Y[j] and Y[j+1]>X[q-1]
        return Y[j]
    if Y[j]>X[q] and X[q+1]>Y[j-1]
        return X[q]
    if X[q+1]<Y[j-1]
        return MEDIAN(X, q, r, Y, i, j)
    else
        return MEDIAN(X, p, q, Y, j, k)

3

로그 용어는 알고리즘 복잡성 분석에서 매우 자주 나타납니다. 다음은 몇 가지 설명입니다.

1. 숫자를 어떻게 표현합니까?

숫자 X = 245436을 봅시다.이“245436”표기법에는 암시 적 정보가 있습니다. 그 정보를 명시 적으로 만들기 :

X = 2 * 10 ^ 5 + 4 * 10 ^ 4 + 5 * 10 ^ 3 + 4 * 10 ^ 2 + 3 * 10 ^ 1 + 6 * 10 ^ 0

숫자의 소수 확장입니다. 따라서이 숫자를 표현하는 데 필요한 최소 정보량6 자리입니다. 이것은 우연이 아닙니다. 10 ^ d 보다 작은 숫자 는 d 자리 로 나타낼 수 있기 때문 입니다.

그렇다면 X를 나타내는 데 몇 자릿수가 필요합니까? 그것은 X에서 10의 가장 큰 지수에 1을 더한 것과 같습니다.

==> 10 ^ d> X
==> log (10 ^ d)> log (X)
==> d * log (10)> log (X)
==> d> log (X) // 그리고 log가 나타납니다. 다시 ...
==> d = floor (log (x)) + 1

또한 이것은이 범위의 숫자를 표시하는 가장 간결한 방법입니다. 누락 된 숫자는 10 개의 다른 숫자에 매핑 될 수 있으므로 모든 감소는 정보 손실로 이어집니다. 예 : 12 *는 120, 121, 122,…, 129에 매핑 될 수 있습니다.

2. (0, N-1)에서 숫자를 어떻게 검색합니까?

N = 10 ^ d를 취하면 가장 중요한 관찰을 사용합니다.

0에서 N-1 = log (N) 자리 사이의 값을 고유하게 식별하기위한 최소 정보량입니다.

이것은 정수 줄에서 0에서 N-1 사이의 숫자를 검색하라는 요청을 받으면 적어도 log (N) 이이를 찾으려고 시도해야 함을 의미합니다. 왜? 모든 검색 알고리즘은 번호 검색에서 한 자리를 차례로 선택해야합니다.

선택해야하는 최소 자릿수는 log (N)입니다. 따라서 크기 N의 공간에서 숫자를 검색하는 데 필요한 최소 작업 수는 log (N)입니다.

이진 검색, 삼항 검색 또는 데카 검색의 순서 복잡성을 추측 할 수 있습니까?
O (로그 (N))!

3. 숫자 세트를 어떻게 정렬합니까?

일련의 숫자 A를 배열 B로 정렬하라는 메시지가 표시되면 다음과 같습니다.->

Permute 요소

원래 배열의 모든 요소는 정렬 된 배열의 해당 인덱스에 매핑되어야합니다. 따라서 첫 번째 요소의 경우 n 개의 위치가 있습니다. 0에서 n-1 사이의이 범위에서 해당 인덱스를 올바르게 찾으려면 ... log (n) 연산이 필요합니다.

다음 요소에는 log (n-1) 작업, 다음 log (n-2) 등이 필요합니다. 총계는 다음과 같습니다.

==> log (n) + log (n-1) + log (n-2) +… + log (1)

log (a) + log (b) = log (a * b),

==> log 사용 (엔!)

이것은 nlog (n)-n 으로 근사 할 수 있습니다 .
이는 O (N * 로그 (N))!

따라서 우리는 O (n * log (n))보다 더 잘할 수있는 정렬 알고리즘이있을 수 없다는 결론을 내립니다. 그리고 이러한 복잡성을 가진 일부 알고리즘은 인기있는 병합 정렬 및 힙 정렬입니다!

이것이 알고리즘의 복잡성 분석에서 log (n)이 자주 나타나는 이유 중 일부입니다. 같은 것을 이진수로 확장 할 수 있습니다. 여기에 비디오를 만들었습니다.
알고리즘 복잡성 분석 중에 log (n)이 자주 나타나는 이유는 무엇입니까?

건배!


2

해가 n에 대한 반복을 기반으로 할 때 시간 복잡도 O (log n)라고 부릅니다. 알고리즘이 해를 향해 작동하므로 각 반복에서 수행 된 작업이 이전 반복의 일부입니다.


1

아직 댓글을 달 수 없습니다 ... 네크로입니다! Avi Cohen의 대답이 잘못되었습니다.

X = 1 3 4 5 8
Y = 2 5 6 7 9

어떤 조건도 참이 아니므로 MEDIAN (X, p, q, Y, j, k)는 5를 모두 잘라냅니다. 이들은 감소하지 않는 시퀀스이며 모든 값이 구별되는 것은 아닙니다.

또한 고유 한 값으로이 짝수 길이 예제를 시도하십시오.

X = 1 3 4 7
Y = 2 5 6 8

이제 MEDIAN (X, p, q, Y, j + 1, k)가 4 개를 자릅니다.

대신이 알고리즘을 제공하고 MEDIAN (1, n, 1, n)으로 호출합니다.

MEDIAN(startx, endx, starty, endy){
  if (startx == endx)
    return min(X[startx], y[starty])
  odd = (startx + endx) % 2     //0 if even, 1 if odd
  m = (startx+endx - odd)/2
  n = (starty+endy - odd)/2
  x = X[m]
  y = Y[n]
  if x == y
    //then there are n-2{+1} total elements smaller than or equal to both x and y
    //so this value is the nth smallest
    //we have found the median.
    return x
  if (x < y)
    //if we remove some numbers smaller then the median,
    //and remove the same amount of numbers bigger than the median,
    //the median will not change
    //we know the elements before x are smaller than the median,
    //and the elements after y are bigger than the median,
    //so we discard these and continue the search:
    return MEDIAN(m, endx, starty, n + 1 - odd)
  else  (x > y)
    return MEDIAN(startx, m + 1 - odd, n, endy)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.