Bir Sayının Üssünü O(logn) Zamanda Hesaplamak

Bir x sayısının n’inci üssünü almak için akla gelen ilk yöntem x sayısını n – 1 kere kendisi ile çarpmaktır.

int power(int x, int n){
    if(n == 0) return 1;
    int ans = x;
    for(int i = 0; i < n-1; ++i)
        ans *= x;
    return ans;
}

Kolaylıkla görülebileceği üzere bu algoritmanın zaman karmaşıklığı n ile lineer olarak değişmektedir. Neyseki biz zaman karmaşıklığını logaritmik olacak şekilde düşürebiliriz.

Kolayca anlaşılması adına n = 13 ve herhangi bir x için algoritmamızı inceleyelim. Öncelikle 13’ü 2 tabanında yazarak başlayalım. 13 sayısı 2 tabanında 1101_{2} şeklinde yazılır. Yani bizim hesaplamamız gereken değer x^{1101_{2}}‘ye eşittir. Kolayca görebiliriz ki bu değeri şu şekilde yazabiliriz.

x^{1101_{2}} = x^{1000_{2}} * x^{0100_{2}} * x^{0001_{2}}

= x^{8} * x^{4} * x^{1}

Herhang bir üs alma işlemini bu şekle getirdiğimizde bize gerekebilecek değerler x,x^{2},x^{4},x^{8}, ...., x^{\lfloor log_{2}(n)\rfloor} dizisinin elemanlarıdır.

Buradan kolayca görebiliriz ki eğer yine logaritmik zaman karmaşıklığında bu elemanları hesaplayabilirsek tüm işlemi logaritmik zaman karmaşıklığında yapabiliriz.

Bu değerleri hesaplamanın kolay bir yolu x‘i sürekli kendi ile çarpmaktır. Bu şekilde her adımda bir ifadeyi elde ettiğimiz ve en fazla \lfloor log_2(n) \rfloor + 1 ifade olacağından zaman karmaşıklığımız logaritmik olacaktır.

Peki dizinin bu elemanlarından hangilerini çarpmalıyız ki sonucu elde edelim? Burada x‘in oluşturduğu seride her bir elemanı hesaplarken n’de bu elemana karşılık gelen basamağın 1 ya da 0 olmasına bakarız. Eğer 1 ise cevabı şu anki x ile çarparız.

Özetlemek gerekirse adım adım:

1 – Şu an ki basamağın değerini öğrenmek için n’in 2’den kalanını bul.

2 – Eğer 1 ise cevabı x‘in şu anki değeri ile çarp.

3 – Basamağı Kaydırmak için n‘i 2 ile kalansız böl, x‘i kendi ile çarparak güncelle.

4 – Eğer n 0’a eşit ise işlemi bitir değilse 1’e dön

Burada basamak sayısı \lfloor log_2(n)\rfloor + 1 tanedir ve biz de tam olarak bu kadar işlem yapacağımız için zaman karmaşıklığımız O(logn)’dir.

x = 3 için algoritmamızı gösterelim:

cevap := 1 olarak tanımlayalım.

1.Adım: n = 13 = 1101_{2} , 2’den kalanı 1 bu yüzden cevap değişkenini x ile çarparak güncelleyelim ardından x‘i güncelleyelim,n’i de iki ile kalansız bölerek yeni basamağa getirelim :

cevap := cevap * x = 1 * 3 = 3

x := x * x = 3 * 3 = 9

n := \lfloor n/2\rfloor = 6

2.Adım : n = 6 = 110_{2} , 2’den kalanı 0 bu yüzden sadece x ve n’i güncelliyoruz.

x := x * x = 9 * 9 = 81 n :=  \lfloor n/2\rfloor = 3

3.Adım: n = 3 = 11_{2}

cevap := cevap * x = 3 * 81  = 243

x := x * x = 81 * 81  = 6.561

n :=  \lfloor n/2\rfloor = 1

4. Adım: n = 1 = 1_{2} , 2’den kalanı 1

cevap := cevap * x = 243 * 6561 = 1.594.323

x := x * x = 6.561*6.561 = 43.046.721

n :=  \lfloor n/2\rfloor = 0

n = 0 olduğundan işlemi bitiriyoruz ve cevabımızı 1.594.323 olarak buluyoruz.

Artık mantığı oturttuğumuza göre koda dökebiliriz. Aşağıda örnek bir C/C++ kodu vardır.

int power(int x, int n){
    int cevap = 1;
    while(n != 0){
        if(n % 2 == 1){ //Eğer şu anki bit açık ise  
            cevap = cevap * x; // Cevabı x ile çarparak güncelle
        }
        x *= x; // x'in değerini yükselt
        n /= 2; // biti kaydır.
    }
    return cevap;
}


One Comment

Add a Comment

Your email address will not be published. Required fields are marked *