Discussion:
[Python-de] Sudoku mit Python
Mathias Uebel
2006-08-04 03:41:52 UTC
Permalink
Hallo Python-Fans,

wenn ich Langeweile habe, übe ich mich in Python.

Ich habe jetzt mal so etwas gemacht:

http://www.meeloon.de/sudoku/index.cgi

Hat jemand mal Lust den Code zu prüfen?
Ich habe leider niemanden, mit dem ich mich austauschen kann (Python
betreffend!).

Grusz aus der Enklave
grassi
2006-08-04 04:05:53 UTC
Permalink
Hallole,
Post by Mathias Uebel
wenn ich Langeweile habe, übe ich mich in Python.
löblich ;-)
Post by Mathias Uebel
http://www.meeloon.de/sudoku/index.cgi
Hat jemand mal Lust den Code zu prüfen?
Ich würde ihn mir gern mal anschauen, ob ich zu kommentieren in der
Lage bin sei dahin gestellt.
Post by Mathias Uebel
Ich habe leider niemanden, mit dem ich mich austauschen kann (Python
betreffend!).
Sieht auf den ersten Blick so aus, als ob der Server 50 Sekunden
"rödelt", wenn ich auf Submit drücke, ohne was einzugeben. Falls das
die CPU auslasten sollte, bietet das für potentielle Angreifer
eventuell die Möglichkeit, deinen Server zu "überlasten" (DoS-Attacke).
Aber dir gings jetzt womöglich mehr um den Algorithmus ...

Schöne Grüsse,
Marcus
Mathias Uebel
2006-08-04 05:31:08 UTC
Permalink
Post by grassi
Hallole,
Post by Mathias Uebel
wenn ich Langeweile habe, übe ich mich in Python.
löblich ;-)
Post by Mathias Uebel
http://www.meeloon.de/sudoku/index.cgi
Hat jemand mal Lust den Code zu prüfen?
Ich würde ihn mir gern mal anschauen, ob ich zu kommentieren in der
Lage bin sei dahin gestellt.
[...]
Post by grassi
Sieht auf den ersten Blick so aus, als ob der Server 50 Sekunden
"rödelt", wenn ich auf Submit drücke, ohne was einzugeben.
[...]
Ja, es rödelt ganz schön. Man kann die Schleifen begrenzen. Meine
Versuche haben gezeigt, dass es mit ca. 10-15 Durchläufe erledigt ist.
Darum habe ich eine Max von 50 eingegeben.
Jetzt habe ich ein Demo eingerichtet (Zahlenvorgabe).

Ich weiss, dass html als Interface nicht geeignet ist. Ein GUI kommt noch.

Hier die Klasse:

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
#
# copyright ***@meeloon.de
#

"""
Sudoku
--------------------------------------------------------------
| x | 6 | 4 | 5 | x | 9 | 2 | 3 | x |
--------------------------------------------------------------
| 8 | x | 3 | x | 4 | x | 6 | x | 1 |
--------------------------------------------------------------
| 1 | 2 | x | 3 | x | 6 | x | 4 | 5 |
--------------------------------------------------------------
| 5 | x | 7 | x | 2 | x | 8 | x | 4 |
--------------------------------------------------------------
| x | 1 | x | 8 | x | 5 | x | 6 | x |
--------------------------------------------------------------
| 6 | x | 8 | x | 7 | x | 5 | x | 9 |
--------------------------------------------------------------
| 9 | 8 | x | 7 | x | 2 | x | 5 | 6 |
--------------------------------------------------------------
| 2 | x | 6 | x | 5 | x | 1 | x | 3 |
--------------------------------------------------------------
| x | 4 | 5 | 1 | x | 8 | 9 7 | x |
--------------------------------------------------------------


"""

import sys

class sudoku:
"""
Diese Klasse nimmt ein Dictionary mit 81 Wertepaaren((int,int):int) auf,
z.B.{(0,0):3,(1,1):5,...}
Die Schluessel sind die Sudoku-Koordinaten, die Values sind die
vorhandenen Werte.
Die Methode 'Scan' scannt dann die Matrix und gibt ein Dictionary mit
den moeglichen Treffern zurueck.
Treffer sind die Werte-Paare, welche fuer jede gegebene Ziffer einer
Zelle nur eine Moeglichkeit ergibt (ScanLevel, default = 1).
Die Methode 'Result' gibt des Ergebnis von 'MaxTries' Versuche aus
(default sind 100 Versuche).


table
col0 col1 col2 col3 col4 col5 col6 col7 col8
-----------------------------------------------------------
row0 ||field|field|field|| ---- | ---- |
row1 ||field|field|field|| cell | cell |
row2 ||field|field|field|| ---- | ---- |
-----------------------------------------------------------
row3 | ---- | ---- | ---- |
row4 | cell | cell | cell |
row5 | ---- | ---- | ---- |
-----------------------------------------------------------
row6 | ---- | ---- | ---- |
row7 | cell | cell | cell |
row8 | ---- | ---- | ---- |
-----------------------------------------------------------


def __init__(self, TableDict):
""" Initialisierung """
self.table = TableDict or list
# Abbruch, wenn das Dictionary nicht genug Daten hat
if len(self.table) is not 81:
sys.exit('zu wenig oder zu viel daten an "class table" uebergeben')

def _Zero(self):
""" Koordinaten fuer Nullen feststellen """
list = []
Keys = self.table.keys()
Keys.sort()
for a in Keys:
if self.table[a] == 0:
list.append(a)
#print self.table[a][b]
return list

def _RangeRow(self, n):
""" die Zeilen der Zelle 'n' """
if n in [0,1,2]:
Range = [0,1,2]
if n in [3,4,5]:
Range = [3,4,5]
if n in [6,7,8]:
Range = [6,7,8]
return Range

def _RangeCol(self, n):
""" die Spalten der Zelle 'n' """
if n in [0,3,6]:
Range = [0,1,2]
if n in [1,4,7]:
Range = [3,4,5]
if n in [2,5,8]:
Range = [6,7,8]
return Range

def Cell(self,n=0):
"""Ermittelt die Werte einer Zelle 'n=0' """
list = []
for a in self._RangeRow(n):
for b in self._RangeCol(n):
list.append(self.table[(a,b)])
return list

def Row(self,n=0):
"""Ermittelt die Werte einer Reihe 'n=0' """
list = []
for a in range(9):
list.append(self.table[(n,a)])
return list

def Col(self,n=0):
"""Ermittelt die Werte einer Spalte 'n=0' """
list = []
for a in range(9):
list.append(self.table[(a,n)])
return list

def ValueFailt(self,n):
"""print Liste: Welche Werte fehlen?"""
list = []
for a in range(1,10):
if a not in self.Cell(n):
list.append(a)
return list

def Scan(self,ScanLevel = 1):
""" Scannt alle Reihen und Spalten """
out = []
for n in range(9): # alle Zellen werden gescannt
dict = {}
for a in self.ValueFailt(n): # Die Liste der fehlenden Felder
abarbeiten
z = 0
list = []
for b in self._RangeRow(n): # Tabelle nach Reihen einlesen
if a not in self.Row(b): # Reihe auf fehlende Werte scannen
for c in self._RangeCol(n): # Tabelle nach Spalten einlesen
if a not in self.Col(c): # Spalte auf fehlende Werte scannen
if (b,c) in self._Zero(): # nur gueltig, wenn es eine leere
0-Stelle ist
#list.append(("%s" % (b+1,
['A','B','C','D','E','F','G','H','I'][c])))
list.append((b,c)) # die Koordinaten in eine Liste geben
z = z + 1 # Anzahl der moeglichen Treffer zaehlen z =
z + 1 # Anzahl der moeglichen Treffer zaehlen
if len(list) <= ScanLevel: # ScanLevel='1': nur die die Felder
mit einer x,y Uebereinstimmung
dict[a]=list
out.append(dict) # gibt Dictionary mit x,y Uebereinstimmungen
zurueck
return out

def Result(self, MaxTries = 100):
"""Arbeitet die Scanvorgaenge bis maximal 'MaxTries' durch.
Gibt die Anzahl der Versuche zurueck."""
z = 0
while z < MaxTries: # maximale Schleifen
for a in self.Scan():
for b in a.keys():
d = a[b]
#dict = {a[b] : b}
for w in d:
Dict = {w:b}
#print Dict
self.table.update(Dict)
if len(self._Zero()) == 0:
break
z = z + 1
return z

def __str__(self):
str = ""
z = 0
Keys = self.table.keys()
Keys.sort()
for a in Keys:
if z%9 == 0: str = str + "\n"
str = str + " %s " % self.table[a]
z = z + 1
return str
Diez B. Roggisch
2006-08-04 16:23:08 UTC
Permalink
Post by Mathias Uebel
Ja, es rödelt ganz schön. Man kann die Schleifen begrenzen. Meine
Versuche haben gezeigt, dass es mit ca. 10-15 Durchläufe erledigt ist.
Darum habe ich eine Max von 50 eingegeben.
Jetzt habe ich ein Demo eingerichtet (Zahlenvorgabe).
Also da faellt einem schon so einiges auf:


Die Repraesentation

Es ist zwar legitim, dafuer ein dict von Koordinate nach Belegung zu
nehmen - aber da ein sudoku ja keine Luecken haben kann (nur unbestimmte
Felder, aber fehlen tun die ja nicht), macht man das normalerweise mit
einer liste von listen.

Zweitens ist es nicht clever, die Freiheitsgrade einfach mit 0 zu
belegen - damit hast du ja garkeine Informationen mehr! Besser ist es,
pro Feld eine Menge (set) zu speichern, mit den moeglichen Werten. Fuer
unbelegte Felder ist das dann set(xrange(1, 10)), fuer vorbelegte
set([belegung])

Dann kannst du dir auch die explizite Zero-Speicherung sparen. Zero ist
wo zuviel drin ist!

Berechnung der Zellenkoordinaten

Du berechnest die jedes mal, aber noch nicht mal "richtig". Soll
heissen: entweder erstellt man ein dictionary, das die Zellen-nummer auf
die Koordinaten abbildet, oder man errechnet das wirklich. Letzteres
waere zb so moeglich:

def cellcols(n, width=3):
start = (n % width) * width
return xrange(start, start + width)

def cellrows(n, width=3):
start = (n / width) * width # achtung, integer division! Eventuell
// verwenden
return xrange(start, start + width)


Aber du machst halt einen unsinnvollen Mischmasch aus beidem -
festkodiertes Wissen und code. Und selbst wenn du auf obigen Trick nicht
gekommen waerst: man kann die Werte dann ja mal minimal cachen!

Algorithmus

Abgesehen das ValueFailt ValuesMissing heissen sollte, ist es
hochgerading ineffizient. Denn statt dir das einmal zu merken und immer
nur Werte wegzunehmen, berechnest du das jedes mal neu.

Und dann "kann" dein Algorithmus auch wirklich nur die einfachsten aller
Sudokus loesen: wo sich jedes mal ein Feld neu ergibt, aus simpler
Schnittmengenberechnung der Zelle, Reihe und Spalte. Es gibt aber
Sudokus, bei denen man so nicht weiterkommt. Da muss man dann mittels
Backtracking spekulativ vorgehen.

Dann zaehlst du die moeglichen Werte pro Element immer durch - dabei
koenntest du im Fall von >1 sofort abbrechen!

Was du aber auf jeden Fall vermeiden solltest ist diese komische
Abbruchbedingung als Schleife - das ist Unfug. Stattdessen merkst du
dir, ob du wirklich was veraendert hast. Der Algorithmus laeuft dann
solange durch, bis sich nix mehr getan hat. Und entweder ist das Ding
dann geloest, oder eben nicht.



Ich habe mal meinen sehr alten solver angehangen - er kann nur eine
Optimierung (naked pairs), aber dank backtracking loest er jedes Sudoku.


MfG Diez
-------------- nächster Teil --------------
Ein Dateianhang mit Binärdaten wurde abgetrennt...
Dateiname : sudoku.py
Dateityp : application/x-python
Dateigröße : 4559 bytes
Beschreibung: nicht verfügbar
URL : http://starship.python.net/pipermail/python-de/attachments/20060804/eaa2d2fd/sudoku-0001.bin
Mathias Uebel
2006-08-04 19:21:26 UTC
Permalink
Diez B. Roggisch schrieb:
[...]
Hallo Diez,
Also Dir fällt etwas auf.
Post by Diez B. Roggisch
Die Repraesentation
Es ist zwar legitim, dafuer ein dict von Koordinate nach Belegung zu
nehmen - aber
[...]
Ja, die Entscheidung für ein Dictionary war vieleicht nicht die
Richtige. Aber ich dachte an die Koordinaten und ...
Post by Diez B. Roggisch
Zweitens ist es nicht clever, die Freiheitsgrade einfach mit 0 zu
belegen -
[...]
Nun ich wollte hier im gleichen Datentyp bleiben.
[...]
Post by Diez B. Roggisch
Berechnung der Zellenkoordinaten
Du berechnest die jedes mal, aber noch nicht mal "richtig".
[...]
Es funktioniert.
[...]
Post by Diez B. Roggisch
start = (n % width) * width
return xrange(start, start + width)
start = (n / width) * width # achtung, integer division! Eventuell
// verwenden
return xrange(start, start + width)
Diese Abkürzung ist gut und werde ich übernehemen.

[...]
Post by Diez B. Roggisch
Algorithmus
Abgesehen das ValueFailt ValuesMissing heissen sollte
Geschenkt.
, ist es
Post by Diez B. Roggisch
hochgerading ineffizient. Denn statt dir das einmal zu merken und immer
nur Werte wegzunehmen, berechnest du das jedes mal neu.
Richtig. Das werde ich ändern.
Post by Diez B. Roggisch
Und dann "kann" dein Algorithmus auch wirklich nur die einfachsten aller
Naja, fast alle Sudokus aus einer Zeitung hat es berechnet (8 von 10).
Den Rest habe ich mir für jetzt aufgehoben. Eventuell bin ich etwas zufrüh.
[...]
Post by Diez B. Roggisch
Dann zaehlst du die moeglichen Werte pro Element immer durch - dabei
koenntest du im Fall von >1 sofort abbrechen!
Stimmt.
Post by Diez B. Roggisch
Was du aber auf jeden Fall vermeiden solltest ist diese komische
Abbruchbedingung als Schleife - das ist Unfug. Stattdessen merkst du
dir, ob du wirklich was veraendert hast. Der Algorithmus laeuft dann
solange durch, bis sich nix mehr getan hat. Und entweder ist das Ding
dann geloest, oder eben nicht.
Ja, das stimmt auch. Das ist etwas sinnlos. Schon entfernt.
Post by Diez B. Roggisch
Ich habe mal meinen sehr alten solver angehangen - er kann nur eine
Optimierung (naked pairs), aber dank backtracking loest er jedes Sudoku.
Das schau ich mir an. Aber, wie geschrieben: Ich will üben. Und darum
bin ich einen anderen Weg gegangen.

Grusz aus der Enklave
Diez B. Roggisch
2006-08-04 19:38:48 UTC
Permalink
Post by Mathias Uebel
Nun ich wollte hier im gleichen Datentyp bleiben.
Warum? Abgesehen davon das "meine" Mengen auch immer der gleiche
Datentyp sind.
Post by Mathias Uebel
Post by Diez B. Roggisch
Berechnung der Zellenkoordinaten
Du berechnest die jedes mal, aber noch nicht mal "richtig".
[...]
Es funktioniert.
Ja. Sortieren indem man eine Permutation generiert und guckt ob das
Ergebnis sortiert ist funktioniert auch. Ist es deswegen eine gute Idee?


MfG Diez
Mathias Uebel
2006-08-04 19:54:57 UTC
Permalink
Post by Diez B. Roggisch
Post by Mathias Uebel
Nun ich wollte hier im gleichen Datentyp bleiben.
Warum? Abgesehen davon das "meine" Mengen auch immer der gleiche
Datentyp sind.
Post by Mathias Uebel
Post by Diez B. Roggisch
Berechnung der Zellenkoordinaten
Du berechnest die jedes mal, aber noch nicht mal "richtig".
[...]
Es funktioniert.
Ja. Sortieren indem man eine Permutation generiert und guckt ob das
Ergebnis sortiert ist funktioniert auch. Ist es deswegen eine gute Idee?
Hallo Dietz,

Auch in diesem Punkt hast Du natürlich Recht.

Grusz aus der Enklave

Timothy Kesten
2006-08-04 12:20:33 UTC
Permalink
Post by Mathias Uebel
Hallo Python-Fans,
wenn ich Langeweile habe, übe ich mich in Python.
http://www.meeloon.de/sudoku/index.cgi
Hat jemand mal Lust den Code zu prüfen?
Ich habe leider niemanden, mit dem ich mich austauschen kann (Python
betreffend!).
Hi Matthias,

falls es dich interessiert.

Zum Thema Python und Sudoku

http://www.linuxjournal.com/article/8729
http://www.linuxjournal.com/article/8858

Und im Linux-Magazin 6/06 ist auch dazu ein Artikel enthalten.
Solltest Du Interesse haben aber den Linux-Magazin Artikel nicht anderweitig
bekommen können, so könnte ich ihn Dir einscannen und als PDF zusenden.

Gruß
Timothy
Mathias Uebel
2006-08-04 14:25:31 UTC
Permalink
[...]
Post by Timothy Kesten
Post by Mathias Uebel
http://www.meeloon.de/sudoku/index.cgi
[...]
Post by Timothy Kesten
Hi Matthias,
falls es dich interessiert.
Zum Thema Python und Sudoku
http://www.linuxjournal.com/article/8729
http://www.linuxjournal.com/article/8858
Und im Linux-Magazin 6/06 ist auch dazu ein Artikel enthalten.
Solltest Du Interesse haben aber den Linux-Magazin Artikel nicht anderweitig
bekommen können, so könnte ich ihn Dir einscannen und als PDF zusenden.
Gruß
Timothy
Hallo Timothy,

herzliche Dank für Dein Interesse.
Die englisch-sprachige Zeitschrift kenne ich nicht. Der Beitrag ist
interessant, Ich werde mich mal durcharbeiten.
Das Linux-Magazin werde ich nicht mehr am Kiosk bekommen.
Darum möchte ich auf Dein Angebot gerne eingehen.

Im Voraus schon mal eine dickes Dankeschön

Grusz aus der Enklave
Timothy Kesten
2006-08-04 14:34:29 UTC
Permalink
Post by Mathias Uebel
Hallo Timothy,
herzliche Dank für Dein Interesse.
Kein Problem
Post by Mathias Uebel
Die englisch-sprachige Zeitschrift kenne ich nicht. Der Beitrag ist
interessant, Ich werde mich mal durcharbeiten.
Das Linux-Magazin werde ich nicht mehr am Kiosk bekommen.
Darum möchte ich auf Dein Angebot gerne eingehen.
Okay - schicke ich Dir heute Abend als PM
Post by Mathias Uebel
Im Voraus schon mal eine dickes Dankeschön
s.o. ;-)

Timothy
Lesen Sie weiter auf narkive:
Loading...