Laz tamirci

Evde bir sürü jaluzi var ama bir tanesi sürekli bozuluyordu. Motorlu bir şey de değil. Bildiğimiz mekanik, elle çekilen bir halatı olan basit bir şey. Nedense hep bu bozuluyor ama. Sonunda kapının üzerindeki bölmeyi açtım ve sorun nerede diye bakayım dedim. Halat normalde sarılması gereken diskten çıkmış ve sıkışmış, böylece bütün jaluzinin hareketini de engellemiş.

Tabii ki düğümü çözmek kolay diye düşündüm ve sıkışan halatı çekiştirmeye başladım. Halatın aksın etrafında defalarca dolanmış olması işimi iyice zorlaştırıyordu. Ayrıca bütün işi tek elle yapmam gerekiyordu çünkü olay yerine sadece bir elim sığıyordu.

İlk gece aksa dolanmış düğüm sayısını dörde indirebildim. Ertesi gün, bu sefer kalanı hallederim diyordum ki düğüm sayısının yediye çıktığını gördüm. Anlaşılır gibi değildi. Öncelikle orada bir düğüm oluşmasına bile imkan veremezken düğümlerin sayısının durup dururken artması hiç de hayra alamet değildi. Ertesi gün devam ederim diye işi orada bıraktım.

Ertesi gün çocukların da yardımıyla düğüm sayısını biraz da azalttım ama halatın pozisyonu gördüğüm kadarıyla imkansız bir şekildeydi. Yanı normal sarılmış bir durumdan bu konuma nasıl gelmiş olabileceğini çözemiyordum. Bu sırada yanlı bir hareket sonucunda jaluzinin lamellerini sola doğru kaydırabildiğimi gördüm. Bu da çalışmak için daha çok alan demekti ve halatı biraz daha bollaştırdım, şimdi halatın oluşturduğu halkaları birbirlerinin içinden geçirebiliyordum ama hala bütün düğümü çözmenin yolunu bulamamıştım. Bu sırada jaluzinin aksını yine yanlışlıkla hareket ettirirken olduğu yerden çıktığını da farkettim. Bu sayede halatı dolandığı yerden komple çıkarabildim fakat aksı yerine takarken ne oldu bilmiyorum, diske giren kısım yerinden çıktı. Çıktığıyla da kalmadı kayboldu. Evet, nerede olduğunu bilmiyorum. Bir ihtimal duvardaki bir delikten aşağıya düştü.

Bütün bu yazıdan da fark edileceği üzere jaluzi ve parçaları hakkında hiçbir bilgim yok. Bu nedenle düşen parçanın adını da bilmiyorum. Bilsem internetten sipariş verebilirdim. Şimdi jaluzinin son halinin fotoğraflarını çekip dükkanları gezmekten başka pek şansım kalmadı. Parçalar pek pahalı gözükmüyor ama anladığım kadarıyla standard da değiller. En iyisi pandemi yüzünden sokağa çıkma yasakları başlamadan hemen yarın gidip şansımı deneyeyim.

Bad Character algoritması

Bu yazı ve algoritma dizisinde de metin arama algoritmalarından Boyer-Moore algoritmasını öğrenmeye çalışacağım. Diğer bazı metin arama algoritmalarında olduğu gibi bu algortimada da aranan metin her adımda tek adım kaydırılmıyor, aksine aranan metnin özelliğine göre kaydırma miktarları optimize ediliyor. Boyer-Moore algoritmasının şimdiye kadar öğrendiğim algoritmalara göre iki farklı özelliği var. Birincisi, aranan metin ve ana metin karşılaştırması sağdan sola doğru yapılıyor. İkincisi de kaydırma miktarını bulmak için iki değişik alt algoritma kullanılıyor. Bu yazıda bu iki algoritmadan biri olan Bad Character algoritmasını anlamaya çalışacağım.

Bu algoritma için aşağıdaki animasyonu programlamaya çalıştım.

Bad Character Rule

Bu animasyon başlatıldığında henüz hazırlanmamış bir tablo ve tablonun üzerinde seçilmiş bir aranan metin gösteriliyor. Tabloda normalde bir metinde olabilecek bütün karakterler olmalı (küçük harfler, büyük harfler, rakamlar vb.) ama sadece fikri gösterebilmek için tabloyu küçük tuttum. Animasyonda ana metni kullanmadım çünkü bu algoritma için ana metin değil aranan metne herhangi bir konumda uymayan bir karakter önemli. Bunu da aranan metnin üzerinde hesaplanacak konumda gösterilen bir karakterle göstermeye çalıştım.

Animasyonda aranan metin olarak

 NNAAMAN

dizisini kullandım. Her tablo seçilen aranan metne göre ayrı oluşturulması gerekiyor. Tabloyu şöyle düşünmeye çalıştım. Aranan metinde ana metni sağdan sola doğru karşılaştırdığımızda ana metindeki ilk uyumsuz karakteri en soldaki sütundan buluyoruz. Ardından bu uyumsuzluğun aranan metindeki hangi pozisyonda olduğunu da birinci satırdan okuyoruz. Algoritma bittiğinde bu tablo bize o karakter ve o konum için aranan metni sağa doğru kaç karakter kaydırabileceğimizi söyleyecek.

Bu tabloyu hazırlamak aslında çok kolay ama bu şekliyle tablo oldukça fazla yer tutmakta, çünkü olası her karakter için tabloda yer harcanıyor ama bu ilk versiyon için bu basit yöntemi anlamak yeterliydi benim için. Literatürde bu sorunla ilgili çeşitli çözümler var ve ileride onlara da bakabilirim.

Tablodan sırayla her satırdaki harf için olası her pozisyonu alıyorum ve bu harfi aranan metnin üzerinde o pozisyonda gösteriyorum. Eğer seçtiğim harfler aranan metindeki harf aynı ise tabloda bu noktaya “-” işareti koyuyorum, çünkü bu algoritma sadece uyumsuzluk varken kullanılıyor. Eğer uyumsuzluk varsa aranan metinde sola doğru harfleri tek tek kontrol ediyorum. Seçilen harfle aynı harfi ilk bulduğum konuma kadar aranan metni sağa doğru kaydırabilirim, çünkü aradaki her durumda bir uyumsuzluğun çıkacağı kesin. Eğer bir harfi uyumsuzluğun çıktığı konumdan sonra bir daha bulamazsam da aranan metni uyumsuzluktan bir sonraki konuma kadar kaydırabilirim. Bu şekilde her karakter ve konum için aranan metni ne kadar kaydırmam gerektiğini tabloya işliyorum. Bu şekilde bu basit algoritma da tabloyu oluşturmuş oluyor.

Evet, hayır, bilmiyorum

Ahmet’le Betül teneffüste yine sayı bulmaca oynamışlar. Biri aklında birle yüz arasında bir sayı tutuyor ve diğeri cevabı sadece evet ya da hayır olacak sorular sorarak o sayıyı bulmaya çalışıyor. Ders zili çaldığında sınıfa dönerken Betül Ahmet’e “bu oyun çok basit artık. Biraz değişiklik yapalım en iyisi” diyor. “Mesela, sadece cevabı evet ya da hayır olan değil, cevabı bilmiyorum olan sorular da soralım” diye ekliyor. Ahmet “Hmmm, ilginç fikir ama aklıma nasıl sorular olacağı gelmiyor” diyor. Betül de “Ben de şu an bilmiyorum ama teneffüse kadar bir ders zamanımız var, belki o zamana kadar buluruz” diyor.

Birle yüz arasında tutulan bir sayıyı minimum denemede bulmak için cevabı evet, hayır ya da bilmiyorum olacak soruları bulun.

Çözüm

Bilgisayarlar satranç oynar mı?

Bu soru nereden aklıma geldi ki yine? Heralde okuduğum psikoloji kitabındaki akıllı Hans adlı atın gerçekte aritmetik işlem yapmadan insanları aritmetik işlem yaptığına inandırması hikayesi yüzünden oldu. Bu gerçek hikayede at sorulan işlemin sonucunu soruyu soran kişinin farkında olmadan verdiği vücut dili işaretlerinden buluyordu. Bu yöntemi kullandığı bilinmediği sürece gerçekten işlem yaptığı düşünülüyordu ve bu yüzden de akıllı lakabı takılmıştı. İşin sırrı çözüldükten sonra ise o kadar da akıllı değilmiş denmiştir heralde ama atın kullandığı yöntem bence çok daha ileri bir zekanın göstergesi olabilir.

Uzun bir süre, bilgisayar programlarının emekleme döneminde bilgisayarların satranç oynamadığı iddia ediliyordu. Programların yaptığı tek şey basit algoritmalar yardımıyla olası pozisyonları değerlendirmek ve en yüksek değerdeki pozisyona götüren hamleyi seçmekti. Bu değerlendirme hesabı da kolaydı. Figürlerin belli değerleri olurdu, kontrol edilen kareler, hareketlilik filan gibi faktörler de hesaba katılırdı. İnsanlar bu kadar fazla hesap yapmadan, daha stratejik oynadıklarından mı yoksa oynarken hissettikleri duygulardan ötürü mü bilmem ama programların satranç oynamadığını düşünürdü. Onların yaptığı matematiksel, algoritmik bir hareketti ve satranç bu şekilde açıklanamaz ya da açıklanmamalı gibi düşünülüyordu. Hala yaptığı hamlenin nedenini insanların anlayabileceği şekilde açıklayabilen bir program yoktur heralde.

Zamanla programlar gelişti. İnsanların kullandığı stratejilerin bazıları yavaş yavaş eklendi. Yaratıcılık dediğimiz sezgiye dayalı şeyler eklenemedi tabii ki. Sanırım bunun ne olduğunu bilinmediğinden nasıl programlanacağı da çözülemedi. Bunun yerine açılış ansiklopedisi ve oyun sonu veri tabanları eklendi ama. Böylece programlar oyun ortası kısmı hariç her yeri neredeyse hatasız oynayabilmeye başladı. Bir süre sonra bilgisayarların yeterince hızlanmasıyla insanın bu hesap gücü karşısında pek bir şansı kalmamıştı. Artık hedef yenilmemek olmuştu ki o da Kasparov’un Deep Blue’ya karşı kaybetmesiyle geçmişte kalmış oldu.

Programlarda yapılan son devrim de yapay zeka oldu. Artık bilgisayarlar yapay zekanın ihtiyacı olan performansı sunabilecek kadar hızlılar. Yapay zeka ile artık programlarla insanlar arasındaki oyun gücü farkı çok yükseldi. Hatta en son AlphaZero adlı program sadece kurallarla başlayıp kendi kendine oynayarak sadece bir gün içinde insanlardan güçlü, üç gün içinde de dünya şampiyonu programları yenebilecek seviyeye gelmeyi başardı. Bu bence ilginç bir adım oldu. Artık bilgisayar insanların programladığı stratejileri kullanmadan, kendi bulduğu stratejilerle oynuyordu ve anlaşılan bizim bulduklarımızdan çok daha güçlü stratejileri bulabiliyor.

Artık yapay zekalı programların bu hamleleri nasıl hesapladığını genelde bilmiyoruz. Aslında teknik olarak biliyoruz da heralde analizi anlayabileceğimiz hale getirmek pek mümkün değil. Oyunu sadece kurallardan öğrenebiliyorlar ve insanların programlara kendilerinden bir şey katmasına da gerek yok. Bu durumda bence en geç şimdi programların satranç oynadıklarını rahatlıkla söyleyebiliriz. Belki oynarken bir şey hissetmiyorlar, ya da bir hamleyi neden yaptıklarını açıklayamıyorlar ama heralde bunları bir tür iletişim eksikliği olarak yorumlayabiliriz.

Peki başta sorduğum soruyu daha da genişletebilir miyiz? Örneğin en basitinden başlayayım. İnsanlar bilgisayarlara göre satranç oynuyor mu? Bu soru bilgisayarların umrunda değildir diyip geçebiliriz ama. Peki ya bilimler? Çok ileri bir uzaylı uygarlığı karşısında yaptığımız şeyler bilgisayarların kırk yıl önceki satranç oynama seviyesi gibi ilkel olamaz mı? O zaman bu uygarlık bizim için aslında fizik yapmıyorlar diyebilir mi? Deseler bile biz fizik yapmadığımızı düşünmeli miyiz?

Çekiç ve çivi

“Eğer sahip olduğun tek aletin çekiç ise her problem çivi gibi görünür.” Twain? Maslow? Kaplan? Kim demişse demiş. Doğru da demiş ama. Yazılım dünyasında hemen hemen her gün bu sözün gerçekleşmesine şahit oluyorum.

Ürettiğimiz makineyi ayarlamak için binlerce parametremiz var. Evet, tahmin ettiğiniz gibi bizim çekicimiz de parametreler. Geçen gün iki eleman yüksek gerilim kaynağı ile haberleşme protokolü üzerine tartışıyordu. Yeni protokol eski protokole de benziyor biraz. En azından ikisi de binary protokol. Yani insan tarafından okunmasına gerek olmayan, sadece makine için tasarlanmış bir protokol. Tabii içerikte de birkaç fark var. Neyse, elemanların biri “bu yeni bir alet, üretici farklı, kullanılan protokol de farklı. O zaman yeni bir protokol tanımlayıp diğerinden ayrı programlaman lazım” derken diğeri de “ama o zaman bir sürü kod yazmam lazım. Protokoller arasındaki farklar da çok değil, üç parametre ile bu işi halledebilirim” tezini savunuyordu. Bunun üzerine ilk eleman “öyle yaparsan ilk protokolü de değiştirmiş olursun, bu durumda eski güç kaynaklarını yine test etmemiz gerekir” şeklinde karşı saldırıya geçti. İkinci eleman da “sadece üç yerde değişiklik olacak. Onların testi de kolayca yapılabilir” diye karşılık verdi. Başka işlerim olduğu için bu tartışmaya pek dalmadım ama kendimi birinci elemanın düşüncesine daha yakın buluyorum. Schrödinger’in programları gibi programları sevmiyorum. Bir bakışta programın ne yaptığını anlamak isterim.

Bu hafta yine buna benzer bir tartışma oldu. Bu sefer işin ucu bana da dokunuyordu. Benim yazdığım modül de yapılacak değişikliklerden etkilenecekti. Kontrol birimi her gün belli bir saatte bana belli bir mesaj gönderece ve ben de bu mesaja göre belli bir işlem yapacağım. Bu işlemin sonucunu da kontrol birimine geri göndereceğim. Yani çok basit ve temel bir işlem. Daha önce de defalarca yapmışız. Tabii ki sorun şu: Harika bir proje yönetimi ve beklenmeyen aksilikler yüzünden bu işi yapmak için bir gün zamanımız kalmış. Problem, aynı anda üç değişik modülde değişiklik yapmak yerine bunu tek modülde değişiklik yaparak nasıl yaparız şekline indirgenmiş. Kontrol biriminin bana göndermesi gereken mesaja benzer bir mesajı zaten daha önce programlamış olduğumuz için bütün işi kontrol birimine yıktık. Bir süre sonra kontrol birimi programcıları bu mesajın aynı işi yapamayacağını buldu. Zaman da azalmıştı. Bunun üzerine yeni bir mesaj tanımlamak yerine (o zaman üç modülde değişiklik gerekecek) bir parametre kullanmayı (sadece bir modül etkilenecek) teklif ettiler. Görünüşe göre istenen çözüm de böyle olmalıydı ama parametrenin sorunu şuydu: Bu parametrenin anlamı ve yaptığı iş normal bir kullanıcı ya da servis elemanı için hiç de kolay anlaşılır bir şey değildi. Dolayısıyla ileride bir sürü gereksiz telefon görüşmesi gerekecekti. Sonuçta parametrenin yaptığı iş şu olacak: İlk modülün gönderdiği mesajın aslında başka bir mesaj olduğunu söyleyecek ve kontrol birimi de bu durumda bu mesajı eski mesajımıza uygun hale getirip (içindeki bir miktar bilgiyi atıp) gönderecek. İşin komik tarafı projedeki asıl plan işin sadece ilk modülde yapılmasıyken birden iş ikinci modüle, kontrol birimine, kaymış oldu. Hiç kimsenin hoşuna gitmedi ama başka türlü pazartesiye yetişmesi de mümkün değildi heralde. Bu arada, ben cuma günü işten çıktığımda kontrol birimi bu işi hala bitirememişti.

Parametrelerle bir çok şey yapılabilir, bir sürü problem çözülebilir ama burada tasarruf ettiğimiz zaman bize dokümantasyon, hata arama, elemanların eğitimi, kullanıcıların hayatını zorlaştırma şeklinde geri dönebilir. Sonuçta parametreler de bir tür programlamadır ve bu tür çekiçler kullanarak işi böylece bizden sonrakilere, yani hem kullanıcılara hem de bu sorunları düzeltecek programcılara bırakmış oluyoruz.

Üçgenin açıortayları (Geogebra)

Bu animasyonda geogebrada bir ABC üçgeni tanımladım ve her köşedeki iç açıortayları çizdirdim. Bu açıortaylar bir D noktasında kesiştiler. Üçgenin köşelerini hareket ettirip yeni üçgenler oluşturduğumda da açılar ve açıortayla değişse de her seferinde üçü de aynı noktada kesişmeye devam etti. İki doğrunun bir noktada kesişmesi çok normal ama belli bir özellikteki üç doğrunun her zaman aynı noktada kesişmesi bence ilginç bir durum. Bu noktanın başka bir özelliği var mı acaba?

Animasyon

Evrimimiz

Genelde hep diğer canlıların ya da virüslerin mutasyonlarını kafamıza takıyoruz ama acaba bizim mutasyonlarımız neler yapıyor? Acaba ne yöne doğru evrimleştiğimizi düşünüyor muyuz?

Belki şikayet edip durduğumuz şeylerin bir kısmı evrim yolunda atmaya başladığımız bazı adımlardır. Örneğin yeni nesillerin çok dikkatsiz olması, bir şeye konsantre olamaması. Ya son yıllarda aşırı miktarda artan bilgiye karşı ortaya çıkan bazı mutasyonlar gelecek nesilleri aynı anda birden çok şey yapabilmeye yönlendiriyorsa? Mutasyonlar küçük adımlar olduğundan heralde hem bunları tespit etmesi zor oluyordur hem de yönünü anlaması zordur. Ne olursa olsun ilk anda normal karşılanmalarını da beklemiyorum açıkçası. Eğer beynimiz gelişmeye başladığında, mesela konuşmaya başladığımızda doktorlarımız, psikologlarımız olsaydı (daha ilkel versiyonları tabii ki) büyük ihtimalle kelimeleri kullanmaya çalışan ilk insanları hasta ilan ederlerdi.

Aklıma takılan başka bir soru da acaba insanlar arasında başka türler de oluşuyor mudur? Evrim açısından oluşmaması için bir neden yok tabii ki ama bilim insanları dışında böyle bir şeyi beklemiyoruzdur.

Lisede ya da orta okulda türleri öğrenirken aynı türde olma şartlarından birisi üreyebilmekti. Yıllar önce bir çift tanımıştım. Bu ikisi birbirlerinden çocuk sahibi olamıyorlardı. İşin ilginç tarafı ise ikisi de büyük ihtimalle herhangi başka birinden çocuk sahibi olabileceklerdi. Şimdi bu ikisi bu tanıma göre ayri türlerden miydi? Yoksa sadece bir hastalık mıydı? Eğer hastalıksa genetik bir hastalık heralde ve bu durumda ayrı türler olup olmadıklarını anlama kıstasımız nedir acaba?

Bu soruların cevabını çok merak etmedim. Daha doğrusu cevaplamaya çalışmadım çünkü bunlar bence ayrımcı, sınıflayıcı sorular. Bilim insanları tabii ki bu sorularla uğraşacaklar ama ben sosyal alanlarda bu sorular yerine daha kapsayıcı, daha türlerden bağımsız sorularla uğraşılması taraftarıyım. Durup dururken hayvanlara yaptığımız eziyeti bize yakın yeni türlere yapmaya gerek yok. Ya da yeni türlerin bize yapmasına!

Şapka yerine sayılar (Çözüm)

Soru

Henüz bir planım olmadığı için yine sorunun daha basit bir versiyonuyla başlayayım. Diyelim ki iki tutuklu var ve alınlarına 0 ya da 1 yazılsın. İki tutukluyu da bir önceki gece 0 ve 1 diye numaralayalım. Yani oyun boyunca ikisinin de rolü belli. Şimdi ertesi gün oyun tarafından kendilerine rastgele numaralar verilecek. Bu durumda aşağıdaki tabloya göre 4 değişik durum olacak:

Birinci tutuklunun alnındaki sayıİkinci tutuklunun alnındaki sayı
00
01
10
11

Tutuklular sadece gördükleri sayılara göre bir karar verirse kurtulmaları için yeterli olmaz. Bunu görmek için basit bir deneme yapalım. Diyelim ki herkes gördüğü sayının tersini söylesin, yani 0 gören 1 desin, 1 gören de 0 desin. O zaman şöyle bir tablo elde ederiz:

1. tutuklunun alnındaki sayı2. tutuklunun alnındaki sayı1. tutuklunun tahmini2. tutuklunun tahmini
0011
0101
1010
1100
Tutukluların gördükleri sayıların tersini söylediği durumdaki sonuçlar

Görüldüğü gibi iki tutuklunun aldında aynı sayılar yazıyorken ikisi de yanlış cevap veriyor, farklı sayılar varken ikisi de doğru cevap veriyor. Peki ikisi de gördüğü sayıyı söylese durum değişir mi?

1. tutuklunun alnındaki sayı2. tutuklunun alnındaki sayı1. tutuklunun tahmini2. tutuklunun tahmini
0000
0110
1001
1111
Tutukluların gördükleri sayıları söylediği durumdaki sonuçlar

Bu durumda da eğer alınlarındaki sayılar aynı ise ikisi de doğru cevap veriyor, farklı ise ikisi de yanlış cevap veriyor. Soruyu çözmek için aynı anda ikisinin de doğru cevap vermesine gerek yok ama, sadece biri doğru cevap verse yeterli. Bunu elde etmek için bir gece önce kendilerine verilen sayıları da kullanabilirler belki.

Bunu yapmanın bir sürü yolu var. En basitinden şöyle düşünebiliriz. İki tutuklu da farklı kurallara göre seçim yapabilir. Örneğin birinci tutuklu her zaman gördüğü sayıyı söylesin, ikinci tutuklu da gördüğü sayının tersini söylesin. Bakalım bu sefer nasıl bir tablo çıkıyor?

1. tutuklunun alnındaki sayı2. tutuklunun alnındaki sayı1. tutuklunun tahmini2. tutuklunun tahmini
0001
0111
1000
1110
Birinci tutuklunun gördüğü sayıyı söylerken, ikinci tutuklunun gördüğü sayının tersini söylediği durumdaki sonuçlar

Bu stratejiye göre her durumda sadece bir tutuklu doğru cevabı veriyor ama her durum için bir doğru cevap olduğundan her durumda ikisi de kurtuluyor. Demek ki bu basit versiyon için bir çözüm var. Yalnız şimdi de 10 tutuklunun olduğu versiyon için çok kompleks bir çözüm bulmamız gerektiği gibi bir sonuçla karşı karşıyayız. Peki bu çözümde biraz daha durup stratejimizi daha kolay bir hale getirebilir miyiz?

Önce daha kolaydan ne kastettiğimi anlatayım. Yani belki basit matematiksel bir ifade bulabiliriz ve daha sonra bu ifadeyi daha fazla tutuklu ile deneyerek test edebiliriz. Fakat sonunda gerçek testi bir ispat verecektir bu durumda. Yine de matematiksel bir ispattan faydalanmak için matematiksel bir ifade daha kolaylaştırıcı olacaktır.

Örneğin şöyle bir model tasarlayabiliriz. Her tutuklu gördüğü sayıdan plan yapma aşamasında kendisine verilen sayıyı çıkarabilir. Tabii ki çıkan sonuç her zaman alınlarındaki olası sayılardan biri olmayabilir. Birinci tutuklunun alnında 0 yazıyorsa ikinci tutuklu 0 – 1 işlemini yapıp -1 sonucunu bulacaktır. Demek ki bu sonucu da 0 ve 1 sayılarına dönüştürmemiz lazım. En kolay yol bu soncu modulo 2 sisteminde yazmak olabilir. Şimdi tabloyu bu şekilde tekrar yazayım.

1. tutuklunun alnındaki sayı2. tutuklunun alnındaki sayı1. tutuklunun tahmini2. tutuklunun tahmini
00\(0 – 0 \equiv {0} \mod{2}\)\(0 – 1 \equiv {1} \mod {2}\)
01\(1 – 0 \equiv {1} \mod {2}\)\(0 – 1 \equiv {1} \mod {2}\)
10\(0 – 0 \equiv {0} \mod {2}\)\(1 – 1 \equiv {0} \mod {2}\)
11\(1 – 0 \equiv {1} \mod {2}\)\(1 – 1 \equiv {0} \mod {2}\)
Yukarıdaki yöntemi modüler aritmetik yardımıyla tekrar yazdık

Bu şekilde ifade ettiğimizde de yukarıdaki çözümle aynı sonucu bulabildik. Şimdi önümüzde iki imkan var. Ya bu yöntemi diğer tutuklu sayılarıyla test edeceğiz ya da genel bir ispat arayacağız.

1. tutuklunun sayısı2. tutuklunun sayısı3. tutuklunun sayısı1.tutuklunun tahmini2. tutuklunun tahmini3. tutuklunun tahmini
000\(0 + 0 – 0 \equiv {0} \mod {3}\)\(0 + 0 – 1 \equiv {2} \mod {3}\)\(0 + 0 – 2 \equiv {1} \mod {3}\)
001\(0 + 1 – 0 \equiv {1} \mod {3}\)\(0 + 1 – 1 \equiv {0} \mod {3}\)\(0 + 0 – 2 \equiv {1} \mod {3}\)
002\(0 + 2 – 0 \equiv {2} \mod {3}\)\(0 + 2 – 1 \equiv {1} \mod {3}\)\(0 + 0 – 2 \equiv {1} \mod {3}\)
Üç tutuklu için strateji test tablosu

Bu tamamlanmamış tablodaki testten kolayca görüyoruz ki bu strateji doğru çalışmıyormuş. Peki bunun yerine nasıl bir kural koyabiliriz? Mesela her tutuklu toplamı kendine atanan sayıya eşitleyebilir. Yani birinci tutuklu tüm toplamı 0 yapmaya çalışırken, ikinci tutuklu da tüm toplamı 1 yapmaya çalışabilir. Tabii ki modulo 2’ye göre. Bunu da bir deneyelim.

1. tutuklunun sayısı2. tutuklunun sayısı1. tutuklunun tahmini2. tutuklunun tahmini
00\(0 – 0 \equiv {0} \mod {2}\)\(1 – 0 \equiv {1} \mod {2}\)
01\(0 – 1 \equiv {1} \mod {2}\)\(1 – 0 \equiv {1} \mod {2}\)
10\(0 – 0 \equiv {0} \mod {2}\)\(1 – 1 \equiv {0} \mod {2}\)
11\(0 – 1 \equiv {1} \mod {2}\)\(1 – 1 \equiv {0} \mod {2}\)
Her tutuklunun tüm toplamı kendine atanan sayıya eşitlemeye çalıştığı stratejini tablosu

İki tutuklu için işe yaradı bu strateji. Bakalım üç tutuklu için daha iyi bir sonuç verecek mi?

1. tutuklunun sayısı2. tutuklunun sayısı3. tutuklunun sayısı1. tutuklunun tahmini2. tutuklunun tahmini3. tutuklunun tahmini
000\(0 – (0 + 0) \equiv {0} \mod {3}\)\(1 – (0 + 0) \equiv {1} \mod {3}\)\(2 – (0 + 0) \equiv {2} \mod {3}\)
001\(0 – (0 + 1) \equiv {2} \mod {3}\)\(1 – (0 + 1) \equiv {0} \mod {3}\)\(2 – (0 + 0) \equiv {2} \mod {3}\)
002\(0 – (0 + 2) \equiv {1} \mod {3}\)\(1 – (0 + 2) \equiv {2} \mod {3}\)\(2 – (0 + 0) \equiv {2} \mod {3}\)
010\(0 – (1 + 0) \equiv {2} \mod {3}\)\(1 – (0 + 0) \equiv {1} \mod {3}\)\(2 – (0 + 1) \equiv {1} \mod {3}\)
011\(0 – (1 + 1) \equiv {1} \mod {3}\)\(1 – (0 + 1) \equiv {0} \mod {3}\)\(2 – (0 + 1) \equiv {1} \mod {3}\)
012\(0 – (1 + 2) \equiv {0} \mod {3}\)\(1 – (0 + 2) \equiv {2} \mod {3}\)\(2 – (0 + 1) \equiv {1} \mod {3}\)
020\(0 – (2 + 0) \equiv {1} \mod {3}\)\(1 – (0 + 0) \equiv {1} \mod {3}\)\(2 – (0 + 2) \equiv {0} \mod {3}\)
021\(0 – (2 + 1) \equiv {0} \mod {3}\)\(1 – (0 + 1) \equiv {0} \mod {3}\)\(2 – (0 + 2) \equiv {0} \mod {3}\)
022\(0 – (2 + 2) \equiv {2} \mod {3}\)\(1 – (0 + 2) \equiv {2} \mod {3}\)\(2 – (0 + 2) \equiv {0} \mod {3}\)
100\(0 – (0 + 0) \equiv {0} \mod {3}\)\(1 – (1 + 0) \equiv {0} \mod {3}\)\(2 – (1 + 0) \equiv {1} \mod {3}\)
101\(0 – (0 + 1) \equiv {2} \mod {3}\)\(1 – (1 + 1) \equiv {2} \mod {3}\)\(2 – (1 + 0) \equiv {1} \mod {3}\)
102\(0 – (0 + 2) \equiv {1} \mod {3}\)\(1 – (1 + 2) \equiv {1} \mod {3}\)\(2 – (1 + 0) \equiv {1} \mod {3}\)
110\(0 – (1 + 0) \equiv {2} \mod {3}\)\(1 – (1 + 0) \equiv {0} \mod {3}\)\(2 – (1 + 1) \equiv {0} \mod {3}\)
111\(0 – (1 + 1) \equiv {1} \mod {3}\)\(1 – (1 + 1) \equiv {2} \mod {3}\)\(2 – (1 + 1) \equiv {0} \mod {3}\)
112\(0 – (1 + 2) \equiv {0} \mod {3}\)\(1 – (1 + 2) \equiv {1} \mod {3}\)\(2 – (1 + 1) \equiv {0} \mod {3}\)
120\(0 – (2 + 0) \equiv {1} \mod {3}\)\(1 – (1 + 0) \equiv {0} \mod {3}\)\(2 – (1 + 2) \equiv {2} \mod {3}\)
121\(0 – (2 + 1) \equiv {0} \mod {3}\)\(1 – (1 + 1) \equiv {2} \mod {3}\)\(2 – (1 + 2) \equiv {2} \mod {3}\)
122\(0 – (2 + 2) \equiv {2} \mod {3}\)\(1 – (1 + 2) \equiv {1} \mod {3}\)\(2 – (1 + 2) \equiv {2} \mod {3}\)
200\(0 – (0 + 0) \equiv {0} \mod {3}\)\(1 – (2 + 0) \equiv {2} \mod {3}\)\(2 – (2 + 0) \equiv {0} \mod {3}\)
201\(0 – (0 + 1) \equiv {2} \mod {3}\)\(1 – (2 + 1) \equiv {1} \mod {3}\)\(2 – (2 + 0) \equiv {0} \mod {3}\)
202\(0 – (0 + 2) \equiv {1} \mod {3}\)\(1 – (2 + 2) \equiv {0} \mod {3}\)\(2 – (2 + 0) \equiv {0} \mod {3}\)
210\(0 – (1 + 0) \equiv {2} \mod {3}\)\(1 – (2 + 0) \equiv {2} \mod {3}\)\(2 – (2 + 1) \equiv {2} \mod {3}\)
211\(0 – (1 + 1) \equiv {1} \mod {3}\)\(1 – (2 + 1) \equiv {1} \mod {3}\)\(2 – (2 + 1) \equiv {2} \mod {3}\)
212\(0 – (1 + 2) \equiv {0} \mod {3}\)\(1 – (2 + 2) \equiv {0} \mod {3}\)\(2 – (2 + 1) \equiv {2} \mod {3}\)
220\(0 – (2 + 0) \equiv {1} \mod {3}\)\(1 – (2 + 0) \equiv {2} \mod {3}\)\(2 – (2 + 2) \equiv {1} \mod {3}\)
221\(0 – (2 + 1) \equiv {0} \mod {3}\)\(1 – (2 + 1) \equiv {1} \mod {3}\)\(2 – (2 + 2) \equiv {1} \mod {3}\)
222\(0 – (2 + 2) \equiv {2} \mod {3}\)\(1 – (2 + 2) \equiv {0} \mod {3}\)\(2 – (2 + 2) \equiv {1} \mod {3}\)
Üç tutuklu için stratejinin uygulanması

Bu strateji tabloya göre üç tutuklu için de işe yaradı. Bu şekilde on tutukluya kadar gitmek mümkün olmadığından bu stratejinin çalışacağını ispatlamaya çalışmak daha kolay olabilir.

Tutukluları birden n’e kadar numaralayalım. i numaralı tutukluya strateji belirlenirken i-1 sayısı atanmış olacak. Her tutuklunun alnındaki sayıyı da \(a_{i} \) şeklinde gösterelim. Göstermek istediğimiz şey şu: Her tutuklu tüm sayıların toplamı modulo n’de kendine atanan sayı olacak şekilde bir tahmin yaparsa en az bir tutuklu kendi alnındaki sayıyı tahmin eder.

O zaman bu ifadeyi matematiksel bir şekilde ifade edelim.

Tüm sayıların toplamına t diyelim. i numaralı tutuklu kendi dışındaki sayıları göreceğinden bu sayıya da \(s_{i} \) diyelim. Yani \(t = a_{i} + s_{i} \) eşitliği geçerli. i numaralı tutuklu toplamı kendi sayısı olan i-1 sayısına eşitlemeye çalıştığına göre yapacağı tahmin de \(i – 1 – s_{i} \mod{n}\) olacaktır. \(s_{i} = t – a_{i} \) olduğundan i numaralı tutuklunun tahminini \(i – 1 – t + a_{i} \mod{n}\) şeklinde yazabiliriz. Göstermek istediğimiz şey ise bu sayının en az bir i için \(a_{i} \) sayısına, yani i numaralı tutuklunun alnındakisayıya eşit olduğunu göstermek. Bu ifadeyi de yerine koyarsak aradığımız şeyin şu olduğu ortaya çıkar:

\(i – 1 – t + a_{i} – a_{i} \equiv{0} \mod{n}\)

Bunu da sadeleştirirsek

\(i – 1 – t \equiv{0} \mod{n}\)

denklemini buluruz. Burada t toplamı sabit bir sayı ve i sayısı da 1’den n’e kadar bütün tam sayılar. Dolayısıyla \(i – 1 – t \mod{n}\) ifadesi belli i değerleri için 0’dan n-1’e kadar bütün sayılara denk olacak. Yani bu denklemin 0’a denk olduğu bir i sayısı da vardır. Hatta tek bir i sayısı vardır. Yani her durum için sadece bir tutuklu doğru cevabı verecektir.

Bu çözüm pek konstruktif olmadı, bulması biraz deneme yanılma ve şansa dayalı oldu ama en azından bu çözümün ispatını verebildim.

Thales (Geogebra)

Çocuklar Thales teoremini öğrenirken bana nasıl oluyor diye sormuşlardı. Ben de lisede öğrendiğim gibi paralel doğrular, üçgenler beklerken birden karşıma çember çıkarmışlardı. Meğer Thales teoremi dedikleri bir çember üzerinde çizilen bir üçgenin bir kenarı çemberin bir çapıysa o üçgen diküçgendir teoremiymiş. Tabii ki ben bununla aynı anlama gelen bir çemberinin üzerinde çemberin bir çapını gören açı dikaçıdır teoremi biliyordum ama bunu Thales adıyla hatırlamıyordum.

Bu sefer de geogebrada bu teoremin animasyonunu yapmaya çalıştım. Kaydırma bileşeniyle çember üzerindeki C noktası kaydırılabiliyor ve her pozisyonda C açısının değeri ölçülüyor. Bu açının her zaman 90 derece olması teoremin de söylediği şeyin gözlemi oluyor.

Java ve skript programlama

Önce çözmeye çalıştığım sorundan bahsedeyim. Şirkette yazdığım program ürettiğimiz makineyi kontrol eden başka programla haberleşiyor ve bazı durumlarda onların ayarlanmasını da düzenliyor. Yalnız çok değişik türlü makineler ürettiğimizden bu makinelerin ayarları da aynı şekilde olmuyor. Örneğin üretim bantı hızı her makinede eşit olmuyor. Dolayısıyla bant üzerindeki ürünü inceleyecek kameranın hızının da bu bant hızına göre ayarlanması gerekiyor. Bunun gibi birbiriyle ilişkili parametreleri program dışından, makineye özel ayarlayabilmek için bir konfigürasyon dosyası kullanmıştım. Bu dosya basit ilişkileri programlamak için yeterliydi.

Zamanla makinelerimiz daha da kompleks olmaya başladı. Artık o konfigürasyon dosyası ihtiyacımız olan ilişkileri tanımlayacak durumda değildi. Yeni ilişkileri tanımlamak da bunları programlamamı gerektirecekti. Bunun yerine başka bir çözüm aramaya başladım.

Sonuçta konfigürasyon dosyası da bir tür program olduğundan, bu iş en kolay bir programlama diliyle çözülür diye düşündüm. Örneğin parametreler arasındaki ilişkileri bir programlama diliyle programlasak ve bu ayarlar yapılırken programın içinde bu dosyayı (yani programı) çalıştırıp o ilişkileri doğru kursak o zaman sorun çözülmüş olabilirdi. Burada tabii ki şöyle bir özelliğe de ihtiyacım var. Bu konfigürasyon dosyası java ile yazdığım programdaki bazı nesnelere erişebilmeliydi. Java programının yapması gereken tek şey bu programı çalıştırmak olacaktı.

Kısa bir araştırma sonucu Java 6 ile gelen JSR-223 yoluyla bu işleri yapabileceğimi öğrendim. Yalnız internette yapmak istediğim şeyi anlatan bir yazıya ya da açıklamaya rastlayamadım. Bunun üzerine okuduğum bazı makalelerden, javadoc sayfalarından yola çıkarak küçük bir örnek program yazdım. Bu sayede istediğim şeyin yapılabileceğini görmüş oldum.

İlk iş olarak yjthon.org sitesinden günce jython sürümünü indirdim ve orada anlatılan şekilde kurdum.

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.script.*;

public class Tutorial {
	
	public static class Configuration {
		private Map<String, String> configuration = new     HashMap<>();
		
		public String get(String identifier) {
			return configuration.get(identifier);
		}
		
		public void set(String identifier, String value) {
			configuration.put(identifier, value);
		}

		@Override
		public String toString() {
			return "Configuration [configuration=" + configuration + "]";
		}
	}
	
	private static String getScript() {
		StringBuilder builder = new StringBuilder();
		builder.append("import sys\n");
		builder.append("configuration.set(\"id1\", \"234\")\n");
		return builder.toString();
	}

	public static void main(String[] args) throws ScriptException {
		System.setProperty("python.home", new File(
		        System.getProperty("user.home"), "jython2.7.2").getPath()
		);
		ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
		
		ScriptEngine engine = scriptEngineManager.getEngineByName("python");
		Configuration con = new Configuration();
		ScriptContext context = engine.getContext();
		context.setAttribute("configuration", con, ScriptContext.GLOBAL_SCOPE);
		String script = getScript();
		CompiledScript compiledScript = ((Compilable)engine).compile(script);
		compiledScript.eval(context);
        System.out.println("configuration: " + con.toString());
	}
}

Yukarıdaki program denemeyi yaptığım oldukça küçük bir proje. Configuration sınıfı kabaca java programım ile skrip arasındaki haberleşmeyi sağlayacak nesneyi tanımlayacak. Skrip bu nesne aracılığı ile ana programdaki konfigürasyona erişip tanımlanan ilişkiler yardımıyla bu konfigürasyonu güncelleyecek.

getScript metodu örnek olarak minimum python kodunu tanımlıyor. Bu kod java programından gelen configuration nesnesindeki id1 isimli nesneye 234 değerini atıyor. main metodunda ise önce python.home sistem değişkenini tanımladım. Bu değişken jython kurulumunu yaptığım dizini gösteriyor. Ardından ScriptEngineManager ve ScriptEngine tipinden nesneleri yarattım. ScriptEngine nesnesinin kullandığı ScriptContext nesnesini kullanarak yarattığım Configuration nesnesini (con) skripte configuration adı altında global bir şekilde aktaracağımı bildirdim. Bunu yapmak için ScriptContext sınıfının setAttribute metodunu kullandım.

Ardından bu skripti her ayar değişikliğinde tekrar kullanacağımdan sonraki kullanımlarda zamandan kazanmak için ilk seferde derlemeyi denedim. Bunun için

CompiledScript compiledScript = ((Compilable)engine).compile(script);

satırını kullandım. Bu durumda her kullanımda aynı nesneyi kullanmam gerekecek ama bu kolay bir sorun. Ardından bu skripti çalıştırdım ve java programındaki nesnenin içeriğinin değiştiğini gördüm. Tam da istediğim sonuç.