O(log n)
Zaman karmaşıklığını zaman karmaşıklığına tercih edeceğiniz durumlar var O(1)
mı? Ya da O(n)
hiç O(log n)
?
Hiç örneğin var mı?
O(log n)
Zaman karmaşıklığını zaman karmaşıklığına tercih edeceğiniz durumlar var O(1)
mı? Ya da O(n)
hiç O(log n)
?
Hiç örneğin var mı?
Yanıtlar:
Düşük olana göre daha yüksek büyük O zaman karmaşıklığına sahip bir algoritmayı tercih etmek için birçok neden olabilir:
10^5
büyük-O bakış açısından 1/10^5 * log(n)
( O(1)
vs O(log(n)
) ' den daha iyidir , ancak en mantıklı n
olarak ilk olan daha iyi performans gösterecektir. Örneğin, matris çarpımı için en iyi karmaşıklık, O(n^2.373)
ancak sabit o kadar yüksektir ki (benim bildiğim kadarıyla) hiçbir hesaplama kütüphanesi bunu kullanmaz.O(n*log(n))
veya O(n^2)
algoritması.O(log log N)
bir öğeyi bulmak için zaman karmaşıklığı veren bir veri yapısı tango ağacı vardır , ancak aynı şeyi bulan bir ikili ağaç da vardır O(log n)
. Çok sayıda n = 10^20
fark olsa bile ihmal edilebilir.O(n^2)
ve O(n^2)
bellek gerektiren bir algoritma düşünün . N gerçekten büyük olmadığında O(n^3)
zaman ve O(1)
mekanda tercih edilebilir . Sorun, uzun süre beklemeniz, ancak algoritmanızla kullanmak için yeterince büyük bir RAM bulabileceğinizden şüphe duymanızdır.O(n^2)
hızlı sıralama veya birleştirme sıralamasından daha kötü bir ortalama zaman karmaşıklığına sahiptir , ancak çevrimiçi bir algoritma olarak, diğer çoğu algoritmanın yalnızca verimli bir şekilde çalışabildiği bir değer listesini alındıklarında (kullanıcı girişi olarak) verimli bir şekilde sıralayabilir değerlerin tam listesi.Her zaman O (log n ) algoritmasında daha düşük olabilecek gizli sabit vardır . Böylece gerçek hayattaki veriler için pratikte daha hızlı çalışabilir.
Alan endişeleri de var (örneğin bir ekmek kızartma makinesinde koşma).
Ayrıca geliştirici zaman kaygısı da vardır - O (log n ) uygulaması ve doğrulaması 1000 × daha kolay olabilir.
lg n
böylece, bu yüzden, bu kadar yakın olan k
büyük için n
en operasyonlar fark asla.
Henüz kimsenin hafızaya bağlı uygulamalardan bahsetmediğine şaşırdım.
Karmaşıklığı nedeniyle (yani O (1) < O (log n )) veya karmaşıklığın önündeki sabit daha küçük olduğu için (yani 2 n 2 <6 n 2 ) daha az kayan nokta işlemine sahip bir algoritma olabilir. . Ne olursa olsun, daha düşük FLOP algoritması daha fazla belleğe bağlıysa, daha fazla FLOP içeren algoritmayı tercih edebilirsiniz.
"Belleğe bağlı" ile kastettiğim, sürekli önbellek dışında kalan verilere eriştiğinizdir. Bu verileri almak için işleminizi gerçekleştirmeden önce belleği gerçekte bellek alanınızdan önbelleğinize almanız gerekir. Bu getirme adımı genellikle oldukça yavaştır - işleminizin kendisinden çok daha yavaştır.
Bu nedenle, algoritmanız daha fazla işlem gerektiriyorsa (yine de bu işlemler zaten önbellekte olan verilerde gerçekleştirilir (ve bu nedenle getirme gerekmez)), algoritmanız daha az işlemle (dışarıda yapılması gereken) - gerçek duvar zamanı açısından önbellek verilerini [ve dolayısıyla getirmeyi gerektirir]).
O(logn)
üzerinde O(1)
. Mümkün olan her şey için n
, daha az bellek gerektiren uygulamanın daha yüksek karmaşıklıkta bile daha hızlı duvar zamanında çalışacağı bir durumu kolayca hayal edebilirsiniz .
Veri güvenliğinin önemli olduğu bağlamlarda, daha karmaşık algoritmanın zamanlama saldırılarına karşı daha iyi direnci varsa, daha az karmaşık bir algoritmaya daha karmaşık bir algoritma tercih edilebilir .
(n mod 5) + 1
, yine de O(1)
hakkında bilgi verir n
. Bu nedenle, asemptotik (ve muhtemelen pratikte) daha yavaş olsa bile daha sorunsuz çalışma süresine sahip daha karmaşık bir algoritma tercih edilebilir.
Alistra çivilenmiş ama örnek veremedi, ben yapacağım.
Mağazanızın sattığı ürün için 10.000 UPC kodu içeren bir listeniz var. 10 haneli UPC, fiyat için tam sayı (pennies cinsinden fiyat) ve makbuz için 30 karakter açıklaması.
O (log N) yaklaşımı: Sıralanmış bir listeniz var. ASCII ise 44 bayt, Unicode ise 84 bayt. Alternatif olarak, UPC'ye int64 gibi davranın ve 42 ve 72 bayt elde edin. 10.000 kayıt - en yüksek durumda, bir megabaytlık depolama alanının biraz altına bakıyorsunuz.
O (1) yaklaşımı: UPC'yi saklamayın, bunun yerine diziye giriş olarak kullanın. En düşük durumda terabaytlık bir depolama alanının neredeyse üçte birine bakıyorsunuz.
Hangi yaklaşımı kullandığınız, donanımınıza bağlıdır. Çoğu makul modern konfigürasyonda log N yaklaşımını kullanacaksınız. RAM'in kritik derecede kısa olduğu ancak bol miktarda yığın depolama alanınız olan bir ortamda çalışıyorsanız, ikinci yaklaşımın doğru cevap olduğunu düşünebilirim. Bir diskteki terabaytın üçte biri önemli değildir, verilerinizi diskin bir sondasına almak bir şeye değer. Basit ikili yaklaşım ortalama 13 alır. (Ancak, anahtarlarınızı kümeleyerek bunu garantili 3 okumaya indirebileceğiniz ve pratikte ilkini önbelleğe alabileceğinizi unutmayın.)
malloc(search_space_size)
geri döndüğünü söylemek ve abone olmak, elde etmek kadar kolaydır.
Kırmızı-siyah bir ağaç düşünün. Erişebilir, arayabilir, ekleyebilir ve silebilirO(log n)
. Erişimi olan bir dizi ile karşılaştırın O(1)
ve işlemlerin geri kalanı O(n)
.
Bu nedenle, eriştiğimizden daha sık eklediğimiz, sildiğimiz veya aradığımız bir uygulama ve sadece bu iki yapı arasında bir seçim yapıldığında, kırmızı-siyah ağacı tercih ederiz. Bu durumda, kırmızı-siyah ağacın daha hantal O(log n)
erişim süresini tercih ettiğimizi söyleyebilirsiniz .
Neden? Çünkü erişim bizim en büyük endişemiz değil. Bir takas yapıyoruz: başvurumuzun performansı, bunun dışındaki faktörlerden daha fazla etkileniyor. Diğer algoritmaları optimize ederek büyük kazançlar elde ettiğimiz için bu özel algoritmanın performanstan etkilenmesine izin veriyoruz.
Sorunuzun cevabı basitçe şu: algoritmanın büyüme oranı optimize etmek istediğimiz şey olmadığında, optimize etmek istediğimizde şey olmadığında, başka bir şeyi. Diğer tüm cevaplar bunun özel durumlarıdır. Bazen diğer işlemlerin çalışma süresini optimize ederiz. Bazen bellek için optimize ediyoruz. Bazen güvenlik için optimize ediyoruz. Bazen sürdürülebilirliği optimize ederiz. Bazen geliştirme süresi için optimize ederiz. Algoritmanın büyüme hızının çalışma süresi üzerinde en büyük etki olmadığını bildiğinizde, önemli olan sabitin önemli ölçüde düşük olması bile çalışma süresi için optimize eder. (Veri kümeniz bu aralığın dışındaysa, algoritmanın büyüme oranını optimize edersiniz, çünkü sonunda sabite hükmeder.) Her şeyin bir maliyeti vardır ve birçok durumda, daha yüksek bir büyüme oranının maliyetini başka bir şey optimize etmek için algoritma.
O(log n)
"kırmızı-siyah ağaç" olduğunu beyan ettiğiniz "erişim" ve "arama " nedir? Arasında yerleştirin 5
dizinin 2 konumunda [1, 2, 1, 4]
neden olur[1, 2, 5, 1 4]
(element 4
indeksi 4'e 3 ila güncellenecektir). O(log n)
"Sıralı liste" olarak adlandırdığınız "kırmızı-siyah ağaç" içinde bu davranışı nasıl elde edersiniz ?
Evet.
Gerçek bir durumda, hem kısa hem de uzun dize anahtarlarıyla tablo aramaları yapmak için bazı testler yaptık.
Bir std::map
, a std::unordered_map
, a dizginin uzunluğu boyunca en fazla 10 kez örnek veren bir karma (anahtarlarımız kılavuzlu olma eğilimindedir, bu yüzden bu iyi) ve her karakteri (teoride çarpışmaları azalttı) örnekleyen bir karma kullandık, bir ==
karşılaştırma yaptığımız sıralanmamış bir vektör ve ayrıca bir hash sakladığımız sıralanmamış bir vektör, önce hash'ı karşılaştırın, sonra karakterleri karşılaştırın.
Bu algoritmalar O(1)
(unordered_map) ile O(n)
(doğrusal arama) arasında değişir .
Mütevazı boyutta N için, sıklıkla O (n) O (1) 'i geçer. Bunun nedeni, düğüm tabanlı kapların bilgisayarımızın bellekte daha fazla atlamasını gerektirdiği, ancak doğrusal tabanlı kapların gerektirmediğinden şüpheleniyoruz.
O(lg n)
ikisi arasında var. Nasıl olduğunu hatırlamıyorum.
Performans farkı o kadar büyük değildi ve daha büyük veri setlerinde karma tabanlı olan çok daha iyi performans gösterdi. Bu yüzden karma tabanlı düzensiz haritaya saptık.
Uygulamada, makul büyüklükte n için, O(lg n)
bir O(1)
. Bilgisayarınızda tablonuzda yalnızca 4 milyar giriş için yer varsa O(lg n)
, yukarıda sınırlandırılmıştır 32
. (lg (2 ^ 32) = 32) (bilgisayar bilimlerinde, lg log tabanlı 2 için kısa eldir).
Pratikte, lg (n) algoritmaları logaritmik büyüme faktörü nedeniyle değil O (1) algoritmalarından daha yavaştır, çünkü lg (n) kısmı genellikle algoritmaya belirli bir karmaşıklık düzeyi anlamına gelir ve bu karmaşıklık bir lg (n) teriminden herhangi bir "büyüme" den daha büyük sabit faktör.
Bununla birlikte, karmaşık O (1) algoritmaları (karma eşleme gibi) kolayca benzer veya daha büyük bir sabit faktöre sahip olabilir.
Paralel olarak bir algoritma yürütme olasılığı.
Sınıflara örnek olup olmadığını bilmiyorum O(log n)
veO(1)
ancak bazı problemler için, algoritmanın paralel olarak yürütülmesi daha kolay olduğunda daha yüksek karmaşıklık sınıfına sahip bir algoritma seçersiniz.
Bazı algoritmalar paralel hale getirilemez ancak karmaşıklık sınıfı çok düşüktür. Aynı sonucu elde eden ve kolayca paralelleştirilebilen, ancak daha yüksek bir karmaşıklık sınıfına sahip olan başka bir algoritmayı düşünün. Bir makinede yürütüldüğünde, ikinci algoritma daha yavaştır, ancak birden çok makinede yürütüldüğünde, ilk algoritma hızlanamazken gerçek yürütme süresi azalır.
Diyelim ki, 0 ile 1.000.000 arasındaki sayıların kara listeye alınabileceği gömülü bir sisteme bir kara liste uyguluyorsunuz. Bu size iki seçenek sunar:
Bit kümesine erişim, sürekli erişimi garanti edecektir. Zaman karmaşıklığı açısından en uygunudur. Hem teorik hem de pratik bir bakış açısından (son derece düşük sabit bir yük ile O (1)).
Yine de ikinci çözümü tercih etmek isteyebilirsiniz. Özellikle kara listeye alınmış tamsayıların sayısının çok az olmasını beklerseniz, bellekte daha verimli olacağından.
Ve hafızanın az olduğu gömülü bir sistem için geliştirmeseniz bile, sadece 1.000.000 ila 1.000.000.000.000 arasındaki keyfi sınırı artırabilir ve aynı argümanı yapabilirim. Daha sonra bit seti yaklaşık 125G bellek gerektirir. O (1) garantili en kötü durum karmaşıklığına sahip olmak, patronunuzu size bu kadar güçlü bir sunucu sağlamaya ikna etmeyebilir.
Burada, O (1) bit kümesi yerine bir ikili aramayı (O (log n)) veya ikili ağacı (O (log n)) tercih ederim. Ve muhtemelen, O (n) 'nin en kötü durum karmaşıklığına sahip bir karma tablo, uygulamada hepsini yenecektir.
Burada cevabım Stokastik bir matrisin tüm satırlarında hızlı rastgele ağırlıklı seçim, O (m) karmaşıklığına sahip bir algoritmanın, m
çok büyük olmadığı zaman O (log (m)) karmaşıklığına sahip bir algoritmadan daha hızlı olduğu bir örnektir .
İnsanlar sorunuzu tam olarak yanıtladı, bu yüzden buraya geldiklerinde insanların gerçekten düşünebileceği biraz farklı bir sorunun üstesinden geleceğim.
"O (1) zaman" algoritmalarının ve veri yapılarının çoğu aslında sadece beklenen O (1) zamanını alır, yani ortalama çalışma sürelerinin O (1) olduğu , muhtemelen sadece belirli varsayımlar altında.
Ortak örnekler: karma tablolar, "dizi listelerinin" genişletilmesi (dinamik olarak boyutlandırılmış diziler / vektörler olarak da bilinir).
Bu tür senaryolarda, ortalama olarak daha kötü performans göstermelerine rağmen , zamanlarının kesinlikle logaritmik olarak sınırlandırılması garanti edilen veri yapılarını veya algoritmaları kullanmayı tercih edebilirsiniz.
Bu nedenle bir örnek, çalışma süresi ortalama olarak daha kötü ancak en kötü durumda daha iyi olan dengeli bir ikili arama ağacı olabilir.
Daha genel bir soru, kişinin bir O(f(n))
algoritmayı algoritmaya tercih edeceği durumlar O(g(n))
olsa dag(n) << f(n)
olarak n
sonsuza eğilimindedir. Diğerleri zaten söylediğim gibi cevap açıkça durumda "evet" f(n) = log(n)
ve g(n) = 1
. f(n)
Polinom olan ancak g(n)
üstel olan durumlarda bile bazen evettir . Ünlü ve önemli bir örnek doğrusal programlama problemlerini çözmek için Simplex Algoritmasıdır . 1970'lerde olduğu gösterilmiştir O(2^n)
. Bu nedenle, en kötü durum davranışı mümkün değildir. Ancak - ortalama vaka davranışı, on binlerce değişken ve kısıtlama ile ilgili pratik problemler için bile son derece iyidir. 1980'lerde polinom zaman algoritmaları ( Karmarkar'ın iç nokta algoritması) doğrusal programlama için keşfedildi, ancak 30 yıl sonra simpleks algoritması hala tercih edilen algoritma gibi görünüyor (bazı çok büyük problemler hariç). Bu, ortalama durum davranışının genellikle daha kötü durum davranışından daha önemli olduğu açık bir nedendir, ancak aynı zamanda simpleks algoritmanın bir anlamda daha bilgilendirici olması (ör. Duyarlılık bilgilerinin çıkarılması daha kolaydır).
Benim 2 sent koymak için:
Bazen algoritma belirli bir donanım ortamında çalıştığında daha iyi bir karmaşıklık algoritması seçilir. Sorunumuzu çözmek için O (1) algoritmamızın çok büyük, sabit boyutlu bir dizinin her öğesine ardışık olarak erişmediğini varsayalım. Ardından bu diziyi mekanik bir sabit sürücüye veya manyetik bir banda koyun.
Bu durumda, O (logn) algoritması (diske sırayla eriştiğini varsayalım), daha uygun hale gelir.
O (1) algoritması yerine O (log (n)) algoritması kullanmak için çok sayıda diğer cevabın göz ardı ettiği iyi bir kullanım durumu vardır: değişmezlik. Hash haritalarında O (1) vardır ve hash değerlerinin iyi dağılımını varsayar, ancak değişebilir duruma ihtiyaç duyarlar. Değişmez ağaç haritalarının asimptotik olarak daha yavaş olan O (log (n)) koyar ve alır. Bununla birlikte, değişmezlik, daha kötü performans için telafi edecek kadar değerli olabilir ve haritanın birden fazla versiyonunun tutulması gerektiğinde, değişmezlik, haritayı kopyalamaktan kaçınmanıza izin verir, bu O (n) ve bu nedenle iyileştirebilir verim.
Basitçe: Çünkü katsayı - kurulum, depolama ve bu adımın yürütme süresi ile ilişkili maliyetler - daha büyük olandan daha küçük bir büyük O problemiyle çok daha büyük olabilir. Big-O algoritma ölçeklenebilirliğinin sadece bir ölçüsüdür .
Hacker Sözlüğü'nden Kuantum Mekaniğinin Çoklu Dünya Yorumuna dayanan bir sıralama algoritması öneren aşağıdaki örneği düşünün :
- Bir kuantum işlemi kullanarak diziye rastgele izin ver,
- Dizi sıralanmamışsa, evreni yok edin.
- Kalan tüm evrenler artık [bulunduğunuz dünya dahil) sıralanmıştır.
(Kaynak: http://catb.org/~esr/jargon/html/B/bogo-sort.html )
Bu algoritmanın big-O olduğuna dikkat edin. Bu O(n)
, bilinen herhangi bir sıralama algoritmasını genel öğeler üzerinde bugüne kadar yener. Doğrusal adımın katsayısı da çok düşüktür (çünkü sadece bir karşılaştırma, bir takas değil, doğrusal olarak yapılır). Benzer bir algoritma, aslında, her ikisi de herhangi bir sorunu çözmek için kullanılabilir NP ve ko-NP , her olası bir çözüm, çünkü polinom zamanda (ya da herhangi bir çözüm yoktur mümkün geçirmez) kuantum işlemi kullanılarak üretilebilir, daha sonra belirlenmiş polinom zamanı.
Bununla birlikte, çoğu durumda, muhtemelen 2. adımı uygulama eyleminin hala "okuyucu için bir egzersiz olarak kaldığından" bahsetmiyoruz bile, Çoklu Dünyaların doğru olmayabileceği riskini almak istemiyoruz.
N'nin sınırlandığı ve O (1) algoritmasının sabit çarpanının log (n) 'deki sınırdan daha yüksek olduğu herhangi bir noktada. Örneğin, değerlerin bir karma kümesinde depolanması O (1) 'dir, ancak karma işlevinin pahalı bir hesaplanmasını gerektirebilir. Veri öğeleri önemsiz bir şekilde karşılaştırılabilirse (bazı siparişlere göre) ve n üzerindeki sınır, log n'nin herhangi bir öğedeki karma hesaplamasından önemli ölçüde daha az olması durumunda, dengeli bir ikili ağaçta depolamak, depolamaktan daha hızlı olabilir bir hashset.
Sağlam bir üst sınıra ihtiyaç duyduğunuz gerçek zamanlı bir durumda, örneğin bir Quicksort'un aksine bir yığın seçersiniz, çünkü yığının ortalama davranışı da en kötü durum davranışıdır.
Zaten iyi cevaplara ekleme Pratik bir örnek postgres veritabanındaki Hash dizinleri ve B-ağacı dizinleri olacaktır.
Karma dizinler, adından da anlaşılacağı gibi btree bir Btree veri yapısı kullandığında diskteki verilere erişmek için bir karma tablo dizini oluşturur.
Big-O zamanında bunlar O (1) ve O (logN) 'dir.
Hash dizinleri şu anda postgreslerde cesaretini kırıyor, çünkü özellikle veritabanı sistemlerinde gerçek bir yaşam durumunda, çarpışma olmadan karma elde etmek çok zor (O (N) en kötü durum karmaşıklığına yol açabilir) ve bu nedenle, yapmak daha da zor güvenli çöküyorlar (yazma öncesinde günlüğe kaydetme denir - postgreslerde WAL).
Bu takas bu durumda yapılır çünkü O (logN) indeksler için yeterince iyidir ve O (1) 'in uygulanması oldukça zordur ve zaman farkı gerçekten önemli değildir.
veya
Bu, birisinin bir soruna çok hızlı bir şekilde yanıt almasını önlemek için algoritmaları bilerek yavaş olan sorunları tasarlamak istediğimiz güvenlik uygulamaları için genellikle geçerlidir.
İşte kafamın üstünden birkaç örnek.
O(2^n)
zamanında kırılacak şekilde tasarlanmıştır n
.CS'nin başka yerlerinde, Hızlı Sıralama O(n^2)
en kötü durumda ancak genel durumda O(n*log(n))
. Bu nedenle, algoritma verimliliğini analiz ederken bazen “Büyük O” analizi önem vermez.
O(log n)
O(1)