Bir Algoritmanın “Büyük O” Notasyonunu Belirlemede Uygun Bir “Kural” mı?


29

Büyük O Notasyonu ve algoritmanın nasıl yazıldığına bağlı olarak nasıl hesaplanacağı hakkında daha fazla şey öğrendim. Bir algoritma hesaplamak için ilginç bir "kurallar" seti ile karşılaştım. Big O notasyonu ve doğru yolda olup olmadığımı görmek istedim.

Büyük O Notasyonu: N

function(n) {
    For(var a = 0; i <= n; i++) { // It's N because it's just a single loop
        // Do stuff
    }
}

Büyük O Notasyonu: N 2

function(n, b) {
    For(var a = 0; a <= n; a++) {
        For(var c = 0; i <= b; c++) { // It's N squared because it's two nested loops
            // Do stuff
        }
    }
}

Büyük O Notasyonu: 2N

function(n, b) {
    For(var a = 0; a <= n; a++) {
        // Do stuff
    }
    For(var c = 0; i <= b; c++) { // It's 2N the loops are outside each other
        // Do stuff
    }
}

Büyük O Notasyonu: NLogN

function(n) {
    n.sort(); // The NLogN comes from the sort?
    For(var a = 0; i <= n; i++) {
        // Do stuff
    }
}

Örneklerim ve müteakip gösterim doğru mu? Bilmem gereken ek notlar var mı?


3
Bir formül yerine onu bir kural olarak adlandır, ve muhtemelen doğru yoldasın. Tabii ki, bu tamamen "bir şeyler yap" ın ne yaptığına bağlıdır. Log (N) tipik olarak bir tür ikili / ağaç benzeri bölümleme gerçekleştiren algoritmalardan gelir. İşte konuyla ilgili mükemmel bir blog yazısı.
Daniel B,

15
2NBüyük O notalarında olduğu gibi bir şey yoktur .
vartec

15
@ JörgWMittag çünkü O (2n) = O (n) Big O
cırcır ucube tarafından

3
@ JörgWMittag: Bu gerçekten trolling için bir yer değil.
vartec

3
@vartec - JörgWMittag'ın kasten trolling olduğuna inanmıyorum. Son araştırmalarımda, katı Big-O notasyonu ile Big-O, Theta ve diğer türevleri bir araya getiren "ortak yerel" arasında çok fazla karışıklık olduğunu fark ettim. Ortak kullanımın doğru olduğunu söylemiyorum; Sadece çok olur.

Yanıtlar:


26

Resmen, büyük O notasyonu karmaşıklık derecesini tanımlar .

Büyük O gösterimini hesaplamak için:

  1. algoritma karmaşıklığı formülünü tanımlar. Örneğin diğeri iç içe geçmiş iki döngü, sonra iç içe olmayan diğer üç döngü diyelim:2N² + 3N
  2. en yüksek terim dışındaki her şeyi kaldır: 2N²
  3. tüm sabitleri kaldır:

Başka bir deyişle, biri iç içe, diğeri iç içe geçmiş iki ilmek O (N²)

Elbette bu, döngülerinizde sahip olduğunuzun basit talimatlar olduğunu varsayar. Örneğin sort()döngünün içinde varsa, döngünün karmaşıklığını sort()altta yatan dil / kütüphanenin kullandığı uygulamanın karmaşıklığı ile çarpmanız gerekir .


Kesin anlamda "tüm sabitleri kaldır" açacak 2N³içine N. "tüm katkı ve çarpım sabitlerini kaldırın" gerçeğe daha yakın olacaktır.
Joachim Sauer

@ JoachimSauer: N² = N * N, orada sabit yok.
vartec

@vartec: aynı argümana göre 2N = N+N.
Joachim Sauer

2
@ JoachimSauer, "kesinlikle konuşan" sizin kesinlikle geleneksel olmayan. Bakınız en.wikipedia.org/wiki/Constant_(mathematics) . Polinomlardan bahsederken, "sabit" her zaman yalnızca katsayıları ifade eder, üsleri değil.
Ben Lee,

1
@vartec, yukarıdaki yorumuma bakın. Burada "sabit" kullanımınız kesinlikle doğru ve geleneksel.
Ben Lee,

6

Bu algoritmaları analiz etmek istiyorsanız sonucu tanımlayabilmeniz için // dostuff'ı tanımlamanız gerekir. Diyelim ki dostuff, sürekli O (1) sayıda işlem gerektiriyor.

İşte bu yeni gösterimde bazı örnekler:

İlk örneğiniz için, doğrusal geçiş: bu doğru!

O (N):

for (int i = 0; i < myArray.length; i++) {
    myArray[i] += 1;
}

Neden doğrusal (O (n))? Girdiye (dizi) ek elemanlar ekledikçe, gerçekleşen işlemlerin sayısı, eklediğimiz elemanların sayısıyla orantılı olarak artar.

Bu nedenle, bellekte bir tamsayıyı arttırmak için bir işlem yapılırsa, döngü f (x) = 5x = 5 ek işlemle yaptığı işi modelleyebiliriz. 20 ek element için, 20 ilave işlem daha yaparız. Bir dizinin tek bir geçişi doğrusal olma eğilimindedir. Bu yüzden, bir dizinin tek bir geçidinde bir sıralama yapmak için veri yapısından faydalanabilen kova sıralama gibi algoritmalar da vardır.

İkinci örneğiniz de doğru olurdu ve şöyle görünür:

O (N ^ 2):

for (int i = 0; i < myArray.length; i++) {
    for (int j = 0; j < myArray.length; j++) {
        myArray[i][j] += 1;
    }
}

Bu durumda, ilk dizideki her ek öğe için, i, TÜMÜ j'yi işlememiz gerekir. 1 i ekleyerek aslında j (j uzunluğu) ekler. Böylece haklısın! Bu desen O (n ^ 2), ya da bizim örneğimizde aslında O (i * j) (ya da i == j ise n = 2, ki bu genellikle matris işlemleri ya da kare veri yapıları için geçerlidir.

Üçüncü örneğiniz, dostluğa bağlı olarak işlerin değiştiği yerdir; Eğer kod yazıldığı gibi ve şeyler ise bir sabittir, aslında sadece O (n) 'dir, çünkü 2 boyuta sahip bir diziden 2 geçişimiz olur ve 2n n' ye düşer. Döngülerin birbirlerinin dışında olması 2 ^ n kodu üretebilecek anahtar faktör değildir; işte 2 ^ n olan bir fonksiyon örneği:

var fibonacci = function (n) {
    if (n == 1 || n == 2) {
        return 1;
    }

    else {
        return (fibonacci(n-2) + fibonacci(n-1));
    }
}

Bu işlev 2 ^ n'dir, çünkü işleve yapılan her çağrı, işleve İKİ ek çağrı yapar (Fibonacci). Fonksiyonu her çağırdığımızda, yapmamız gereken iş miktarı iki katına çıkar! Bu hızla büyür, tıpkı bir hidranın kafasını kesmek ve her seferinde iki yeni filiz olmak gibi!

Son örneğiniz için, birleştirme sıralama gibi bir nlgn sıralama kullanıyorsanız, bu kodun O (nlgn) olacağını doğrularsınız. Bununla birlikte, belirli durumlarda daha hızlı türler geliştirmek için (1-100 gibi bilinen sınırlı bir değer aralığı gibi) verilerin yapısından yararlanabilirsiniz. Ancak, en yüksek dereceli kodun hüküm sürdüğünü düşünmekte haklısınız; Bu yüzden, O (nlgn) sıralama O (nlgn) zamandan daha az zaman alan herhangi bir işlemin yanındaysa, toplam zaman karmaşıklığı O (nlgn) olacaktır.

JavaScript'te (en azından Firefox'ta) Array.prototype.sort () 'daki varsayılan sıralama gerçekten MergeSort'tur, bu nedenle son senaryonuz için O (nlgn)' ye bakıyorsunuzdur.


Fibonacci örneğiniz aslında Fibonacci mi? Bunun yapmaya çalıştığınız noktaya karşı çıkmadığını biliyorum, ancak isim başkalarına yanıltıcı olabilir ve bu nedenle aslında Fibonacci değilse dikkat dağıtıcı olabilir.
Paul Nikonowicz

1

İkinci örnek (0 ila dış döngü n , 0 ile iç döngü b ) O (olacaktır nb ) değil, O ( n, 2 ). Kural şu ​​ki , n kez bir şeyi hesaplıyorsunuz ve her biri için b kez başka bir şey hesaplıyorsanız , bu nedenle bu işlevin gelişimi yalnızca n * b'nin büyümesine bağlıdır .

Üçüncü örneğiniz sadece O ( n ) - n ile büyümediğinden tüm sabitleri kaldırabilirsiniz ve büyüme Big-O notasyonu ile ilgili.

Son örneğinize göre, evet, Big-O notasyonunuz kesinlikle karşılaştırmaya dayalıysa (tipik olarak olduğu gibi) en verimli biçimde O ( n * logn ) olacak olan sıralama yönteminden gelecektir. .


0

Bunun çalışma zamanının yaklaşık bir temsili olduğunu hatırlayın. “Temel kural” yaklaşıktır çünkü kesin değildir ancak değerlendirme amacıyla iyi bir birinci derece yaklaşım verir.

Gerçek çalışma zamanı, ne kadar yığın alanı, işlemcinin ne kadar hızlı olduğuna, komut setine, ön ekin veya düzeltme sonrası artış operatörlerinin vs. Uygun çalışma zamanı analizi, kabulün belirlenmesini sağlar, ancak temel bilgileri bilmeniz, en baştan programlamanıza izin verir.

Big-O'nun bir ders kitabından pratik bir uygulamaya nasıl rasyonelleştirildiğini anlamak için doğru yolda olduğunuzu kabul ediyorum. Üstesinden gelinmesi zor bir engel olabilir.

Asimptotik büyüme hızı büyük veri setlerinde ve büyük programlarda önemli hale gelir, bu nedenle tipik örnekler için uygun sözdizimi ve mantığın önemini göstermez.


-1

Büyük oh, tanım gereği: f (t) işlevi için c * g (t) işlevi var, burada c, isteğe bağlı bir sabit, öyle ki f (t) <= c * g (t) olur, burada n> n keyfi bir sabittir, o zaman f (t) O (g (t)) 'da bulunur.Bu, bilgisayar bilimlerinde algoritmaları analiz etmek için kullanılan matematiksel bir notasyondur. Kafanız karıştığında kapanma ilişkilerine bakmanızı tavsiye ederim, bu şekilde bu algoritmaların bu büyük oh değerlerini nasıl elde ettiğini daha ayrıntılı bir şekilde görebilirsiniz.

Bu tanımın bazı sonuçları: O (n) aslında O (2n) ile uyumludur.

Ayrıca birçok farklı sıralama algoritması vardır. Bir karşılaştırma türü için asgari Büyük-Oh değeri O (nlogn), ancak daha kötü büyük-oh olan birçok çeşit vardır. Örneğin, seçim sıralama O (n ^ 2). Bazı kıyaslama dışı sıralamaların daha iyi big-oh değerleri olabilir. Bir kova sıralama, örneğin O (n).

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.