Ders 06 / 23
Rastgeleleştirilmiş Algoritmalar
Rastgeleliğin iki ayrı yerde kullanılabileceği ve ikisinin ayrı ölçüldüğü: doğrulamasız örnekleme çoğunluğu olmayan 40 dizinin 40'ında yanlış değer döndürürken doğrulamalı örnekleme aynı dizilerde hiç yanılmıyor, ve on altı vezirlik tahtada rastgele sütun sırası 10.053 düğümlük belirlenimci aramayı ortalama 360 düğüme indiriyor.
İçindekiler
Buraya kadarki bütün yordamlar belirlenimciydi: aynı girdi her zaman aynı adımları ve aynı yanıtı veriyordu. Son tasarım yaklaşımı bu güvenceyi gevşetir. Yordamın içine bir rastgele seçim konur ve karşılığında bir şey umulur — ya arama alanının kötü bölgelerine takılmamak, ya da tam sayım yapmadan bir yanıta ulaşmak.
Gevşetilen güvencenin hangisi olduğu belirleyicidir ve iki seçenek vardır. Birincisinde yanıt kesin kalır, yalnız adım sayısı rastgeleleşir; yordam her zaman doğru yanıtı verir, ne kadar adımda vereceği belli değildir. İkincisinde adım sayısı sınırlı kalır, yanıt olasılıklı olur; yordam hızlıdır ama bazen yanılır. Bu ders ikisini de aynı ölçüyle sayar ve rastgeleliği bu kursun kuralına göre modeller: belirlenimci bir üreteçle.
- TY47. Rastgelelik belirlenimci bir üreteçle modellenir; standart kitaplığın rastgele sayı üreteci kullanılmaz. Her koşum tohumuyla yeniden üretilebilir.
- TY48. İki tohum kullanılır: 20260218 ve 20260219. Rastgele bir yordamın sonucu tek tohumla bildirilmez.
- TY49. Ölçülen ilk problem: 15 değerli bir dizide çoğunluk elemanı — uzunluğun yarısından çoğunu kaplayan değer. Yoksa doğru yanıt “yok”tur.
- TY50. Kâhin her değeri tek tek sayar; hiçbir örnekleme yapmaz.
- TY51. Dağarcık iki öbektir: çoğunluğu olan 40 dizi ve olmayan 40 dizi.
- TY52. Bir adım, bir örnek çekmek ya da bir değeri karşılaştırmaktır.
- TY53. Kalıbın ön koşulu: çoğunluk elemanının var olması. İkinci öbek bu ön koşulu bozar.
- TY54. Ölçülen ikinci problem: vezir yerleşiminde ilk çözümü bulmak; sütun sırası rastgeleleştirilir.
- TY55. Rastgele adım sayısı en az, ortalama ve en çok olarak bildirilir; yirmi koşum alınır ve tek bir sayı yeterli sayılmaz.
- TY56. Beklenen başarım adımın ortalamasıdır, süre değildir.
Rastgelelik Nasıl Modellenir
Bu kursta rastgelelik bir kütüphane çağrısı değil, tohumlu bir üreteçtir. Nedeni ölçmedir: bir rastgele yordamın ayrılan girdi sayısı ancak koşum yeniden üretilebilirse bildirilebilir.
# Rastgelelik belirlenimci uretecle modellenir; standart kitapligin ureteci kullanilmaz. UZUNLUK = 15 class Sayac: def __init__(self): self.adim = 0 def say(self, n=1): self.adim += n def uretec(tohum): d = tohum def sonraki(n): nonlocal d d = (d * 1103515245 + 12345) % 2147483648 return d % n return sonraki def cogunluklu(tohum, adet=40, cogunluk=True): """cogunluk=True -> bir deger uzunlugun yarisindan cok yer tutar.""" r = uretec(tohum) kume = [] for i in range(adet): baskin = r(6) pay = 8 + r(3) if cogunluk else 5 + r(3) # 8..10 ya da 5..7 dizi = [baskin] * pay + [6 + r(6) for _ in range(UZUNLUK - pay)] for j in range(len(dizi) - 1, 0, -1): # belirlenimci karistirma k = r(j + 1) dizi[j], dizi[k] = dizi[k], dizi[j] kume.append({"no": i + 1, "dizi": dizi}) return kume def kahin_cogunluk(dizi, s): """Her degeri tek tek sayar. Hakem budur.""" for i in range(len(dizi)): sayi = 0 for j in range(len(dizi)): s.say() if dizi[j] == dizi[i]: sayi += 1 if sayi * 2 > len(dizi): return dizi[i] return None VAR = cogunluklu(20260218) YOK = cogunluklu(20260218, cogunluk=False) s1, s2 = Sayac(), Sayac() v = sum(1 for k in VAR if kahin_cogunluk(k["dizi"], s1) is not None) y = sum(1 for k in YOK if kahin_cogunluk(k["dizi"], s2) is not None) print("cogunluklu dagarcik: 40 dizi x", UZUNLUK, "deger | cogunlugu olan:", v) print("cogunluksuz dagarcik:", 40, "dizi | cogunlugu olan:", y) print("kahin adimi:", s1.adim, "(cogunluklu) |", s2.adim, "(cogunluksuz)")
cogunluklu dagarcik: 40 dizi x 15 deger | cogunlugu olan: 40 cogunluksuz dagarcik: 40 dizi | cogunlugu olan: 0 kahin adimi: 780 (cogunluklu) | 9000 (cogunluksuz)
İki öbek de kurulmuş: birinde 40 dizinin 40’ında çoğunluk var, ötekinde hiçbirinde yok. Kâhinin adım sayısındaki fark da anlamlıdır — 780’e karşı 9000. Çoğunluk varsa kâhin genellikle ilk denediği değerde bulur ve durur; çoğunluk yoksa 15 değerin hepsini sonuna kadar saymak zorunda kalır, yani dizi başına tam 225 adım. Bir yordamın en pahalı hâli, yanıtın “yok” olduğu hâldir.
Olasılıklı Yanıt ve Doğrulamanın Rolü
Çoğunluk elemanı için rastgele bir yordam tek cümleyle kurulur: rastgele bir konum seç, oradaki değeri aday say. Çoğunluk varsa aday, yarıdan çok olasılıkla doğrudur. Aşağıdaki iki değişke bu fikri paylaşır ve tek bir noktada ayrılır — biri adayı doğrular, öteki doğrulamaz.
# Onceki blogun uzerine: Sayac, uretec, cogunluklu, kahin_cogunluk, VAR oradan gelir. def orneklemeli_cogunluk(dizi, deneme, r, s, dogrulama=True): """deneme kadar rastgele konum secer. dogrulama=True ise adayi sayarak sinar.""" if dogrulama: for _ in range(deneme): aday = dizi[r(len(dizi))] s.say() sayi = 0 for x in dizi: s.say() if x == aday: sayi += 1 if sayi * 2 > len(dizi): return aday return None ornek = [] # dogrulama yok: ornegin en siki for _ in range(deneme): s.say() ornek.append(dizi[r(len(dizi))]) en, en_sik = None, -1 for a in ornek: c = ornek.count(a) if c > en_sik: en, en_sik = a, c return en def olc(kume, deneme, tohum, dogrulama): r = uretec(tohum) ayrilan, sk, sh = 0, Sayac(), Sayac() for k in kume: a = orneklemeli_cogunluk(k["dizi"], deneme, r, sk, dogrulama) if a != kahin_cogunluk(k["dizi"], sh): ayrilan += 1 return {"ayrilan": ayrilan, "kalip_adim": sk.adim, "kahin_adim": sh.adim} print("deneme tohum dogrulamali ayrilan/adim dogrulamasiz ayrilan/adim") for deneme in (1, 3, 5): for tohum in (20260218, 20260219): a = olc(VAR, deneme, tohum, True) b = olc(VAR, deneme, tohum, False) print(f"{deneme:6d} {tohum} {a['ayrilan']:12d} / {a['kalip_adim']:5d}" f" {b['ayrilan']:16d} / {b['kalip_adim']:4d}")
deneme tohum dogrulamali ayrilan/adim dogrulamasiz ayrilan/adim
1 20260218 10 / 640 10 / 40
1 20260219 17 / 640 17 / 40
3 20260218 0 / 944 6 / 120
3 20260219 1 / 928 13 / 120
5 20260218 0 / 944 6 / 200
5 20260219 0 / 1056 7 / 200
Üç sayı yan yana. Kâhin 780 adım harcıyor. Doğrulamalı örnekleme beş denemede 944 ile 1056 adım harcıyor ve iki tohumda da 0 girdide ayrılıyor. Doğrulamasız örnekleme 200 adım harcıyor — kâhinden neredeyse dört kat az — ve iki tohumda 6 ve 7 girdide ayrılıyor.
Deneme sayısının etkisi iki sütunda farklı. Doğrulamalı sütunda ayrılan girdi 10 ve 17’den üç denemede 0 ile 1’e, beş denemede 0 ile 0’a iniyor: her yeni deneme, önceki denemelerin hepsinin başarısız olma olasılığını yarıdan çok azaltır. Doğrulamasız sütunda ise ayrılan girdi 10 ve 17’den 6 ve 7’ye iniyor ve orada kalıyor; örneklem büyüdükçe iyileşiyor ama sıfıra ulaşmıyor.
Aradaki asıl fark sayının büyüklüğü değil hatanın yönüdür. Doğrulamalı sürüm yanlış bir değer döndüremez; adayı saymadan kabul etmediği için ya doğru değeri verir ya da “yok” der. Bir denemede gördüğümüz 10 ayrılma, on girdide yanlış değer verdiği anlamına gelmez — on girdide bulamadığı anlamına gelir. Doğrulamasız sürüm ise gerçekten yanlış değer döndürür. Doğrulama adımı, hatayı iki yönlüden tek yönlüye çevirir ve bu, 200 adıma karşı 944 adımın satın aldığı şeydir.
Ön koşul Bozulduğunda
Bu ayrımın önemi, çoğunluk elemanı bulunmadığında görünür hâle gelir.
# Onceki bloklarin uzerine: olc, VAR ve YOK oradan gelir. print("on_kosul tohum dogrulamali ayrilan dogrulamasiz ayrilan") for ad, kume in (("saglaniyor", VAR), ("bozuk ", YOK)): for tohum in (20260218, 20260219): a = olc(kume, 5, tohum, True) b = olc(kume, 5, tohum, False) print(f"{ad} {tohum} {a['ayrilan']:16d} {b['ayrilan']:20d}")
on_kosul tohum dogrulamali ayrilan dogrulamasiz ayrilan saglaniyor 20260218 0 6 saglaniyor 20260219 0 7 bozuk 20260218 0 40 bozuk 20260219 0 40
Çoğunluğu olmayan 40 dizide doğrulamasız örnekleme 40 girdinin 40’ında kâhinden ayrılıyor; doğrulamalı örnekleme hiçbirinde ayrılmıyor. Sonuç iki tohumda da aynı, yani dağarcığa bağlı değil.
Nedeni açıktır ve kursun genel biçimine uyar. Doğrulamasız yordam, “çoğunluk vardır” varsayımını kodun içine gömmüştür: örneklemin en sık değerini döndürür ve o değerin gerçekten çoğunluk olup olmadığını hiç sormaz. Ön koşul bozulduğunda yavaşlamaz — yine 200 adımda biter — ama her seferinde var olmayan bir yanıtı bildirir. Doğrulama, ön koşulu kodun içinden çıkarıp çalışma anında sınanan bir koşula dönüştürür; bedeli dizi başına bir tam tarama, kazancı ön koşul bozulduğunda sessiz kalmamaktır.
Kesin Yanıt, Rastgele Adım
Rastgeleliğin ikinci kullanımı yanıta hiç dokunmaz. Önceki dersin budamalı vezir araması sütunları her zaman soldan sağa deniyordu; sıra rastgeleleştirildiğinde bulunan çözüm yine geçerlidir, değişen tek şey ona kaç düğümde ulaşıldığıdır.
# Onceki bloklarin uzerine: Sayac ve uretec oradan gelir. def vezir_ilk_cozum(n, r=None): """Ilk cozumde durur. Sutun sirasi rastgele ise adim degisir, yanit degismez.""" s = Sayac() bulunan = None def gez(satir, yer): nonlocal bulunan s.say() if satir == n: bulunan = tuple(yer) return sutunlar = list(range(n)) if r: for j in range(n - 1, 0, -1): k = r(j + 1) sutunlar[j], sutunlar[k] = sutunlar[k], sutunlar[j] for sutun in sutunlar: if any(sutun == y or abs(sutun - y) == satir - i for i, y in enumerate(yer)): continue yer.append(sutun) gez(satir + 1, yer) yer.pop() if bulunan is not None: return gez(0, []) return {"dugum": s.adim, "gecerli": bulunan is not None} print(" n belirlenimci tohum en az ortalama en cok gecerli cozum") for n in (8, 12, 16): b = vezir_ilk_cozum(n) for tohum in (20260218, 20260219): r = uretec(tohum) kosum = [vezir_ilk_cozum(n, r) for _ in range(20)] d = [k["dugum"] for k in kosum] print(f"{n:2d} {b['dugum']:13d} {tohum} {min(d):6d} {sum(d) / 20:9.1f}" f" {max(d):7d} {sum(k['gecerli'] for k in kosum):9d} / 20")
n belirlenimci tohum en az ortalama en cok gecerli cozum 8 114 20260218 9 35.7 89 20 / 20 8 114 20260219 16 35.0 101 20 / 20 12 262 20260218 15 98.7 491 20 / 20 12 262 20260219 17 90.1 356 20 / 20 16 10053 20260218 19 359.6 3379 20 / 20 16 10053 20260219 19 355.8 1237 20 / 20
Son sütun bütün satırlarda 20 / 20: kırk koşumun hepsi geçerli bir yerleşim buluyor. Yanıt rastgele değildir; rastgele olan yalnız adımdır. On altı vezirlik tahtada belirlenimci sıra 10.053 düğüm geziyor, rastgele sıra ortalama 359,6 ve 355,8 düğüm — yaklaşık 28 kat az. İki tohumun ortalamaları birbirine çok yakın, yani beklenen başarım dağarcığa bağlı değil.
Uç değerler bu tabloyu tamamlar ve tek bir ortalamanın neden yeterli olmadığını gösterir. En az 19 düğüm, en çok 3379; aradaki fark 178 kat ve iki tohumun en çok değerleri de birbirinden uzak, 3379 ile 1237. Rastgele bir yordamın başarımı tek bir sayıyla bildirilemez; ortalama bir güvence değil, bir beklentidir. Belirlenimci sıranın 10.053 düğümü ise bir kaza değildir: sabit sıra, çözüm içermeyen aynı bölgeyi her koşumda aynı derinlikte gezer ve kötü bir sıranın bedeli hiçbir koşumda azalmaz. Rastgeleleştirmenin kazandırdığı şey, aynı kötü sırayı iki kez denememektir.
Uzun Kuyruğu Kesmek
3379 düğümlük uç değer, rastgele yordamın asıl sorunudur: koşumların çoğu kısa, birkaçı çok uzun. Bu kuyruk bir bütçeyle kesilebilir — arama belirli bir düğüm sayısını aşarsa bırakılır ve yeni bir rastgele sırayla baştan başlanır.
# Onceki bloklarin uzerine: Sayac, uretec ve vezir_ilk_cozum oradan gelir. def butceli_arama(n, r, butce): """Butce asilirsa vazgecer. Vazgecmek yanlis yanit degildir - yanit yoktur.""" s = Sayac() bulunan = None def gez(satir, yer): nonlocal bulunan s.say() if s.adim > butce or bulunan is not None: return if satir == n: bulunan = tuple(yer) return sutunlar = list(range(n)) for j in range(n - 1, 0, -1): k = r(j + 1) sutunlar[j], sutunlar[k] = sutunlar[k], sutunlar[j] for sutun in sutunlar: if any(sutun == y or abs(sutun - y) == satir - i for i, y in enumerate(yer)): continue yer.append(sutun) gez(satir + 1, yer) yer.pop() if bulunan is not None or s.adim > butce: return gez(0, []) return s.adim, bulunan print("butce tohum yeniden baslatma toplam dugum en cok gecerli") for butce in (100, 400, 100000): for tohum in (20260218, 20260219): r = uretec(tohum) toplam, deneme, en_cok, gecerli = 0, 0, 0, 0 for _ in range(20): kosum = 0 while True: adim, sonuc = butceli_arama(16, r, butce) kosum += adim deneme += 1 if sonuc is not None: gecerli += 1 break toplam += kosum en_cok = max(en_cok, kosum) print(f"{butce:6d} {tohum} {deneme:16d} {toplam / 20:13.1f} {en_cok:7d}" f" {gecerli:8d} / 20")
butce tohum yeniden baslatma toplam dugum en cok gecerli 100 20260218 54 225.8 940 20 / 20 100 20260219 57 240.8 1229 20 / 20 400 20260218 27 228.8 1731 20 / 20 400 20260219 29 276.1 835 20 / 20 100000 20260218 20 359.6 3379 20 / 20 100000 20260219 20 355.8 1237 20 / 20
Yüz düğümlük bütçeyle yirmi çözüm için 54 ve 57 deneme yapılıyor, yani çözüm başına 2,70 ile 2,85 arası baştan başlama. Buna karşılık toplam düğüm ortalaması 359,6’dan 225,8’e, en kötü koşum 3379’dan 940’a iniyor. Son satırlar bütçesiz koşumu tekrar veriyor ve farkı doğruluyor.
Yanıt yine bozulmuyor: son sütun altı satırda da 20 / 20. Vazgeçmek yanlış yanıt vermek değildir — bütçe aşıldığında yordam bir şey söylemez, ve söylemediği için kâhinden ayrılmaz. Kursun ilk dersindeki ayrım burada son biçimini alır: bilmediğini bildiren bir yordam ölçülebilir, bilmediğini uyduran bir yordam ölçülemez.
Özet
- Rastgeleleştirme iki ayrı güvenceyi gevşetebilir: yanıt kesin kalıp adım rastgeleleşebilir, ya da adım sınırlı kalıp yanıt olasılıklı olabilir. İkisi ayrı ölçülür.
- Çoğunluk elemanında kâhin 780 adım harcıyor; doğrulamalı örnekleme beş denemede 944 ile 1056 adım ve 0 ayrılan girdi, doğrulamasız örnekleme 200 adım ve 6 ile 7 ayrılan girdi veriyor.
- Doğrulama adımı hatayı tek yönlü yapar: doğrulamalı sürüm yanlış değer döndüremez, yalnız “yok” diyebilir; doğrulamasız sürüm gerçekten yanlış değer döndürür.
- Ön koşul bozulduğunda doğrulamasız örnekleme 40 girdinin 40’ında ayrılıyor, doğrulamalı örnekleme hiçbirinde; sonuç iki tohumda da aynı.
- On altı vezirlik tahtada belirlenimci sıra 10.053 düğüm, rastgele sıra ortalama 359,6 ve 355,8 düğüm geziyor ve kırk koşumun kırkı da geçerli çözüm buluyor; uç değerler 19 ile 3379 arasında, yani ortalama bir güvence değil beklentidir.
- Yüz düğümlük bütçeyle yeniden başlatma ortalamayı 225,8’e, en kötü koşumu 3379’dan 940’a indiriyor ve yanıt yine 20 / 20 geçerli kalıyor.
Sonraki Adım
Bu konu altı tasarım yaklaşımını aynı ölçüyle geçirdi ve hepsinde aynı şey çıktı: bir kalıbın değeri kazandırdığı adımda değil, ön koşulu bozulduğunda verdiği yanlış yanıtta görünüyor. Sonraki konu bu ölçüyü problem kalıplarına taşır. Orada kalıplar daha dar ve daha tanınabilirdir — sıralı dizide karşılıklı tarama, bitişik alt dizide artımlı hesap, ızgarada bağlı bileşen arama — ve her birinin ön koşulu tek bir cümleyle yazılabilir. İlk ders sıralı girdi isteyen iki işaretçi kalıbıyla açılır ve sorusu buradakiyle aynıdır: sıralama bozulduğunda kaç girdide yanlış yanıt çıkıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.