İçeriğe geç

Menü

Berk Akademi
Yorumlar Google yorumları Birebir ders başvurusu Ücretsiz ön görüşme Ana Sayfa

Uzay Karmaşıklığı Nedir? Bellek Kullanımı Nasıl Hesaplanır?

uzay-karmasikligi-nedir
Bu yazıda neler var?
  1. Girdi ile ek bellek neden ayrı sayılır?
  2. O(1), O(n) ve O(n²) ek bellek için sezgi
  3. Aynı algoritmanın üç sürümü: en büyük değeri bulmak
  4. Uzay karmaşıklığı hesabında sık yapılan üç hata
  5. Zaman ile bellek arasındaki takas
  6. Üç soruluk karar çerçevesi: ek belleği nasıl bulursun?

Uzay karmaşıklığı, bir algoritmanın girdi büyüdükçe girdi dışında kullandığı ek belleğin nasıl arttığını anlatır. Hesaplarken yardımcı değişkenleri, yeni oluşturulan dizileri ve listeleri, girdinin kopyalarını ve özyinelemede çağrı yığınında açılan çerçeveleri sayarsın. Sonucu Big O gösterimiyle yazarsın: O(1), O(n), O(n²) gibi.

Zaman karmaşıklığında "kaç adım atıldı?" diye sorarsın. Uzay karmaşıklığında soru değişir: "kaç ek hücre ayrıldı?" Bu yazıda önce neyin sayılıp neyin sayılmadığını netleştirecek, sonra üç temel büyüme biçimini örneklerle göreceksin.

Girdi ile ek bellek neden ayrı sayılır?

Bir algoritmaya verilen liste, sayı ya da metin girdidir. Algoritmanın çalışırken kendi oluşturduğu her şey ise ek bellektir: yardımcı değişkenler, yeni listeler, kopyalar ve çağrı yığınındaki çerçeveler.

Küçük bir örnekle ayır. Fonksiyona parametre olarak gelen sayilar listesi girdidir. Fonksiyonun içinde kurduğun sonuc = [] listesi ise ek bellektir, çünkü girdi o listeyi sen istemeden vardı, sonucu sen ürettin.

Bu ayrım hesabın merkezinde durur. Girdi her algoritma için zaten n büyüklüğünde yer kaplar. Onu da sayarsan hangi algoritmayı incelersen incele sonuç en az O(n) çıkar ve algoritmalar arasındaki fark görünmez olur. Yalnızca ek belleğe bakınca bir algoritmanın listeyi yerinde işleyip işlemediğini ya da yeni bir kopya üretip üretmediğini ayırt edebilirsin.

Uzay karmaşıklığını hesaplarken algoritmanın aldığı girdiyi değil, girdi dışında oluşturduğu ek belleğin büyümesini takip et.

Çağrı yığınını da bu ilkeye dahil et. Özyinelemeli bir fonksiyon kendini çağırdıkça her çağrı için yığında yeni bir çerçeve açılır. Kodda hiç liste kurmasan bile, iç içe çağrı derinliği ek bellek demektir. Bu konuyu makalenin ilerleyen bölümlerinde ayrıca ele alacağız.

Big O bellek ölçümü değildir

Asimptotik hesap, bir algoritmanın belleği girdiyle birlikte hangi eğilimle büyüttüğünü söyler. Bayt ya da başka bir ölçüm değeri vermez. Sabit çarpanlar hesaba girmez: her çağrıda üç değişken tutan algoritma da bir değişken tutan algoritma da O(1)'dir. Programlama dilinin ve çalışma ortamının nesneleri nasıl sakladığı gibi ayrıntılar da bu hesabın dışında kalır. Bu yüzden iki O(n) algoritmanın gerçek bellek kullanımı birbirinden farklı olabilir. Big O'yu bir ölçüm cetveli değil, büyüme yönünü gösteren bir pusula olarak düşün.

O(1), O(n) ve O(n²) ek bellek için sezgi

O(1), O(n) ve O(n²) ek bellek için sezgi

Üç büyüme biçimini, girdi iki katına çıktığında ek belleğe ne olduğuyla düşünebilirsin.

  • O(1): Girdi ne kadar büyürse büyüsün sayısı değişmeyen birkaç değişken tutulur. Örneğin bir sayaç ve bir geçici değer.
  • O(n): Girdi kadar eleman taşıyan bir yardımcı liste ya da girdinin bir kopyası oluşturulur. Girdi iki katına çıkarsa ek bellek de yaklaşık iki katına çıkar.
  • O(n²): n x n bir tablo ya da tüm çiftleri saklayan bir yapı kurulur. Örneğin n düğümlü bir graf için her düğüm çiftine bir hücre ayıran komşuluk matrisi. Girdi iki katına çıkarsa ek bellek yaklaşık dört katına çıkar.

n x n tablonun nasıl oluştuğunu kısa bir kodla görebilirsin:

n = 3
tablo = [[0] * n for _ in range(n)]
print(len(tablo))
print(len(tablo[0]))
print(len(tablo) * len(tablo[0]))

Beklenen çıktı:

3
3
9

Üç satır var ve her satırda üç hücre bulunuyor, toplam 9 hücre. n = 4 olduğunda 16, n = 10 olduğunda 100 hücre olur. Tablonun hücre sayısı n'in karesi gibi büyüdüğü için bu yapı O(n²) ek bellek kullanır.

Kendi sezgini dilden bağımsız bir soru setiyle sınamak istersen ücretsiz Algoritmik Düşünme Testi, algoritma mantığını ölçmek için kullanabileceğin bir araçtır.

Aynı algoritmanın üç sürümü: en büyük değeri bulmak

Aynı işi yapan üç fonksiyon, üç farklı ek bellek davranışı gösterebilir. Hepsi listedeki en büyük değeri bulur, ama her biri girdi dışında farklı miktarda bellek kurar.

def en_buyuk_dongu(liste):
    en_buyuk = liste[0]
    for sayi in liste:
        if sayi > en_buyuk:
            en_buyuk = sayi
    return en_buyuk

def en_buyuk_kopya(liste):
    kopya = sorted(liste)      # girdiyle aynı boyda yeni liste
    return kopya[-1]

def en_buyuk_ozyinelemeli(liste, i=0):
    if i == len(liste) - 1:
        return liste[i]
    kalan = en_buyuk_ozyinelemeli(liste, i + 1)
    return max(liste[i], kalan)

veri = [4, 9, 2, 7]
print(en_buyuk_dongu(veri))
print(en_buyuk_kopya(veri))
print(en_buyuk_ozyinelemeli(veri))

Beklenen çıktı:

9
9
9

Birinci sürüm yalnızca en_buyuk ve sayi değişkenlerini tutar. Liste ne kadar uzarsa uzasın bu iki değişken aynı kalır, yani ek bellek O(1).

İkinci sürüm sorted ile girdiyle aynı uzunlukta yeni bir liste kurar. Ek bellek listeyle birlikte büyür: O(n). (Burada yalnızca belleğe bakıyoruz; sıralamanın zaman maliyeti ayrı bir konu.)

Üçüncü sürümde yeni liste yoktur, ama her çağrı çağrı yığınında bir çerçeve açar. Dört elemanlı listede çağrılar i=0, i=1, i=2 ve i=3 değerleriyle iç içe girer. En derin noktada 4 çerçeve üst üste durur ve her biri kendi i ile kalan değişkenleri için bellekte yer tutar. i=3 çağrısı 7 döndürünce çerçeve boşalır; ardından i=2, i=1 ve i=0 sırayla kapanır. Yığın derinliği n ile orantılı olduğundan ek bellek O(n).

Python'da özyineleme derinliği için bir sınır vardır; sys.getrecursionlimit() bu sınırı döndürür. Çok uzun bir listede üçüncü sürüm bu sınıra takılıp RecursionError verebilir.

SürümEk bellek nerede oluşuyorUzay karmaşıklığı
Tek değişkenli döngüYalnızca birkaç sabit değişkenO(1)
Kopya üzerinde sıralamaGirdi boyunda yeni listeO(n)
Özyinelemeli (indeksli)Çağrı yığınındaki n çerçeveO(n)

Bu hesap asimptotiktir: büyümenin eğilimini gösterir, gerçek bellek ölçümü vermez. Gerçek kullanım yorumlayıcıya ve nesne yapısına göre değişir.

Uzay karmaşıklığı hesabında sık yapılan üç hata

Uzay karmaşıklığı hesabında sık yapılan üç hata

1. Her şeyi O(n) saymak

Kötü: "Listeyi dolaşıyor, o hâlde O(n)" demek. Dolaşmak zamanı belirler, belleği değil. Doğru: Döngü yalnızca birkaç değişken tutuyorsa ek bellek O(1)'dir. Önce "ne kadar yeni bellek ayrılıyor?" diye sor.

2. Dilim ve kopyaları gözden kaçırmak

Python'da liste dilimleme yeni bir liste nesnesi oluşturur; liste.copy() de öyle. Kodda tek bir köşeli parantez gibi görünür ama arkasında gerçek bir bellek ayrımı vardır.

def en_buyuk_dilimli(liste):
    if len(liste) == 1:
        return liste[0]
    return max(liste[0], en_buyuk_dilimli(liste[1:]))   # her çağrıda yeni liste

print(en_buyuk_dilimli([4, 9, 2, 7]))

Çıktı 9 olur. Ancak çağrı yığını açıkken her çerçeve kendi dilimini tutar. Uzunlukları n-1, n-2, ... 1 olan listeler aynı anda bellekte bulunur, yani toplam ek bellek O(n²) düzeyine çıkar. Önceki bölümdeki indeks parametreli sürüm aynı işi O(n) ile yapıyordu.

3. Girdiyi saymak ya da çağrı yığınını saymamak

Kötü: Fonksiyona verilen listeyi ek bellek olarak eklemek. Girdi zaten dışarıda oluşmuştur. Tersi de aynı derecede yanıltıcıdır: özyinelemeli bir fonksiyonda "yeni değişken yok, O(1)" demek. Her çağrı bir çerçeve açtığı için derinlik de belleğe yazılır.

Doğru: Girdiyi hesabın dışında tut, çağrı yığınını hesabın içine al.

Zaman ile bellek arasındaki takas

Bazı algoritmalar ek bellek harcayarak zaman kazanır, bazıları belleği az tutarak daha çok işlem yapar. Birinci gruba örnek, hesapladığı sonuçları bir sözlükte saklayan önbellek fikridir. Aynı alt problem tekrar sorulduğunda yeniden hesaplamak yerine sözlükten okursun. Bunun bedeli, sözlüğün girdiyle birlikte büyüyen ek belleğidir. İkinci grupta ise elindeki veriyi yerinde değiştirirsin. Ek bellek az kalır, ama aynı işi tekrar tekrar yapman gerekebilir.

Hangi tarafın daha değerli olduğu problemin kısıtlarına bağlıdır. Bellek darsa yerinde çalışan çözüm, süre darsa önbellekli çözüm daha uygun olabilir. İkisi de dar olabilir ve bu durumda ara bir yol aranır. Bu yüzden her durumda geçerli tek bir doğru seçim yoktur. Önemli olan, iki maliyeti de görüp kararı kısıtlara göre vermendir.

Üç soruluk karar çerçevesi: ek belleği nasıl bulursun?

Bir algoritmanın uzay karmaşıklığını çıkarırken kodu baştan sona şu üç soruyla tarayabilirsin:

  1. Ek bellek nerede oluşuyor? Yeni bir değişken, liste, tablo ya da fonksiyon çağrısının yığında açtığı çerçeve olabilir. Girdinin kendisini bu listeye yazma.
  2. Bu bellek girdiyle büyüyor mu? Girdi ne kadar büyürse büyüsün aynı kalıyorsa O(1) olur. Girdi kadar büyüyorsa O(n) olur. İki boyutlu bir tablo ya da eleman çiftleri kadar yer tutuyorsa O(n²) olur.
  3. Gizli bellek var mı? Dilimleme, kopyalama ve özyinelemenin çağrı yığında biriktirdiği çerçeveler kodda açıkça görünmeyebilir. Bu satırları ayrıca kontrol et.

Çerçeveyi en büyük değeri bulan üç sürüme geri uyguladığında her sürümün farklı bir soruda öne çıktığını görürsün. Tek değişkenle ilerleyen sürüm ilk iki soruda sabit kalır, ek belleği O(1) olarak çıkar. Listenin kopyasını çıkaran sürüm ikinci soruda ayrışır, çünkü kopya girdi kadar büyür. Özyinelemeli sürümün maliyeti üçüncü soruda ortaya çıkar. Kodda yeni bir liste görünmez, ama her çağrı yığında bir çerçeve açar. Böylece ilke cümlesine dönmüş olursun: ek belleğin büyümesini takip et, girdinin kendisini sayma.

Bu çerçeveyi farklı konularda yoklamak istersen ücretsiz bilgi testleri merkezindeki seviye testlerini ve algoritmik düşünme testini deneyebilirsin. Sorular, kavramları kodu okuyarak ve tahmin yürüterek uygulamana imkân verir.

Uzay karmaşıklığı, bir algoritmanın bellekte bıraktığı izi küçük bir soru listesine indirir. Bu listeyi her yeni koda uyguladıkça hesap hızlanır ve zaman karmaşıklığıyla birlikte düşünmek doğal bir alışkanlığa dönüşür.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin, yazılım geliştirici ve eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; İzmir Ekonomi Üniversitesi'ni bölüm birincisi ve yüksek şeref öğrencisi olarak tamamladı. Bugün yalnızca eğitim vermekle kalmıyor, sektörde aktif olarak yazılım projeleri geliştiriyor ve gerçek dünya deneyimini birebir derslerine taşıyor. Ezberden uzak, mühendislik zihniyetini merkeze alan sürdürülebilir öğrenme sistemleri tasarlayarak sorgulayan, üreten ve problem çözebilen yeni nesil yazılımcılar yetiştiriyor.

Sektörel Deneyim & Projeler

  • Ticarify Entegrasyon Yazılım logosu CEO Ticarify Entegrasyon YazılımPazaryerleri ve e-ticaret sitelerine otomatik e-fatura kesimi, sipariş ve kargo takibi hizmetleri sunan e-Dönüşüm platformunun API mimarisini ve yazılım ekibini yönetmektedir.
  • Benim Düğünüm logosu CEO Benim DüğünümDijital etkinlik ve anı paylaşım platformu.
  • Siberdizayn logosu Yazılım Ekibi Lideri SiberdizaynYüksek anlık oyuncu trafiğine sahip oyun kontrol panelleri ve sunucu altyapıları geliştiren yazılım ekibine liderlik etmektedir.
  • MEDYOGRAFYA 360° Dijital Çözümler logosu Dijital Strateji Lideri MEDYOGRAFYA 360° Dijital ÇözümlerŞirketlerin dijital çözümlerde uzun vadede nasıl ilerlemesi gerektiği ve dijital dönüşüm süreçlerinin yönetilmesine destek olmaktadır.
  • İzmir Ekonomi Üniversitesi logosu Danışma Kurulu Üyesi İzmir Ekonomi ÜniversitesiMezun olduğu üniversitesinde, Bilgisayar Programcılığı bölümünün akademik müfredatını güncel sektör ihtiyaçlarına göre şekillendirmek adına Danışma Kurulu'nda görev almaktadır.
WhatsApp Hemen Ara