Merhaba dostlar.
Bugün, akşamüstü vakitlerinde Google’da “Python örnek kodlar” ve “Python örnekleri” diye arattırdım. Sonuçlar içerisinde bir web sitesine denk geldim ve Python ile Anagram (Bkz. Anagram Nedir?) oluşturduğunu iddia eden bir makale okudum. Mantık olarak çok az doğru gidilse de istenilen sonuca ulaşması imkansızdı iddia edilen makalede. Çünkü pek çok detay atlanmış ve düzensiz bir değer ortaya çıkıyordu. Bende emin olmak için kendim yapmaya karar verdim.
Yazının sonlarına doğru, Anagram tekniğinin doğru algoritmasını ve bunun kodlarını göreceksiniz. Ama “Ben hepsini okuyamam, kodu görsem anlarım arkadaş! Sen sadece kodları ver” diyorsan, tıkla bakalım.
Şimdi bahsettiğim web sitesini ve yukarıda yazdıklarımı bir kenara bırakıp; TÜBİTAK’ın yayınlamış olduğu aşağıdaki soruları nasıl cevaplarız, buna odaklanalım.
Önce algoritmayı ele alalım ve neyin ne ifade ettiğini bir kavrayalım.
Anagram tekniğinde; belirtilen cümle veya kelime içerisinde kaç adet harf olduğu tespit edilir ve ardından boşluk karakterler dikkate alınmaksızın, bulunan sonuç alfabetik sıraya alınarak yazılır. Örneğin “merhaba” kelimesini ele alalım. Kelime içerisindeki harfleri, adetleri ile beraber vermiş olduğumuz kelime bazında sıralayacak olursak şu sonuçları elde ederiz (Tablo 1).
Yukarıda Anagram için bulunan harf ve adetlerde alfabetik sıralama yaptığından da bahsetmiştik. Tablo 1’deki verileri alfabetik sıralamaya alacak olursak şöyle bir sonuç alacağız, “2a1b1e1h1m1r”.
Peki bu sonuç yeterli mi? Cevap kesinlikle hayır! Çünkü Anagram tekniğinde kodlanacak olan kelime veya cümle içerisindeki harflerden sadece bir tane varsa, sayısal değeri yazılmaz. Haliyle sonucumuz “2abehmr” şeklinde olmalıdır.
Bu nereden anlıyoruz?
TÜBİTAK’ın vermiş olduğu örneği tekrar inceleyelim. “Dünya güneşin etrafında döner” cümlesini ele alalım. İçerisindeki harfleri alfabetik bazında sıralayacak olursak, sonuç aşağıdaki gibi olacaktır (Tablo 2).
Tablo 2’yi tek satırlık bir düzene sokacak olursak bizim bulduğumuz sonuç “3a3D3e1f1g1ı1i5n1ö2r1ş1t2ü1y” şeklinde olacaktır. Peki TÜBİTAK’ın bu soruya vermiş olduğu cevap neydi? Sayfanın en başına gelerek bakıyoruz, bize verdiği cevap, “3a3d3efgıi5nö2rşt2üy” olarak belirtilmiş. Buradan iki sonuç çıkar:
- Dikkat ederseniz kelime içerisindeki f,g,ı,i,ş,y harflerinin başında 1 yazmamaktadır, yani sayısal değerini belirtmemiştir. Bu demek oluyor ki, kelime veya cümle içerisindeki tek harfler için sayısal değerini yazmamalıyız.
- Bulduğumuz sonuç ile TÜBİTAK’ın bulduğu sonuç arasında bir harf farkı vardır. “Dünya güneşin etrafında döner” cümlesindeki “D” harfi büyüktür. Bulduğumuz sonuç içerisinde “D” büyük iken, TÜBİTAK’ın verdiği sonuç içerisindeki “d” harfi küçüktür. Bu demek oluyor ki, kelime veya cümle içerisindeki harflerin hepsini küçük harf olarak dikkate almalıyız.
Buraya kadar Anagram tekniğinin inceliklerini fark etmişsinizdir.
Şimdi, Python’da yazdığım kodları ve bulduğu sonuçları paylaşıyorum. Detaylı anlatım devam edecektir..
Python ile TÜBİTAK sorusunun cevabını bulan kod.
# -*- coding: cp1254 -*-
__author__ = 'EnisKurtay'
import locale
locale.setlocale(locale.LC_ALL, "Turkish_Turkey.1254")
def anagram(cumle):
cumle = cumle.lower()
liste = []
for s in set(cumle):
if (str(s).isspace() == False) and (str(s).isalpha() == True):
liste.append(s)
liste.sort(key = locale.strxfrm)
if (liste.__contains__("ı") == True) and (liste.__contains__("i") == True):
liste.remove("i")
liste.insert(liste.index("ı") + 1,"i")
sonuc = ""
for d in liste:
if cumle.count(d) == 1:
sonuc = sonuc + "{0}".format(d)
else:
sonuc = sonuc + "{0}{1}".format(cumle.count(d), d)
return sonuc
#Birinci sorunun cevabı
print "TÜBİTAK Cevapları..\n(i) : {0}".format(anagram("Dünya güneşin etrafında döner"))
#Ikinci sorunun cevabı
tubitakCumleler = ["Su molekülünde üç atom vardır", "Yanma oksijen gerektirir", "Elmas karbondan oluşur"]
for s in tubitakCumleler:
if anagram(s) == "2a3eg3ij2km2no3rsty":
print "(ii): {0}".format(s)
Bulduğumuz cevabı da kanıtlayalım.
Detaylı Anlatım
Öncelikle anagram adında bir parametrik fonksiyon tanımladım:
def anagram(cumle):
Ardından “cumle” parametresine gelecek değeri, içerisinde bulunan harfleri tekrarlamayacak şekilde döngüye alıp bir listeye aktarıyorum. Fakat listeye aktarmadan önce, gelen değerin harf mi yoksa boşluk karakteri mi olduğuna dikkat ederek listeye aktarıyorum. Hemen bunun sonrasında bu listeyi alfabetik olarak sıralıyorum.
cumle = cumle.lower()
liste = []
for s in set(cumle):
if (str(s).isspace() == False) and (str(s).isalpha() == True):
liste.append(s)
#Aşağıdaki sort() fonksiyonun içerisindeki parametreyi birazdan açıklayacağım
liste.sort(key = locale.strxfrm)
Ancak burada bir şey dikkatimi çekmişti, sizlerle paylaşmak isterim. Python ile Türkçe’de bulunan tüm harfleri içeren bir diziyi sıralamaya doğrudan aldığımda, Türkçe’de bulunan işaretli (ç, ö, ü, ş gibi) karakterlerin sıralamada en sonlarda belirdiğini gözlemledim. İnternette locale kullanarak türkçe karakterli işlemlerde setlocale yapmamız gerektiğini belirten bir yazıyla karşılaştım. Bu yüzden önce locale tanımlarımı yaptım ve ardından sort() fonksiyonun içerisinde böyle bir parametre yazdım.
locale ettikkten sonra herşeyin düzenli ancak “i” harfinin, “ı” harfinden önce geldiğini gördüm. İnternette bu sefer tam bir derinlemesine araştırma yaptım ve bunun çözümü ile alakalı pek çok şey okudum. Hiçbiri istediğim sonucu vermedi. UTF-8, Unicode yöntemleriyle çözmeye çalışıyorlardı fakat bana Windows – 1254 gerekiyordu. Düşünsenize bir kelime içerisindeki harfleri alfabetik sıraya soktunuz ve “cç”, “gğ”, “oö”, “sş”, “uü” harfleri düzenli & doğru gelirken, “iı” düzensiz geliyor(!) Tam bir saçmalık ve kabul edilemez bir durum!
Bende daha fazla vakit harcamamak ve bunun önüne geçmek için kısa bir çözüm buldum. Eğer bulduğum kelimelerin içerisinde “ı” ve “i” harfleri varsa, yerlerini değiştirmem benim için yeterliydi. Çünkü “ı” harfi yoksa, mantıklı olarak “i” harfi aslında olması gereken yerdedir alfabetik sıralamada. 🙂
if (liste.__contains__("ı") == True) and (liste.__contains__("i") == True):
liste.remove("i")
liste.insert(liste.index("ı") + 1,"i")
Ardından fonksiyonumuzun geri kalanını yazdım. Aşağıdaki kodlarda her harfin, kaç adet olduğunun bilgisini değişkene ekleyerek, fonksiyon içerisinde bulduğum sonucu “return” deyimi döndürdük.
sonuc = ""
for d in liste:
if cumle.count(d) == 1:
sonuc = sonuc + "{0}".format(d)
else:
sonuc = sonuc + "{0}{1}".format(cumle.count(d), d)
return sonuc
Geriye artık fonksiyonumuzu çağırmak kaldı. Tek yapmamız gereken fonksiyonu çağırırken içine parametre vermektir. Bende TÜBİTAK’ın verdiği soruları çözen kodları yazdım. Eğer siz sadece Anagram’daki değeri görmek istiyorsanız ‘print anagram(“cümle veya kelimenizi buraya yazın”)‘ demeniz yeterlidir.
#Birinci sorunun cevabı
print "TÜBİTAK Cevapları..\n(i) : {0}".format(anagram("Dünya güneşin etrafında döner"))
#Ikinci sorunun cevabı
tubitakCumleler = ["Su molekülünde üç atom vardır", "Yanma oksijen gerektirir", "Elmas karbondan oluşur"]
for s in tubitakCumleler:
if anagram(s) == "2a3eg3ij2km2no3rsty":
print "(ii): {0}".format(s)
Bir sonraki makalede görüşmek üzere.
Bu makalede geçen kodlar için : https://github.com/eniskurtayyilmaz/Python27/blob/master/anagram.py