문제

나는 big-o 표기법을 이해하지만 많은 기능에 대해 그것을 계산하는 방법을 모르겠습니다. 특히, 나는 Fibonacci 시퀀스의 순진한 버전의 계산 복잡성을 알아 내려고 노력했다.

int Fibonacci(int n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Fibonacci 서열의 계산 복잡성은 무엇이며 어떻게 계산됩니까?

도움이 되었습니까?

해결책

계산할 시간 기능을 모델링합니다 Fib(n) 계산 시간의 합으로 Fib(n-1) 또한 계산할 시간 Fib(n-2) 또한 함께 추가 할 시간 (O(1)). 이것은 동일한 평가를 반복적으로 평가한다고 가정합니다 Fib(n) 동시에 시간을 내십시오. 즉, 메모리가 사용되지 않습니다.

T(n<=1) = O(1)

T(n) = T(n-1) + T(n-2) + O(1)

이 재발 관계 (예 : 생성 함수 사용)를 해결하면 답으로 끝납니다.

또는 재귀 트리를 그릴 수 있습니다. n 그리고이 기능이 비대칭 적으로 직관적으로 알아냅니다 O(2n). 그런 다음 유도로 추측을 증명할 수 있습니다.

베이스: n = 1 분명합니다

추정하다 T(n-1) = O(2n-1), 그러므로

T(n) = T(n-1) + T(n-2) + O(1) 그것은 동일합니다

T(n) = O(2n-1) + O(2n-2) + O(1) = O(2n)

그러나 의견에서 언급 한 바와 같이, 이것은 단단한 경계가 아닙니다. 이 기능에 대한 흥미로운 사실은 t (n)이 무증상 적으로 똑같다 값으로 Fib(n) 둘 다 정의되므로

f(n) = f(n-1) + f(n-2).

재귀 트리의 잎은 항상 반환됩니다. 1. Fib(n) 재귀 트리의 잎에 의해 반환 된 모든 값의 합은 잎의 수와 동일합니다. 각 잎은 계산하는 데 O (1)가 필요하기 때문에 T(n) 와 동등하다 Fib(n) x O(1). 결과적으로,이 기능의 단단한 경계는 Fibonacci 시퀀스 자체입니다 (~θ(1.6n)). 위에서 언급 한 것처럼 생성 함수를 사용 하여이 단단한 바운드를 찾을 수 있습니다.

다른 팁

얼마나 많은 진술을 실행 해야하는지 스스로에게 물어보십시오. F(n) 완료합니다.

을 위한 F(1), 정답은 1 (조건부의 첫 번째 부분).

을 위한 F(n), 정답은 F(n-1) + F(n-2).

그렇다면 이러한 규칙을 충족시키는 기능은 무엇입니까? 시도해보십시오N (A> 1) :

N == a(N-1) + a(N-2)

a(N-2):

2 == a + 1

해결하십시오 a 그리고 당신은 얻습니다 (1+sqrt(5))/2 = 1.6180339887, 그렇지 않으면 그렇지 않으면 황금 비율.

기하 급수 시간이 걸립니다.

이것에 대한 아주 좋은 토론이 있습니다 MIT에서 구체적인 문제. 5 페이지에서 추가로 추가가 하나의 계산 단위를 취한다고 가정하면 FIB (N)를 계산하는 데 필요한 시간은 FIB (N)의 결과와 매우 밀접한 관련이 있다고 지적합니다.

결과적으로 Fibonacci 시리즈의 매우 근사치로 직접 건너 뛸 수 있습니다.

Fib(N) = (1/sqrt(5)) * 1.618^(N+1) (approximately)

그러므로 순진한 알고리즘의 최악의 성능은

O((1/sqrt(5)) * 1.618^(N+1)) = O(1.618^(N+1))

추신 :에 대한 토론이 있습니다 nth fibonacci 번호의 폐쇄 형태 표현 더 많은 정보를 원한다면 Wikipedia에서.

나는 pgaur와 rickerbh에 동의합니다. 재귀 피보나치의 복잡성은 O (2^n)입니다.

나는 다소 단순한 것으로 같은 결론에 도달했지만 여전히 유효한 추론을 믿는다.

첫째, NTH Fibonacci 번호를 계산할 때 재귀 Fibonacci 함수 (지금부터)가 몇 번이나 호출되는지 알아내는 것이 전부입니다. 시퀀스 0에서 n에서 숫자 당 한 번 호출되면 O (n)이 있습니다. 각 숫자에 대해 n 번으로 호출되면 o (n*n) 또는 o (n^2), 등등.

따라서, f ()가 숫자 n을 요구할 때, 우리가 0에 접근함에 따라 0과 n-1 사이의 주어진 숫자에 대해 f ()의 수가 호출된다.

첫인상으로, 우리가 시각적 방식으로 놓으면 시간당 단위를 뽑는 것이 주어진 숫자를 요구하는 것 같습니다. ). 이 같은:

n              *
n-1            **
n-2           ****  
...
2           ***********
1       ******************
0    ***************************

이제 문제는 N이 자라 면서이 피라미드의 기초가 얼마나 빨리 확대됩니까?

예를 들어 F (6)과 같은 실제 사례를 봅시다

F(6)                 *  <-- only once
F(5)                 *  <-- only once too
F(4)                 ** 
F(3)                ****
F(2)              ********
F(1)          ****************           <-- 16
F(0)  ********************************    <-- 32

우리는 f (0)이 32 배,이 샘플 케이스의 경우 2^(n-1)이라고 불립니다.

이제 우리는 F (x)가 몇 번이나 호출되는지 알고 싶습니다. F (0)이라는 횟수가 그 일부임을 알 수 있습니다.

우리가 모든 *의 모든 것을 f (6)에서 f (2) 라인으로 f (1) 선으로 정신적으로 옮기면 f (1) 및 f (0) 라인이 이제 길이가 같음을 알 수 있습니다. 즉, N = 6이 2x32 = 64 = 2^6 인 경우 총 시간 F ()가 호출됩니다.

이제 복잡성 측면에서 :

O( F(6) ) = O(2^6)
O( F(n) ) = O(2^n)

당신은 그것을 확장하고 방문을 가질 수 있습니다

     T(n) = T(n-1) + T(n-2) <
     T(n-1) + T(n-1) 

     = 2*T(n-1)   
     = 2*2*T(n-2)
     = 2*2*2*T(n-3)
     ....
     = 2^i*T(n-i)
     ...
     ==> O(2^n)

아래쪽 끝에 제한되어 있습니다 2^(n/2) 그리고 상단에서 2^n (다른 의견에서 언급 된 바와 같이). 그리고 그 재귀 구현의 흥미로운 사실은 그것이 FIB (N) 자체의 단단한 점근 경계를 가지고 있다는 것입니다. 이러한 사실을 요약 할 수 있습니다.

T(n) = Ω(2^(n/2))  (lower bound)
T(n) = O(2^n)   (upper bound)
T(n) = Θ(Fib(n)) (tight bound)

단단한 바운드는 ITS를 사용하여 추가로 줄일 수 있습니다 닫힌 양식 당신이 좋아한다면.

증거 답변은 좋지만, 나는 항상 자신을 확신시키기 위해 항상 손으로 몇 가지 반복을해야합니다. 그래서 나는 화이트 보드에 작은 호출 나무를 꺼내고 노드를 세기 시작했습니다. 카운트를 전체 노드, 잎 노드 및 내부 노드로 나눕니다. 여기에 내가 얻은 것은 다음과 같습니다.

IN | OUT | TOT | LEAF | INT
 1 |   1 |   1 |   1  |   0
 2 |   1 |   1 |   1  |   0
 3 |   2 |   3 |   2  |   1
 4 |   3 |   5 |   3  |   2
 5 |   5 |   9 |   5  |   4
 6 |   8 |  15 |   8  |   7
 7 |  13 |  25 |  13  |  12
 8 |  21 |  41 |  21  |  20
 9 |  34 |  67 |  34  |  33
10 |  55 | 109 |  55  |  54

즉시 도약하는 것은 잎 노드의 수가 fib(n). 몇 가지 더 많은 반복이 필요한 것은 내부 노드의 수가 fib(n) - 1. 따라서 총 노드 수는 다음과 같습니다 2 * fib(n) - 1.

계산 복잡성을 분류 할 때 계수를 삭제하기 때문에 최종 답은 다음과 같습니다. θ(fib(n)).

재귀 알고리즘의 시간 복잡성은 재귀 트리를 그리면 더 잘 추정 될 수 있습니다.이 경우 리포지션 트리를 그리는 재발 관계는 t (n) = t (n-1)+t (n-2)+O (1)입니다. 각 단계는 o (1)를 의미하는 시간을 의미합니다. 만약에 Block.cursion 트리는 모양이 될 것입니다

          n
   (n-1)      (n-2)
(n-2)(n-3) (n-3)(n-4) ...so on

여기서 위의 나무의 각 레벨이 I로 표시된다고 가정합니다.

i
0                        n
1            (n-1)                 (n-2)
2        (n-2)    (n-3)      (n-3)     (n-4)
3   (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)

I의 특정 값에서 나무가 끝나고, 그 경우는 ni = 1이 될 때이므로 i = n-1이므로 나무의 높이가 N-1임을 의미합니다. 이제 트리의 각 N 층에 대해 얼마나 많은 작업이 수행되는지 보자.

2^0=1                        n
2^1=2            (n-1)                 (n-2)
2^2=4        (n-2)    (n-3)      (n-3)     (n-4)
2^3=8   (n-3)(n-4) (n-4)(n-5) (n-4)(n-5) (n-5)(n-6)    ..so on
2^i for ith level

i = n-1은 각 레벨에서 수행 된 나무 작업의 높이이므로

i work
1 2^1
2 2^2
3 2^3..so on

따라서 완료된 총 작업은 각 레벨에서 수행 된 작업의 합계가되므로 I = N-1 이후 2^0+2^1+2^2^3 ...+2^(N-1)가됩니다. 기하학적 시리즈 로이 합계는 2^n이므로 여기서 총 시간 복잡성은 다음과 같습니다. O (2^N)

글쎄, 나에 따르면 그것에 따르면 O(2^n) 이 기능에서와 마찬가지로 재귀만이 상당한 시간 (분열 및 정복)이 걸립니다. 우리는 위의 기능이 레벨에 도달 할 때 잎이 접근 할 때까지 나무에서 계속 될 것임을 알 수 있습니다. F(n-(n-1))F(1). 따라서 여기에서 우리가 각 나무 깊이에서 발생하는 시간 복잡성을 낮추면 요약 시리즈는 다음과 같습니다.

1+2+4+.......(n-1)
= 1((2^n)-1)/(2-1)
=2^n -1

그것은 순서입니다 2^n [ O(2^n) ].

Fibonacci의 순진한 재귀 버전은 계산의 반복으로 인해 설계별로 지수됩니다.

루트에서 컴퓨팅 중입니다.

F (n)은 F (N-1) 및 F (N-2)에 따라 다릅니다.

F (n-1)는 다시 f (n-2)에 의존하고 f (n-3)

F (n-2)는 다시 f (n-3)에 의존하고 f (n-4)

그런 다음 계산에 많은 데이터를 낭비하는 각 레벨 2 재귀 호출에있어 시간 함수가 다음과 같습니다.

t (n) = t (n-1) + t (n-2) + c, c 상수와 함께

t (n-1) = t (n-2) + t (n-3)> t (n-2) 그런 다음

t (n)> 2*t (n-2)

...

t (n)> 2^(n/2) * t (1) = o (2^(n/2))

이것은 분석의 목적을 위해 충분해야한다는 하한 일 뿐이지 만 실시간 함수는 동일한 fibonacci 공식과 닫힌 양식 황금 비율의 지수로 알려져 있습니다.

또한 다음과 같은 동적 프로그래밍을 사용하여 최적화 된 Fibonacci 버전을 찾을 수 있습니다.

static int fib(int n)
{
    /* memory */
    int f[] = new int[n+1];
    int i;

    /* Init */
    f[0] = 0;
    f[1] = 1;

    /* Fill */
    for (i = 2; i <= n; i++)
    {
        f[i] = f[i-1] + f[i-2];
    }

    return f[n];
}

그것은 최적화되어 있으며 만 수행합니다 N 단계이지만 지수입니다.

비용 함수는 입력 크기에서 문제를 해결하기위한 단계 수로 정의됩니다. 동적 버전의 Fibonacci를 볼 때 (N 테이블을 계산하는 단계) 또는 숫자가 프라임인지 알기위한 가장 쉬운 알고리즘 (sqrt (n) 숫자의 유효한 제수를 분석하려면). 이러한 알고리즘이 있다고 생각할 수도 있습니다 에) 또는 o (sqrt (n)) 그러나 이것은 다음과 같은 이유로 사실이 아닙니다. 알고리즘에 대한 입력은 숫자입니다. N, 이진 표기법을 사용하여 정수의 입력 크기 N ~이다 log2 (n) 그런 다음 변수 변경을 수행합니다

m = log2(n) // your real input size

입력 크기의 함수로 단계 수를 찾으십시오.

m = log2(n)
2^m = 2^log2(n) = n

그런 다음 입력 크기의 함수로서 알고리즘 비용은 다음과 같습니다.

T(m) = n steps = 2^m steps

이것이 바로 비용이 지수 인 이유입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top