Giriş: Sudoku ve Matematik
Sudoku, dünya çapında popüler olan basit kurallara sahip bir mantık bulmacasıdır. 9x9’luk bir ızgarada, her satır, her sütun ve her 3x3’lük alt kare 1’den 9’a kadar rakamları tam olarak bir kez içermelidir. Genellikle bu bulmacalar deneme yanılma veya daha sistematik geri izleme (backtracking) algoritmalarıyla çözülür.
Peki ya Sudoku’yu daha soyut matematiksel araçlarla, örneğin cebirsel geometri ile modelleyip çözebileceğimizi söylesem? Bu yazıda, Sudoku kısıtlarını polinom denklemlerine dönüştürerek ve bilgisayar cebiri tekniklerini kullanarak bu bulmacaya nasıl farklı bir bakış açısı getirebileceğimizi inceleyeceğiz. Ayrıca, teorik yaklaşımın aksine pratik bir Python çözücüsüne de göz atacağız.
Sudoku Kurallarını Polinomlara Dönüştürmek
Temel fikir, 9x9=81 hücrenin her birini bir x_i
değişkeni (burada i
1’den 81’e kadar) olarak temsil etmektir. Amacımız, bu değişkenlerin Sudoku kurallarına uymasını sağlayan bir denklem sistemi kurmaktır.
Kısıt 1: Her Hücre 1 ile 9 Arasında Bir Değer Almalı
Her x_i
değişkeninin {1, 2, …, 9} kümesinden bir değer almasını nasıl zorunlu kılarız? Bunu başarmak için her x_i
için şu polinomu tanımlayabiliriz:
F_i(x_i) = (x_i - 1)(x_i - 2)...(x_i - 9)
Bir x_i
değerinin 1 ile 9 arasında olması ancak ve ancak F_i(x_i) = 0
denkleminin sağlanması durumunda geçerlidir. Eğer x_i = 5
ise, çarpımın terimlerinden biri (5-5)=0
olacağından F_i(5) = 0
olur. Eğer x_i = 10
ise, hiçbir terim sıfır olmaz ve F_i(10)
sıfırdan farklıdır.
Definition (Değer Kısıtlama Polinomu)
Her i
(1’den 81’e kadar) için, x_i
’nin {1, …, 9} kümesinde bir değer almasını sağlayan polinom F_i(x_i) = (x_i - 1)(x_i - 2)...(x_i - 9)
şeklindedir. Sudoku çözümünde F_i(x_i) = 0
olmalıdır.
Kısıt 2: Satır, Sütun ve Bloklarda Benzersizlik
Aynı satırda, sütunda veya 3x3’lük blokta bulunan iki farklı hücrenin (örneğin x_i
ve x_j
, i
farklı j
) aynı değeri almaması gerekir. Bu “benzersizlik” kısıtını nasıl ifade ederiz?
Öncelikle, aynı grup içinde yer alan (aynı satır, sütun veya blokta) tüm hücre çiftlerini içeren bir küme tanımlayalım:
E = {(i, j) | 1 <= i < j <= 81, x_i ve x_j aynı grupta}
Şimdi, her (i, j)
E kümesindeki çift için x_i
ve x_j
değerlerinin farklı olmasını sağlayan bir polinom denklemi istiyoruz. Orijinal makalede G_ij
adında bir polinom tanımlanmış olsa da, bu kısmın matematiksel doğruluğu ve pratikteki uygulanabilirliği tartışmalıdır.
Daha standart ve yaygın bir yaklaşım, x_i
ve x_j
’nin farklı olması kısıtını, yeni bir yardımcı y_ij
değişkeni kullanarak y_ij * (x_i - x_j) - 1 = 0
denklemiyle ifade etmektir. Bu denklem, x_i - x_j
ifadesinin sıfır olmamasını, yani x_i
ve x_j
’nin farklı olmasını garanti eder.
Lemma (Benzersizlik Kısıtı)
Standart cebirsel modellemede, aynı gruptaki x_i
ve x_j
hücrelerinin farklı değerler alması kısıtı, genellikle her (i, j)
çifti için y_ij * (x_i - x_j) = 1
denklemini sağlayacak bir y_ij
yardımcı değişkeninin varlığını gerektirerek ifade edilir.
Cebirsel Model: İdealler ve Varyeteler
Tüm Sudoku kısıtlarını bir araya getiren cebirsel yapıyı ideal kullanarak tanımlarız. K[x_1, ..., x_81]
polinom halkasında (burada K
genellikle Q (rasyonel sayılar) veya C (karmaşık sayılar) gibi bir cisimdir) şu ideali oluştururuz:
I
ideali, tüm F_i(x_i) = 0
denklemleri ve her (i,j)
E kümesindeki çift için y_ij * (x_i - x_j) - 1 = 0
denklemleri tarafından üretilir.
Bu idealin sıfırlarının kümesi, yani varyetesi V(I)
, tüm Sudoku kısıtlarını sağlayan (x_1, ..., x_81)
vektörlerinin kümesidir. Sudoku bulmacasının çözümleri tam olarak V(I)
içindeki tamsayı koordinatlı noktalardır.
Belirli Bir Bulmacayı Çözmek
Genellikle bir Sudoku bulmacasında bazı hücreler başlangıçta doldurulmuştur. Diyelim ki L
, {1, …, 81}‘in bir alt kümesi olmak üzere, L
içindeki i
indisli hücreler için başlangıç değerleri a_i
verilmiş olsun. Bu ek bilgiyi modele katmak için ideali genişletiriz:
I_S
ideali, I
idealine x_i = a_i
başlangıç koşullarının (i
L’de olmak üzere) eklenmesiyle oluşturulur.
Eğer Sudoku bulmacasının tek bir çözümü (a_1, ..., a_81)
varsa, I_S
idealinin indirgenmiş Gröbner bazı (reduced Gröbner basis) şu şekilde olacaktır:
\{x_1 - a_1, x_2 - a_2, ..., x_81 - a_81\}
Important (Gröbner Bazı ve Çözüm)
İyi tanımlı (tek çözümlü) bir Sudoku bulmacası için I_S
idealinin indirgenmiş Gröbner bazını hesaplamak, doğrudan bulmacanın çözümünü verir. x_i = a_i
denklemleri çözümün değerleridir.
Gröbner bazları, polinom denklem sistemlerini çözmek için güçlü bir araçtır ve bilgisayar cebiri sistemleri (CAS) kullanılarak hesaplanabilir.
Teori vs. Pratik: Hesaplama Maliyeti
Bu cebirsel yaklaşım teorik olarak çok şık olsa da, 81 değişkenli bir sistem için Gröbner bazı hesaplamak hesaplama açısından aşırı pahalıdır. Pratikte, Sudoku çözmek için çok daha verimli algoritmalar mevcuttur.
En yaygın ve etkili yöntemlerden biri geri izleme (backtracking) algoritmasıdır.
Pratik Bir Yaklaşım: Python ile Geri İzleme Algoritması
Aşağıda, standart bir geri izleme tekniği kullanarak Sudoku çözen basit bir Python programı örneği bulunmaktadır:
N = 9EMPTY = 0
def print_board(board): """Sudoku tahtasını formatlı yazdırır.""" print("-------------------------") for i in range(N): print("| ", end="") for j in range(N): if board[i][j] == EMPTY: print(". ", end="") else: print(f"{board[i][j]} ", end="") if (j + 1) % 3 == 0: print("| ", end="") print() if (i + 1) % 3 == 0: print("-------------------------")
def is_valid(board, row, col, num): """Bir sayının belirli bir hücreye yerleştirilip yerleştirilemeyeceğini kontrol eder.""" # Satır kontrolü if num in board[row]: return False
# Sütun kontrolü if num in [board[i][col] for i in range(N)]: return False
# 3x3 blok kontrolü start_row, start_col = row - row % 3, col - col % 3 for i in range(3): for j in range(3): if board[i + start_row][j + start_col] == num: return False
return True
def find_empty_cell(board): """Boş bir hücrenin konumunu bulur (satır, sütun).""" for r in range(N): for c in range(N): if board[r][c] == EMPTY: return r, c return None # Boş hücre yoksa
def solve_sudoku(board): """Sudoku'yu çözen ana geri izleme fonksiyonu.""" find = find_empty_cell(board) if not find: return True # Bulmaca çözüldü else: row, col = find
for num in range(1, 10): # 1-9 arası sayıları dene if is_valid(board, row, col, num): board[row][col] = num # Geçerliyse yerleştir
if solve_sudoku(board): # Özyinelemeli olarak devam et return True
# Çözüme götürmediyse geri al (backtrack) board[row][col] = EMPTY
return False # Bu hücre için geçerli sayı bulunamadı
# Ana Kısım (Örnek Kullanım)if __name__ == "__main__": board = [ [5, 3, 0, 0, 7, 0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9] ]
print("Başlangıçtaki Sudoku:") print_board(board) print("\nÇözülüyor...\n")
if solve_sudoku(board): print("Çözülmüş Sudoku:") print_board(board) else: print("Çözüm bulunamadı.")
Bu algoritma, boş bir hücre bulur, 1’den 9’a kadar geçerli bir sayı yerleştirmeyi dener ve özyinelemeli olarak devam eder. Eğer bir noktada tıkanırsa (geçerli sayı bulunamazsa), önceki adıma döner ve farklı bir sayı dener.
Sonuç
Sudoku bulmacasını cebirsel geometri kullanarak modellemek, probleme farklı ve matematiksel olarak zengin bir bakış açısı sunar. Polinomlar, idealler ve Gröbner bazları gibi kavramlar, Sudoku’nun yapısını derinlemesine anlamamızı sağlar. Ancak, hesaplama verimliliği açısından bu teorik yaklaşım, geri izleme gibi standart algoritmik yöntemlerin gerisinde kalır. Yine de, basit bir mantık bulmacasının ardındaki soyut matematiksel yapıları görmek oldukça etkileyicidir!