Fibonacci Dizisinin hesaplama karmaşıklığı


330

Big-O gösterimini anlıyorum, ancak birçok işlev için nasıl hesaplayacağımı bilmiyorum. Özellikle, Fibonacci dizisinin saf versiyonunun hesaplama karmaşıklığını anlamaya çalışıyorum:

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

Fibonacci dizisinin hesaplama karmaşıklığı nedir ve nasıl hesaplanır?



3
Buradaki matris formu bölümüne bakın: en.wikipedia.org/wiki/Fibonacci_number . ^ n (akıllıca bir şekilde) bu matrisi yaparak O (lg n) 'deki Fib (n) değerini hesaplayabilirsiniz. Hile, güç işlevini yapıyor. İTunesU'da bu kesin sorun ve O (lg n) 'de nasıl çözüleceği hakkında çok iyi bir ders var. Tabii MIT ders 3 algoritmaları için giriş (onun absolutley ücretsiz bu yüzden ilgileniyorsanız kontrol edin)
Aly

1
Yukarıdaki yorumların hiçbiri, saf sürümün (yayınlanan kodda) hesaplama karmaşıklığıyla ilgili olan soruyu, matris formu veya özyinelemeli olmayan hesaplama gibi daha akıllı sürümler hakkında değil.
Josh Milthorpe

Burada özyinelemeli uygulamanın hem alt sınır karmaşıklığı (2 ^ n / 2) hem de üst sınır karmaşıklığı (2 ^ n) hakkında çok güzel bir video .
RBT

1
Bir yan not sorgusu: Fibonacci serisinin saf uygulaması yinelemeli veya özyinelemeli mi?
RBT

Yanıtlar:


374

Hesaplamak için zaman fonksiyonunu, hesaplanacak Fib(n)zaman toplamı ve hesaplanacak Fib(n-1)zaman Fib(n-2)artı birlikte ekleme zamanı ( O(1)) olarak modellersiniz . Bu, aynı şekilde tekrarlanan değerlendirmelerin Fib(n)aynı zamanı aldığını varsayar; yani hiçbir not kullanılmaz.

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

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

Bu yineleme ilişkisini (örneğin, oluşturma işlevlerini kullanarak) çözersiniz ve sonunda yanıt alırsınız.

Alternatif olarak, derinliğe sahip olan nve sezgisel olarak bu fonksiyonun asemptotik olduğunu anlayan özyineleme ağacını çizebilirsiniz . Daha sonra varsayımınızı tümevarım yoluyla kanıtlayabilirsiniz.O(2n)

Temel: n = 1açıktır

Varsayalım , bu nedenleT(n-1) = O(2n-1)

T(n) = T(n-1) + T(n-2) + O(1) bu eşittir

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

Ancak, bir yorumda belirtildiği gibi, bu sıkı sınır değildir. Bu işlev hakkında ilginç bir gerçek, T (n) 'nin asimptotik olarak değeri ile aynı olmasıdır , Fib(n)çünkü her ikisi de

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

Özyineleme ağacının yaprakları her zaman 1 döndürür. Fib(n)Özyineleme ağacında yapraklar tarafından döndürülen ve yaprak sayısına eşit olan tüm değerlerin toplamıdır. Her yaprak hesaplamak için O (1) alacaktır T(n), eşittir Fib(n) x O(1). Sonuç olarak, bu fonksiyon için sıkı bağlanma Fibonacci sekansının kendisidir (~ ). Yukarıda bahsettiğim gibi, üreten işlevleri kullanarak bu sıkı sınırı öğrenebilirsiniz.θ(1.6n)


29
Ayrıca indüksiyon ile kanıtlayın. Güzel. +1
Andrew Rollings

Sınır sıkı olmamasına rağmen.
Kaptan Segfault

@Captain Segfault: Evet. Cevabı açıkladım. Yukarıda yazdığım gibi GF yöntemini kullanarak sıkı sınır elde edersiniz.
Mehrdad Afshari

Şaka olarak StackOverflowException alıyorum. Üstel zaman n için oldukça küçük değerlerle kolayca algılanabilir.
David Rodríguez - dribeas

1
"Alternatif olarak, n derinliğine sahip olacak ve sezgisel olarak bu fonksiyonun asimptotik O (2n) olduğunu belirleyecek özyineleme ağacını çizebilirsiniz." - Bu tamamen yanlış. Zaman karmaşıklığı O'dur (golden_ratio ^ n). Asla O (2 ^ n) 'ye yaklaşmaz. Sonsuza doğru ulaşabilseydiniz, O'ya (golden_ratio ^ n) yaklaşırdı. Asimptot budur, iki çizgi arasındaki mesafe 0'a yaklaşmalıdır.
bob

133

F(n)Tamamlamak için kaç ifadenin çalıştırılması gerektiğini kendinize sorun .

Çünkü F(1)cevap 1(koşulun ilk kısmı).

Bunun F(n)cevabı F(n-1) + F(n-2).

Peki hangi fonksiyon bu kuralları yerine getiriyor? Bir n (a> 1) deneyin :

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

Bir (n-2) ile bölün :

a 2 == a + 1

Altın oran olarak bilinen, aelde edin ve olsun .(1+sqrt(5))/2 = 1.6180339887

Yani üstel zaman alır.


8
Tümevarım ile kanıt. Güzel. +1
Andrew Rollings

2
Yanlış bir cevap için 30 upvotes? :-) 1 = F (1) = (1 + sqrt (5)) / 2'yi takip ediyor mu? Peki ya diğer çözüm (1-sqrt (5)) / 2?
Carsten S

1
Hayır, 1 1 + 1'e eşit değildir. Bu kuralları karşılayan işlev soruda belirtilmiştir.
molbdnilo

6
Cevap yanlış değil. Asemptomatik olarak doğru. Diğer çözüm negatiftir, bu yüzden fiziksel olarak mantıklı değildir.
Da Teng

10
Birisi ^ n == a ^ (n-1) + a ^ (n-2) 'nin bu kuralları nasıl yerine getirdiğini açıklayabilir mi? Tam olarak nasıl tatmin edilir, lütfen spesifik olun.
frank

33

Pgaur ve rickerbh'a katılıyorum, özyinelemeli-fibonacci'nin karmaşıklığı O (2 ^ n).

Aynı sonuca oldukça basit bir şekilde geldim ama hala geçerli bir muhakeme olduğuna inanıyorum.

Birincisi, Nth fibonacci sayısını hesaplarken özyinelemeli fibonacci fonksiyonunun (F ()) kaç kez çağrıldığını bulmakla ilgilidir. 0 ila n dizisindeki sayı başına bir kez çağrılırsa, O (n) var, her sayı için n kez çağrılırsa, O (n * n) veya O (n ^ 2) alırız, ve bunun gibi.

Bu nedenle, n sayısı için F () çağrıldığında, 0 ile n-1 arasında belirli bir sayı için F () çağrısının sayısı 0'a yaklaştıkça artar.

İlk izlenim olarak, görsel olarak koyarsak, belirli bir sayı için F () çağrıldığında bir birim çizilirse, ıslak bir tür piramit şekli elde eder (yani birimleri yatay olarak merkezlersek ). Bunun gibi bir şey:

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

Şimdi soru şu: n büyürken bu piramidin tabanı ne kadar hızlı büyüyor?

Gerçek bir durum ele alalım, örneğin 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) 'ın 32 kez çağrıldığını görüyoruz, bu da 2 ^ 5, bu örnek durum için 2 ^ (n-1).

Şimdi, F (x) 'in kaç kez çağrıldığını bilmek istiyoruz ve F (0)' ın kaç kez çağrıldığını sadece bunun bir parçası olarak görebiliyoruz.

Tüm * 'leri zihinsel olarak F (6)' dan F (2) 'ye doğru F (1) çizgisine taşırsak, F (1) ve F (0) çizgilerinin uzunluğunun eşit olduğunu görürüz. Yani, n = 6 2x32 = 64 = 2 ^ 6 olduğunda toplam F () süresi çağrılır.

Şimdi, karmaşıklık açısından:

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

3
F (3) 4 kez değil 3 kez çağrılır. İkinci piramit yanlış.
Avik

2
F (3) = 3, F (2) = 5, F (1) = 8, F (0) = 5. Düzeltirim, ama bu cevabı bir düzenleme ile kurtarabileceğimi sanmıyorum.
Bernhard Barker

31

MIT'de bu özel sorunun çok güzel bir tartışması var . Sayfa 5'de, bir eklemenin bir hesaplama birimi aldığını varsayarsanız, Fib (N) hesaplamak için gereken sürenin Fib (N) sonucu ile çok yakından ilişkili olduğunu gösterirler.

Sonuç olarak, doğrudan Fibonacci serisinin çok yakın yaklaşımına atlayabilirsiniz:

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

ve bu nedenle, saf algoritmanın en kötü durum performansının

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

Not: Nth Fibonacci numarasının kapalı form ifadesinin Wikipedia'da üzerinde bir tartışma var .


Kurs bağlantısı için teşekkürler. Çok güzel bir gözlem de
SwimBikeRun

16

Genişletebilir ve bir görselleştirebilirsiniz.

     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)

1
İlk satırı anlıyorum. Ama neden <sonunda karakterden daha az karakter var ? Nasıl buldun T(n-1) + T(n-1)?
Quazi Irfan

@QuaziIrfan: D bu bir ok. -> [(en az değil). Son satırla ilgili karışıklık için özür dileriz]. İlk satır için, şey ... T(n-1) > T(n-2)Böylece değiştirip T(n-2)koyabilirim T(n-1). Sadece hala geçerli olan daha yüksek bir sınır elde T(n-1) + T(n-2)
edeceğim

10

Alt uçta 2^(n/2)ve üst uçta 2 ^ n ile sınırlıdır (diğer yorumlarda belirtildiği gibi). Ve bu özyinelemeli uygulamanın ilginç bir gerçeği, Fib (n) 'in kendisinin sıkı bir asimtotik bağına sahip olmasıdır. Bu gerçekler özetlenebilir:

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

Sıkı sınır, isterseniz kapalı formu kullanılarak daha da azaltılabilir .


10

Kanıt yanıtları iyidir, ancak kendimi gerçekten ikna etmek için her zaman el ile birkaç tekrar yapmak zorundayım. Bu yüzden beyaz tahtama küçük bir arama ağacı çizdim ve düğümleri saymaya başladım. Sayımları toplam düğümlere, yaprak düğümlerine ve iç düğümlere ayırdım. İşte ne var:

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

Hemen sıçrayan şey yaprak düğümlerinin sayısının olmasıdır fib(n). Dikkat edilmesi gereken birkaç tekrarlama gereken şey, iç düğümlerin sayısının olmasıdır fib(n) - 1. Bu nedenle toplam düğüm sayısı 2 * fib(n) - 1.

Hesaplama karmaşıklığını sınıflandırırken katsayıları düşürdüğünüz için son cevaptır θ(fib(n)).


(Hayır, beyaz
tahtama

Güzel, özyinelemeli Fib'in kaç ilave ilaveler yaptığını merak ediyordum. Sadece 1tek bir akümülatör Fib(n)süresine eklemekle kalmıyor, aynı zamanda hala tam olarak ilginç θ(Fib(n)).
Peter Cordes

0Yine de bazı (çoğu) özyinelemeli uygulamaların eklenmesi için zaman harcadığına dikkat edin : özyineleme tabanı vakaları 0ve 1çünkü Fib(n-1) + Fib(n-2). Yani muhtemelen 3 * Fib(n) - 2gelen bu bağlantı sadece cevap lenf nodülü sayısıyla değil, daha doğru olduğunu 2 * Fib(n) - 1.
Peter Cordes

Yaprak düğümlerinde aynı sonuçları elde edemiyorum. 0'dan başlayarak: F (0) -> 1 yaprak (kendisi); F (1) -> 1 yaprak (kendisi); F (2) -> 2 yaprak (F (1) ve F (0)); F (3) -> 3 yaprak; F (5) -> 8 yaprak; vs.
alexlomba87

9

Özyinelemeli algoritmanın zaman karmaşıklığı özyineleme ağacı çizilerek daha iyi tahmin edilebilir, bu durumda özyineleme ağacının çizimi için yineleme ilişkisi T (n) = T (n-1) + T (n-2) + O (1) olur. her adım O (1) sabit zaman anlamına gelir, çünkü eğer blokta n değerini kontrol etmek için sadece bir karşılaştırma yapar.

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

Burada diyelim ki yukarıdaki ağacın her seviyesi ben tarafından gösterilir,

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)

diyelim ki i'nin belirli bir değerinde ağaç biter, bu durumda ni = 1, dolayısıyla i = n-1 olduğunda, ağacın yüksekliğinin n-1 olduğu anlamına gelir. Şimdi ağaçtaki n katmanın her biri için ne kadar iş yapıldığını görelim. Her adımın yineleme ilişkisinde belirtildiği gibi O (1) zaman aldığını unutmayı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 olduğu için her seviyede yapılan ağaç işinin yüksekliği

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

Bu nedenle, yapılan toplam iş, her seviyede yapılan işlerin toplamı olacaktır, bu nedenle i = n-1'den beri 2 ^ 0 + 2 ^ 1 + 2 ^ 2 + 2 ^ 3 ... + 2 ^ (n-1) olacaktır. Geometrik serilere göre bu toplam 2 ^ n, dolayısıyla toplam zaman karmaşıklığı O (2 ^ n)


2

Bana göre O(2^n), bu işlevde olduğu gibi sadece özyineleme önemli ölçüde zaman alır (böl ve fethet). Biz yaprakları biz seviyesine ulaştığında yaklaşımlar kadar olduğu, yukarıdaki fonksiyon bir ağaçta devam edecek bakın F(n-(n-1))yani F(1). Yani, burada ağacın her derinliğinde karşılaşılan zaman karmaşıklığını not ettiğimizde, toplama serisi:

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

sırasıdır 2^n [ O(2^n) ].


1

Fibonacci'nin naif özyineleme sürümü, hesaplamadaki tekrarlama nedeniyle tasarımla üsteldir:

Kökte hesaplıyorsunuz:

F (n), F (n-1) ve F (n-2) 'ye bağlıdır

F (n-1) tekrar F (n-2) ve F (n-3) 'e bağlıdır

F (n-2) tekrar F (n-3) ve F (n-4) değerlerine bağlıdır

hesaplamada çok fazla veri harcayan her düzeyde 2 özyinelemeli çağrı yapıyorsunuz, zaman işlevi şu şekilde görünecektir:

T (n) = T (n-1) + T (n-2) + C, C sabiti ile

T (n-1) = T (n-2) + T (n-3)> T (n-2) sonra

T (n)> 2 * T (n-2)

...

T (n)> 2 ^ (n / 2) * T (1) = O (2 ^ (n / 2))

Bu sadece analizin amacı için yeterli olması gereken bir alt sınırdır, ancak gerçek zamanlı fonksiyon aynı Fibonacci formülü tarafından sabit bir faktördür ve kapalı formun altın oranın üstel olduğu bilinmektedir.

Ayrıca, aşağıdaki gibi dinamik programlama kullanarak optimize edilmiş Fibonacci sürümlerini bulabilirsiniz:

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];
}

Bu optimize edilmiş ve sadece n adım yapmak ama aynı zamanda üstel.

Maliyet fonksiyonları, Giriş boyutundan sorunu çözmek için adım sayısına kadar tanımlanır. Fibonacci'nin dinamik sürümünü ( tabloyu hesaplamak için n adım) veya bir sayının asal olup olmadığını bilmek için en kolay algoritmayı (sayının geçerli bölenlerini analiz etmek için sqrt (n)) gördüğünüzde. bu algoritmaların O (n) veya O (sqrt (n)) olduğunu düşünebilirsiniz, ancak bu aşağıdaki nedenden dolayı doğru değildir: Algoritmanıza giriş bir sayıdır: n , bir tam sayı n, bir log 2 (n) daha sonra değişken bir değişiklik yaparak

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

giriş boyutunun bir fonksiyonu olarak adım sayısını öğrenelim

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

giriş boyutunun bir fonksiyonu olarak algoritmanızın maliyeti:

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

ve bu yüzden maliyet üsteldir.


1

Fonksiyon çağrılarını çizerek hesaplamak kolaydır. Her n değeri için işlev çağrılarını ekleyin ve sayının nasıl büyüdüğüne bakın.

Big O, O (Z ^ n) 'dir; burada Z, altın oran veya yaklaşık 1.62'dir.

N'yi artırdıkça hem Leonardo sayıları hem de Fibonacci sayıları bu orana yaklaşıyor.

Diğer Big O sorularının aksine, girdide değişkenlik yoktur ve algoritmanın hem algoritması hem de uygulaması açıkça tanımlanmıştır.

Bir grup karmaşık matematiğe gerek yoktur. Aşağıdaki fonksiyon çağrılarını çiziniz ve rakamlara bir fonksiyon yerleştiriniz.

Veya altın orana aşina iseniz, bunu böyle tanıyacaksınız.

Bu cevap, f (n) = 2 ^ n'ye yaklaşacağını iddia eden cevaptan daha doğrudur. Asla olmayacak. F (n) = golden_ratio ^ n değerine yaklaşacaktır.

2 (2 -> 1, 0)

4 (3 -> 2, 1) (2 -> 1, 0)

8 (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
            (2 -> 1, 0)


14 (5 -> 4, 3) (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
            (2 -> 1, 0)

            (3 -> 2, 1) (2 -> 1, 0)

22 (6 -> 5, 4)
            (5 -> 4, 3) (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
                        (2 -> 1, 0)

                        (3 -> 2, 1) (2 -> 1, 0)

            (4 -> 3, 2) (3 -> 2, 1) (2 -> 1, 0)
                        (2 -> 1, 0)

1
Altın oran hakkında bu iddia için herhangi bir kaynak sağlayabilir misiniz?
Nico Haase

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.