Bir O (log n) algoritmasını ilk gördüğünüzde oldukça tuhaf olduğunu kabul etmeliyim ... bu logaritma nereden geliyor? Bununla birlikte, büyük-O gösteriminde görünmek için bir günlük terimi almanın birkaç farklı yolu olduğu ortaya çıktı. Burda biraz var:
Tekrar tekrar bir sabite bölerek
Herhangi bir sayıyı al n; diyelim 16. Bire eşit veya daha küçük bir sayı elde etmeden önce n'yi kaç kez ikiye bölebilirsin? 16 yaşımız için buna sahibiz
16 / 2 = 8
8 / 2 = 4
4 / 2 = 2
2 / 2 = 1
Bunun tamamlanması için dört adım attığına dikkat edin. İlginç bir şekilde, bu log 2 16 = 4'e sahibiz . Hmmm ... 128'e ne dersiniz?
128 / 2 = 64
64 / 2 = 32
32 / 2 = 16
16 / 2 = 8
8 / 2 = 4
4 / 2 = 2
2 / 2 = 1
Bu yedi adım attı ve log 2 128 = 7. Bu bir tesadüf mü? Hayır! Bunun için iyi bir sebep var. Farz edelim ki n sayısını i çarpı 2'ye böldük. Sonra n / 2 i sayısını elde ederiz . Bu değerin en fazla 1 olduğu yerde i değerini çözmek istersek,
n / 2 ben ≤ 1
n ≤ 2 ben
günlük 2 n ≤ i
Diğer bir deyişle, i ≥ log 2 n olacak şekilde bir i tamsayısı seçersek , n'yi i çarpı ikiye böldükten sonra en fazla 1 olan bir değere sahip oluruz. Bunun garanti edildiği en küçük i kabaca log 2'dir. n, yani sayı yeterince küçük olana kadar 2'ye bölen bir algoritmamız varsa, O (log n) adımlarında sonlandığını söyleyebiliriz.
Önemli bir ayrıntı, n'yi neye böldüğünüzün (birden büyük olduğu sürece) önemli olmamasıdır; sabit k'ye bölerseniz, 1'e ulaşmak için log k n adım gerekir. Dolayısıyla, girdi boyutunu tekrar tekrar bir kesire bölen herhangi bir algoritmanın, sonlandırmak için O (log n) yinelemelerine ihtiyacı olacaktır. Bu yinelemeler çok zaman alabilir ve bu nedenle net çalışma zamanının O (log n) olması gerekmez, ancak adımların sayısı logaritmik olacaktır.
Peki bu nerede ortaya çıkıyor? Klasik bir örnek, bir değer için sıralı bir diziyi aramak için hızlı bir algoritma olan ikili aramadır . Algoritma şu şekilde çalışır:
- Dizi boşsa, elemanın dizide bulunmadığını döndür.
- Aksi takdirde:
- Dizinin orta elemanına bakın.
- Aradığımız öğeye eşitse, başarıya dönün.
- Aradığımız öğeden daha büyükse:
- Dizinin ikinci yarısını atın.
- Tekrar et
- Aradığımız öğeden daha azsa:
- Dizinin ilk yarısını atın.
- Tekrar et
Örneğin, dizide 5'i aramak için
1 3 5 7 9 11 13
İlk önce orta öğeye bakacağız:
1 3 5 7 9 11 13
^
7> 5 olduğundan ve dizi sıralandığından, 5 sayısının dizinin arka yarısında olamayacağını biliyoruz, bu yüzden onu atabiliriz. Bu yapraklar
1 3 5
Şimdi buradaki orta öğeye bakıyoruz:
1 3 5
^
3 <5 olduğundan, 5'in dizinin ilk yarısında görünemeyeceğini biliyoruz, bu nedenle ilk yarı diziyi bırakmak için atabiliriz
5
Yine bu dizinin ortasına bakıyoruz:
5
^
Bu tam olarak aradığımız sayı olduğu için, 5'in gerçekten dizide olduğunu bildirebiliriz.
Peki bu ne kadar verimli? Her yinelemede, kalan dizi elemanlarının en az yarısını çöpe atıyoruz. Dizi boşalır boşalmaz algoritma durur veya istediğimiz değeri buluruz. En kötü durumda, öğe orada değildir, bu nedenle öğeler tükenene kadar dizinin boyutunu yarıya indirmeye devam ederiz. Ne kadar sürer? Pekala, diziyi tekrar tekrar yarıya indirmeye devam ettiğimiz için, en çok O (log n) yinelemede işimiz bitecek, çünkü diziyi çalıştırmadan önce O (log n) kereden daha fazla yarı yarıya kesemeyiz dizi elemanlarının dışında.
Genel bölme ve yönetme tekniğini izleyen algoritmalar (problemi parçalara ayırma, bu parçaları çözme, sonra problemi tekrar bir araya getirme) aynı nedenle içlerinde logaritmik terimlere sahip olma eğilimindedir - bazı nesneleri kesmeye devam edemezsiniz. O (log n) katından yarısı kadar. Bunun harika bir örneği olarak birleştirme sıralamasına bakmak isteyebilirsiniz .
Bir seferde bir basamak değerlerin işlenmesi
10 nolu taban sayısında kaç basamak vardır? Sayıda k basamak varsa, o zaman en büyük basamağın 10 k'nin birkaç katı olduğunu elde ederiz . En büyük k basamaklı sayı 999 ... 9, k kez ve bu 10 k + 1 - 1'e eşittir . Sonuç olarak, n'nin içinde k basamakları olduğunu bilirsek, o zaman n'nin değerinin en çok 10 k + 1 - 1. k için n cinsinden çözmek istersek, şunu elde ederiz
n ≤ 10 k + 1-1
n + 1 ≤ 10 k + 1
günlük 10 (n + 1) ≤ k + 1
(günlük 10 (n + 1)) - 1 ≤ k
Buradan k, yaklaşık olarak n'nin 10 tabanlı logaritmasıdır. Başka bir deyişle, n'deki hane sayısı O (log n) 'dir.
Örneğin, bir makine kelimesine sığmayacak kadar büyük olan iki büyük sayıyı toplamanın karmaşıklığını düşünelim. Bu sayıların 10 tabanında temsil edildiğini varsayalım ve m ve n sayılarını arayacağız. Bunları eklemenin bir yolu ilkokul yöntemidir - sayıları bir seferde bir basamak yazın, sonra sağdan sola doğru çalışın. Örneğin, 1337 ve 2065'i eklemek için sayıları şöyle yazarak başlarız:
1 3 3 7
+ 2 0 6 5
==============
Son basamağı ekliyoruz ve 1:
1
1 3 3 7
+ 2 0 6 5
==============
2
Sonra ikinci ("sondan bir önceki") basamağı ekleriz ve 1'i taşırız:
1 1
1 3 3 7
+ 2 0 6 5
==============
0 2
Daha sonra, üçüncüden sona ("antepenultimate") rakamı ekliyoruz:
1 1
1 3 3 7
+ 2 0 6 5
==============
4 0 2
Son olarak, sondan dördüncü ("preantepenultimate" ... İngilizceyi seviyorum) hanesini ekliyoruz:
1 1
1 3 3 7
+ 2 0 6 5
==============
3 4 0 2
Şimdi, ne kadar iş yaptık? Rakam başına toplam O (1) iş yapıyoruz (yani, sabit bir iş miktarı) ve işlenmesi gereken O (max {log n, log m}) toplam basamak var. Bu, toplam O (maks {log n, log m}) karmaşıklığı verir, çünkü iki sayıdaki her bir rakamı ziyaret etmemiz gerekir.
Birçok algoritma, bir bazda bir seferde bir basamakta çalışmaktan içlerinde bir O (log n) terimi alır. Klasik bir örnek, tam sayıları bir seferde bir basamak olarak sıralayan radix sıralamasıdır . Radix sıralamanın birçok çeşidi vardır, ancak bunlar genellikle O (n log U) zamanında çalışırlar, burada U, sıralanan olası en büyük tamsayıdır. Bunun nedeni, sıralamanın her geçişinin O (n) süresi alması ve sıralanan en büyük sayının O (log U) rakamlarının her birini işlemek için gereken toplam O (log U) yinelemesinin olmasıdır. Gabow'un en kısa yol algoritması veya Ford-Fulkerson maksimum akış algoritmasının ölçeklendirme versiyonu gibi birçok gelişmiş algoritma, her seferinde bir basamakta çalıştıkları için karmaşıklıklarında bir günlük terimine sahiptir.
Bu sorunu nasıl çözeceğinizle ilgili ikinci sorunuza gelince, daha gelişmiş bir uygulamayı araştıran bu ilgili soruya bakmak isteyebilirsiniz . Burada açıklanan problemlerin genel yapısı göz önüne alındığında, sonuçta bir günlük terim olduğunu bildiğinizde problemler hakkında nasıl düşüneceğinizi artık daha iyi anlayabilirsiniz, bu yüzden siz verene kadar cevaba bakmamanızı tavsiye ederim. bazı düşünceler.
Bu yardımcı olur umarım!
O(log n)
Şu şekilde görülebilir: Problem boyutunu ikiye katlarsanızn
, algoritmanızın yalnızca sabit sayıda adım daha fazlasına ihtiyacı vardır.