Sonntag, 26. Mai 2013

LibreOffice Draw: Kreise auf einem Kreis anordnen

In LibreOffice Draw gibt es zwar inzwischen eine verbesserte Funktion, um Elemente auszurichten und zu verteilen, aber kein Hilfsmittel, um Kreise vernünftig auf einem Kreis anzuordnen.

Folgende Anleitung schafft Abhilfe:
Der Trick ist, dass zunächst einmal vier Kreise (die hier rot markierten) an den Außenkanten eines Quadrats angeordnet (oben, rechts, unten, links ausrichten) und dort mittig bzw. zentriert ausgerichtet werden. Die Kreise werden anschließend gruppiert und kopiert. Anschließend einfügen und um eine bestimmte Gradzahl drehen. In diesem Fall wurde das Verfahren zwei Mal wiederholt und die Gruppe jeweils um 30 ° gedreht, so dass am Ende der Eindruck von auf einem Kreis ausgerichteten Kugeln entsteht. Das Quadrat zum Ausrichten wird am Ende herausgelöscht. Im Prinzip lässt sich dies auch mit einer abweichenden Gradzahl und mit kleineren Kreisen durchführen.

Freitag, 29. März 2013

SimpleCode 2

Ich habe eine eigene Version von SimpleCode in PHP programmiert. Das ging überraschend einfach, wenn auch der Aufwand es zu coden exorbitant war. Ganz verstanden habe ich das Problem noch nicht...
<?PHP

  echo("Hallo Welt!");

?>
Das eigentliche Problem ist, dass ich noch nicht weiß, ob meine Version wirklich schon völlig funktioniert. Der Test hier sagt: Es funktioniert. Wenn ich den PHP-Code direkt posten würde, dann würde er in Blogger nicht angezeigt werden.

Sonntag, 24. März 2013

Die Funktion header()

Ich fange gerade erst an das erste PHP-Tutorial durchzuarbeiten, bin aber in die Verlegenheit gekommen, eine Umleitung zu programmieren. Ich möchte verhindern, dass ein Benutzer auf ein PHP-Skript zugreift, dass Daten eines anderen Skripts verarbeitet und ausgibt.

Im Augenblick behelfe ich mir mit folgendem Code:

if (empty($_POST)){
  header('Location: skript_1.php');
 };

Wahrscheinlich ist das so nicht direkt im Sinne des Erfinders, aber es funktioniert erst einmal.

Sonntag, 10. März 2013

Morse Code Palindrome

Das „Morse Code Palindrome“-Problem gehört zum „HP Code wars“-Wettbewerb 2013 und war eher sehr einfach zu lösen. Einmal galt es eine Botschaft in Morsecode zu übersetzen und dann die Botschaft auf ihre Palindrom-Eigenschaft hin zu untersuchen:
# HP Code wars 2013
# problem 12
# Morse Code Palindrome
# 6 points


def botschaft_decodieren(botschaft):
    """ Bekommt eine Botschaft und setzt sie
    in Morsezeichen um. Leerzeichen und Satzzeichen
    werden ignoriert. Kleinbuchstaben werden nicht
    erwartet oder beachtet.

    """
    morsecode = {"A": "•-", 
                 "B": "-•••",
                 "C": "-•-•",
                 "D": "-••",
                 "E": "•",
                 "F": "••-•",
                 "G": "--•",
                 "H": "••••",
                 "I": "••",
                 "J": "•---",
                 "K": "-•-",
                 "L": "•-••",
                 "M": "--",
                 "N": "-•",
                 "O": "---",
                 "P": "•--•",
                 "Q": "--•-",
                 "R": "•-•",
                 "S": "••••",
                 "T": "-",
                 "U": "••-",
                 "V": "•••-",
                 "W": "•--",
                 "X": "-••-",
                 "Y": "-•--",
                 "Z": "--••"}
    codierte_botschaft = []
    for a in botschaft:
        codierte_botschaft.append(morsecode.get(a,""))
    return "".join(codierte_botschaft)


def palindrom_testen(datensatz):
    """ Testet auf ein Palindrom und
    schickt den Datensatz dazu zum decodieren

    """
    botschaft = botschaft_decodieren(datensatz)
    if botschaft == botschaft[::-1]:
        return True
    else:
        return False


data = """ELEGIZED 
QUIRKILY 
MERCURY 
FACE A WINE 
HAPPY DAY 
FEVER REBEL 
SOPRANOS 
EMIT OLD UFO TIME 
PROTEIN POWDER 
ANNEXING 
ENJOIN 
. """


datensaetze = data.split("\n")
for datensatz in datensaetze:
    datensatz = datensatz.strip(" ")

    if not datensatz == ".":

        if palindrom_testen(datensatz):
            print ("{} is a MCP".format(datensatz))
        else:
            print("{} is *not* a MCP".format(datensatz))
Anders als einige andere Aufgaben des diesjährigen Wettbewerbs eher keine Herausforderung.

Webcrawler zur Websitenanalyse

Bei einer gestrigen Besprechung kam das Problem der Website-Wartung, der Behandlung toter Weblinks, des Aufspürens inzwischen falscher E-Mail-Adressen und - ich ergänze - defekter (Bild–)Dateien auf die Tagesordnung. Das Problem scheint „ungelöst“, jedenfalls wenn man dem Wikipedia-Artikel zu „Toten Weblinks“ glauben darf.

Erste Überlegungen

Ich hatte mir gestern überlegt an drei Stellen ansetzen zu können:
  1. Datenbank-Dump zur Website als CSV-Datei oder als SQL-Datei.
    Vorteil: Die Prüfung erfasst alle Seiten und nicht nur die, die öffentlich zugänglich sind, sondern auch die, die einst zugänglich waren und vielleicht noch von jemandem über ein Lesezeichen besucht werden.

    Nachteil: Per Hand muss zunächst ein Datenbank-Dump erzeugt werden. 
  2. Website im Laufenden Betrieb parsen.
    Vorteil: Ich erfasst die vollständig zugängliche Website und brauche keinen Datenbankzugriff.
    Nachteil: Einige Seiten werden übersehen.
  3. Ich generiere spezielle 404-Seiten, die eine Meldung per E-Mail an den Webadmin absetzen und zeitnah über einen möglichen Fehler der Seite informieren.
Tja.

Ausgabe eines Protokolls
Prüfung der Bilddateien auf dem Rechner
Prüfung der E-Mail-Adressen durch Test-E-Mail und Information an den Empfänger.
Prüfung der Links über Protokoll (Liste) zur manuellen Prüfung und automatischen Prüfung über Test auf 404- oder weitere Fehlerseiten...

Samstag, 9. März 2013

Family tree

Eigentlich könnte „Family tree“ ein schönes Problem sein, müsste man nicht eine durchaus komplexe Datenstruktur aufbauen, die sowohl aufsteigend, als auch absteigend durchsucht werden kann. Ich denke daran die Datenstruktur in ein Dictionary zu packen und die Auf- bzw. Abwärtssuche dann gesondert zu implementieren.

Ein- und Ausgabe

Ich erhalte als Eingabe folgende Datensätze:
9 
A + B : C , D , E . 
F + G : H . 
I + J : K , L . 
C + H : M , N . 
D + K : O . 
L + E : P , Q , R , S . 
N + Q : T , U , V . 
O + S : W , X . 
Y + H : Z . 
7 
K > O ? 
F > W ? 
B ^ U ? 
O ^ V ? 
A > Z ? 
F > Z ? 
X ^ Z ?

Die Ausgabe soll folgendermaßen aussehen:
K > O ? TRUE 
F > W ? FALSE 
B ^ U ? FALSE 
O ^ V ? TRUE 
A > Z ? FALSE 
F > Z ? TRUE 
X ^ Z ? FALSE 

Das Problem

Eine Visualisierung dient der ersten Orientierung:

Ein Stammbaum erzeugt aus den Anweisungen

Also gehen wir es an.

Room for an argument

Room for an argument“ war Problem 11 bei HP Code wars 2013 und es gab hierfür insgesamt 5 Punkte, was mir recht hoch erscheint, wenn man sich das Problem vergegenwärtigt. Insgesamt galt es bei einer Reihe von Texten aus „is“ ein „is not“ zu machen und umgekehrt.

Aus meiner Sicht ließ sich das mit Stringoperationen erledigen ohne jede Zeile gesondert zu parsen, womit ich direkt einen Gedankenfehler gemacht habe, weil ich dabei z. B. „This“ übersehen hatte. Folglich muss doch jede Zeile gesondert geparst werden bzw. müssen die Ersetzungen angepasst werden.
data = """7 
This is not an argument. 
An argument is an intellectual process. 
It is fair if you do not go. 
The ferris wheel is not working. 
A butterfly is beautiful, but litter is not. 
A lady discerns that which is not elegant from that which is. 
A lemur is a monkey and a grivet is a monkey but a chimp is not. """

for i, line in enumerate(data.split("\n")):
    if not i == 0:

        print(line)
        
        line = line.strip(" ")
        line = line.replace(" is not"," ")
        line = line.replace(" is"," is not")
        line = line.replace(" "," is")
        
        print(line,"\n")
Ob die es sich so gedacht haben? Alternativ müsste man jede Zeile in eine Liste aufsplitten und dann jedes Element und ggf. sein Folgeelement einzeln parsen und Änderungen auf einer Liste vornehmen. Die Lösung hier scheint mit vertretbar.

Dienstag, 5. März 2013

Hot Pad

Das „Hot Pad“-Problem von HP Codewars für 9 Punkte besteht eigentlich aus 2 Problemen. Im Rahmen des Problems erhalte ich von einem Keypad die Temperaturen je Taste und muss eine Liste der möglichen Kombinationen ausgegeben, die ein Anwender getippt haben könnte.

Problem

Die beiden Probleme sind folglich:

  1. Lies die Temperaturwerte ein und stelle fest, welche Taste einmal und welche ggf. zweimal gedrückt wurde. Es gibt eine durchschnittliche Temperatur und die Taste # muss zwingend zum Absenden gedrückt sein.
  2. Für die ermittelten Tasten muss ich anschließend alle möglichen Kombinationen ausgeben. Das ist ein Problem der Kombinatorik, wenn ich mich nicht irre.

Erste Annäherung

Beachten muss ich, dass die eingegebenen Codes eine Länge von 4 Ziffern haben und mit einem # abgeschlossen werden. Das Keypad sieht so aus:

1 2 3
4 5 6
7 8 9
* 0 #

Die Temperaturwerte für das Pad sehen u. a. so aus:

Datensatz 1
72.1
75.0
75.1
72.0
72.1
72.2
72.0
72.2
77.9
72.1
72.2
75.2

Datensatz 2

77.2
77.1
76.9
77.0
83.5
77.0
77.1
77.3
77.1
77.0
79.4
79.1

Die Temperaturen können schwanken, was beachtet werden muss! Denke daran, dass eine Taste einmal, zweimal, dreimal oder auch viermal gedrückt werden könnte; die Raute wird einmal gedrückt worden sein. Das Sternchen sollte eigentlich nicht gedrückt worden sein.

Counting Ones

Das Problem Nr. 9 „Counting Ones“ wird bereis als Spaßproblem („This program is a fun math problem!“) beschrieben. Es sind in einem Zahlenbereich von 1 bis einschließlich einer Zahl n alle Einser gezählt werden. Im Bereich bis 10 finden sich so z. B. zwei Einser und zwar bei 1 und einmal bei 10. Die Lösung des Problems, wenn man die Dateneingabe außen vor lässt, ist knapp zu formulieren:
def counting_ones(n):
    """ Soll alle Einser zwischen
    1 bis einschließlich n ermitteln

    """
    zahlen = [str(n) for n in range(1,n + 1)]
    zahlenreihe = "".join(zahlen)
    return zahlenreihe.count("1")


for i in [13,1,999,23,1111,9997,511]:
    print(counting_ones(i))
Schöne, einfache Probleme, zumindest die, die ich mir jetzt angesehen habe.

Distinct Letters

Das Problem 8 „Distinct Letters“ von HP Code wars 2013 für 4 Punkte ist eher eines der sehr einfachen Probleme. Es muss geprüft werden, ob ein Wort einen jeden Buchstaben des Wortes nur einmal verwendet.
data = """UNCOPYRIGHTABLE 
FLIPPER 
EXECUTABLE 
UNPROFITABLE 
QUESTIONABLY 
WINDOW 
TAMBOURINE 
. """


def daten_aufbereiten(data):
    """ Wandelt die Daten in Zeilen
    in einer Liste um und gibt die
    Liste zurück...

    """
    zeilen = data.replace(" ","").split("\n")
    if zeilen[-1] == ".":
        zeilen.pop()
    return zeilen


def distinct_letters_pruefen(wort):
    """ Muss prüfen, ob ein Wort einen
    Buchstaben mehr als einmal nutzt,
    dann ist der Rückgabewert
        FALSE
    sonst
        TRUE
    """
    if len(wort) != len(set(wort)):
        return False
    else:
        return True


woerter = daten_aufbereiten(data)

for wort in woerter:
    if distinct_letters_pruefen(wort):
        print("{} USES DISTINCT LETTERS.".format(wort))
    else:
        print("{} DOES NOT USE DISTINCT LETTERS.".format(wort))
Ich finde meine Lösung intelligent, weil kurz und richtige Ergebnisse geliefert werden ohne dass ich eine Prüfung je Buchstabe durchführen muss.

Selling Shirts

Die Aufgaben von HP Code wars 2013 sind online und die erste echte Aufgabe ist eine Sache für wenige Minuten:
""" Die Formel zur Berechnung des Profits lautet

    P = 8 * N – 95

    $ 8 pro verkauftem Shirt
    N   = die Zahl der verkauften Shirts
    95  = die Standgebühr
"""

profit = lambda n : 8 * n - 95

print(profit(31),end=" $")
Die Formel zur Berechnung des Profits kann gut mittels lambda-Operator ausgedrückt werden.

Sonntag, 3. März 2013

E-Mail-Versand mit Python

Das Thema E-Mail-Versand mit Python finde ich ganz spannend.

Brauchbare Ressourcen

Für den Versand von E-Mails mit Python haben sich folgende Ressourcen als ganz brauchbar herausgestellt:

Module:
  • Das Modul smtplib
  • Das Modul email
  • Das Modul ...

Referenzen:

Einfache Text-E-Mails

Text

Versand von HTML-E-Mails

Text

Versand von E-Mails mit Anhängen

Text

Name That Cow

Aus dem OpenClipart-Project
Das Name That Cow“-Problem klingt recht spannend, allerdings ist ein Problem, dass mir die Datentabelle mit den 5.000 „acceptable cattle names“ nicht zur Verfügung steht.

Problembeschreibung

Auf einer texanischen Rinderfarm soll für die Seriennummer einer Kuh eine Liste weiblicher Vornamen vorgeschlagen werden, wobei die Übersetzung sich am Tastenfeld eines Telefons orientiert, d. h. für die Zahl 2 kann im Vornamen der Buchstabe A, B oder C stehen.

Ich verwende meine Telefontastatur, wie ich sie auf meinem Telefon vorfinde.

1 2
ABC
3
DEF
4
GHI
5
JKL
6
MNO
7
PQRS
8
TUV
9
WXYZ

Diese Änderung sollte unproblematisch sein. Die richtige Funktionsweise meines Programms kann später über die Tastatur überprüft werden. Wirft mir das Programm für den Code 23353 z. B. den Vornamen Adele aus, dann kann ich prüfen, ob die Buchstaben jeweils in den den Zahlen zugeordneten Buchstabenlisten enthalten sind.

Folglich stellt sich folgendes Problem: Schreibe ein Programm das für die Nummer eines Rinds alle validen Namen ausgibt, die für diese Nummer generiert werden können. Sonst lautet die Ausgabe „No matching names found“. Seriennummern dürfen maximal 9 Zahlen lang sein. Das Programm endet, wenn eine 0 eingegeben wird.

Vorüberlegungen

Die Aufgabenstellung führt zu folgenden Vorüberlegungen:
  • Die Seriennummer eines Rindes darf keine 0 oder 1 enthalten, weil ich diesen Ziffern keinen Buchstaben zuweisen kann.
  • Die Verwendung des RE-Moduls bietet sich m. E. an. Die dynamische Generierung eines RE-Ausdrucks ist m. E. erforderlich. (Ging einfacher)
  • Möglicherweise macht es Sinn die Vornamen in Listen entsprechend der Länge aufzuteilen. Das könnte helfen die Performanz zu verbessern.

Datendatei aufbauen

Ich habe mir - leider manuell - aus der Wikipedia eine Liste mit - laut Wikipedia - 1.761 weiblichen Vornamen (Stand März 2013) geholt und aufbereitet. Aus dieser Liste soll das Programm geeignete Namen auswählen.

Die Vornamensliste wird aus dem Quelltext der Kategorienseiten heraus aufgebaut:
with open("Männlicher Vorname.txt") as f, open("Männliche Vornamen.txt","w") as g:

    for line in f:
        
        line = line.replace("\n","")
        # title="Zsuzsanna"
        if "title" in line:

            line = line.split('title="')[1]
            vorname = line.split('"')[0]

            vorname = vorname.replace(" (Vorname)","")
            vorname = vorname.replace(" (Name)","")

            print(vorname)
            vorname += "\n"
            g.write(vorname)
Das war gerade etwas Handarbeit, aber noch im Rahmen und einfacher und schneller als sich beim Pywikipediabot einzulesen.

Quelle: Deutschsprachige Wikipedia (2. März 2013)


Die entsprechende Übersicht mit denn männlichen Vornamen bietet - laut Wikipedia - 3.390 männliche Vornamen (Stand März 2013).

Quelle: Deutschsprachige Wikipedia (2. März 2013)


Aus beiden Listen kann man die benötigte Vornamensliste aufbauen.

Ein- und Ausgabe

Die Eingabe stellt man sich bei HP in etwa in folgender Form vor:
Program Input
232
252473
727225
0

Program Output
Possible names for #232 are: Ada
Possible names for #252473 are: Blaise, Claire
Possible names for #727225 are: Pascal
Sieht leistbar aus.

Mein Programm

Das re-Modul kommt hier nicht zum Einsatz, weil es Treffer für Strings findet, nicht aber in Listen. Eigentlich hätte ich gerne alle Listenelemente bekommen und muss daher - gefühlt - einen kleinen Umweg nehmen.
# Code wars IV
# Problem 9: Name That Cow


def codeliste_aufbauen(data):
    ''' Bekommt die Daten und gibt
    einzelne Codes in einer Liste zurück

    '''
    data = data.replace(" ","")
    data = data.split("\n")
    if data[-1] == "0":
        data.pop()

    return data


def ergebnis_ausgeben(code,namensliste):
    ''' Erledigt die Ausgabe in der Konsole

    '''
    if namensliste:        # Wahr, wenn die Liste nicht leer ist!
        print("Possible names for #{} are: {}.\n".format(code,", ".join(namensliste)))
    else:
        print("No matching names found for #{}.\n".format(code))


def vornamenliste_aufbauen():
    ''' Holt sich die Daten aus den Vornamenslisten

    Offene Probleme:
    [  ]   Es werden alle Namen geholt, nicht nur die, die
           auch möglich sind, z. B. < 10 oder ohne Leerzeichen
           und ohne Sonderzeichen
    '''
    vornamen = []
    dateien = ["Männliche Vornamen.txt","Weibliche Vornamen.txt"]
    
    for datei in dateien:
        with open(datei) as f:
            for line in f:
                line = line.replace("\n","")
                vornamen.append(line)
    vornamen.sort()

    # Trainingsmaterial
    '''
    Wähle code = 382 -> EVA
    vornamen = ['Bob', 'Eda', 'Eda', 'Eka', 'Eka', 'Eva', 'Ewa', \
                'Ida', 'Iga', 'Ina', 'Ina', 'Ira', 'Ira', 'Isa', \
                'Isa', 'Iva', 'Lea', 'Lia', 'Mia', 'Nea', 'Nia', \
                'Noa', 'Néa', 'Oda', 'Ola', 'Ola', 'Ona', 'Oya', \
                'Pia', 'Ria', 'Rob', 'Uta']
    '''
    return vornamen


def treffer_suchen(code,vornamen):
    ''' Suche alle Treffer in der Vornamensliste für
    den dekodierten code und gibt die Liste zurück
    '''
    codeliste = {"2":["A","B","C"],
                 "3":["D","E","F"],
                 "4":["G","H","I"],
                 "5":["J","K","L"],
                 "6":["M","N","O"],
                 "7":["P","Q","R"],
                 "8":["T","U","V"],
                 "9":["W","X","Y","Z"]}

    results = [] # Nimmt die Ergebnisse auf
    
    if "1" in code or "0" in code:
        # Der 1 sind keine Buchstaben zugeordnet
        return []
    else:

        for vorname in vornamen:

            match = True
            
            if len(vorname) != len(code):
                # Kann nicht passen
                pass
            elif " " in vorname:
                # Kann nicht passen
                pass
            else:
                for letter, i in zip(list(vorname),list(code)):
                    if not letter.upper() in codeliste[i]:
                        match = False
                        # Muss nicht weiterprüfen
                        break
                        # Nächster Vorname!
                    
                
                if match == True:
                    results.append(vorname)
        
        return results


def main():
    """ Mainfunktion

    """
    data = """114
    2662
    6886
    42664
    42662
    58473
    58472
    5642662
    382
    232
    252473
    727225
    6424235
    0"""

    # Übungsmaterial
    # data = """382"""

    # Daten aufbereiten
    codes = codeliste_aufbauen(data)
    vornamen = vornamenliste_aufbauen()

    # Treffer suchen
    for code in codes:
        treffer = treffer_suchen(code,vornamen)
        ergebnis_ausgeben(code,treffer)


if __name__ == "__main__":
    main()
Zu verbessern wäre hier wohl die Funktion treffer_suchen(code,vornamen). Mich würde interessieren, ob es eine bessere Lösung - z. B. mittels Datenbank oder re-Modul geben könnte. Eine Musterlösung von HP gibt es für die frühen Code wars leider noch nicht.

Verbesserungsvorschläge und Feedback

Ich habe im Python-Forum und zwar hier um Kritik gebeten und bekommen.

Samstag, 2. März 2013

CSI Crime Lab

Bei Aufgabe „CSI Crime Lab“ von HP Codewars VII sollte eine DNA-Prüfung implementiert werden, was mich etwas Zeit und Energie gekostet hat, weniger aufgrund der Komplexität der Aufgabe, als vielmehr, weil ich mir die Datenstruktur mit 2 dictionaries und dort u. U. hinterlegten Listen erst einmal vorgestellen musste.
# CodeWars VII Problems
# CSI Crime Lab

def suspects_aufbereiten(suspects):
    ''' Soll die Daten aufbereiten und in
    ein Wörterbuch packen
    '''
    personen = {}

    suspects = suspects.split("\n")
    for suspect in suspects:
        name,DNA = suspect.split(": ")
        personen[name] = DNA
    return personen


def daten_aufbereiten(scene):
    ''' Soll die Daten vom Tatort
    aufbereiten
    '''
    data = {}
    
    scene = scene.split("\n")
    for datum in scene:
        datum = datum.replace("On ","")
        ort,dna_spur = datum.split(": ")
        data[ort] = dna_spur.split(", ") # Werte sind in einer Liste (!)
    return data


def spuren_prüfen(suspects,spuren):
    ''' Vorgaben von HP
    a.) Es gibt nur einen Verdächtigen, dessen Name
        zurückgegeben werden muss
    b.) Falls kein positiver Treffer, dann ist die Ausgabe
        des Programms "NO MATCH", berücksichtigen
    c.) Positiver Treffer nur, wenn ein Verdächtiger
        DNA-Spuren an allen Tatorten hinterlassen hat
    d.) Es kann je Tatort mehrere DNA-Spuren geben

    Achtung - meine Ergänzung
    e.) Das Programm liefert nur den ersten Treffer mit
        Spuren an allen Tatorten zurück. Es wird nicht
        geprüft, ob das auch für andere Verdächtige
        zutrifft. Das könnte eine Fehlerquelle sein!
    '''

    # Beispieldaten
    # Larry King: GACTAATAACTTCATATATACACAGGTTAC
    # revolver: GACTATTC, GACTAATA

    # Was muss getan werden?
    # Prüfe, ob eine der Spuren in der DNA-Sequenz enthalten ist

    results = {}
    
    for suspect in suspects:

        results[suspect] = 0
        
        dna = suspects[suspect]

        for spurensatz in spuren:   # Spuren im Datensatz

            match = False

            for spur in spuren[spurensatz]:

                if spur in dna:
                    match = True

            if match == True:
                results[suspect] += 1
    
    # Rückgabe: Name eines Verdächtigen oder "" (= False!)
    keys = results.keys()
    for key in keys:
        if results[key] == 3:
            return key
    else:
        return "NO MATCH"


suspects = """Larry King: GACTAATAACTTCATATATACACAGGTTAC
Paula Abdul: GACTATTCATCATAGATAGACAGTACCTAA
Charlie McCarthy: GATTCATTGACATACATACATTAGAGTTCA"""

scene = """On revolver: GACTATTC, GACTAATA
On door: GACTAAT, CATAGAT
On phone: CATACATT, ATTAGAG, ATAGATAG"""

# Daten aufbereiten
suspects = suspects_aufbereiten(suspects)

spuren = daten_aufbereiten(scene)

# Schick die Daten durch die Datenbank
suspect = spuren_prüfen(suspects,spuren)

# Ermittle das Ergebnis
if not suspect == "NO MATCH":
    print("The suspect is: {}".format(suspect))
else:
    print("NO MATCH")
Das hätte wohl etwas den Zeitrahmen gesprengt und ich bin mir auch noch nicht sicher, ob das wirklich die beste Implementierung ist. Produktiv würde ich das jedenfalls nicht zum Einsatz empfehlen, weil nur ein erster Verdächtiger benannt wird, nicht alle möglichen Täter ermittelt werden.

Spellbinder

Aufgabe Spellbinder war Problem 2 bei HP Code wars II. Das Problem ist recht einfach, wie mir scheint:
import string


def buchstaben_ermitteln(eingabe,ausgabe):
    ''' Ermittelt den Buchstaben und stellt fest,
    ob es Groß- (capital) oder Kleinbuchstabe (lower-case)
    ist...
    '''
    for a, b in zip(eingabe,ausgabe):

        if a != b:
            letter = b
            break

    if letter in string.ascii_uppercase:
        type = "capital"
    else:
        type = "lower_case"

    return type,letter
    

data = """fountain mountain
pet pen
check chuck
Mike bike"""

data = data.split("\n")

for line in data:
    ''' Ausgabe
    '''
    ausgabe,eingabe = line.split(" ")

    type,letter = buchstaben_ermitteln(eingabe,ausgabe)
    
    print('Ripping the {} "{}" from his shirt, Letterman'.format(type,letter)) 
    
    print('changes "{}" back to "{}".'.format(eingabe,ausgabe))
Keine Herausforderung für 2 Punkte.

Palindromtest

Problem Nr. 3 bei HP Code Wars II war einen Palindromtest zu implementieren. Ich hatte mich mit dem Thema Palindrom bereits einmal befasst. Im Kern keine Herausforderung, aber eine nette Übung.
def palindrom_testen(word):
    ''' Teste auf Palindrom

    '''
    for zeichen in [" ",",",".",";","'","\n"]:
        word = word.replace(zeichen,"")
        
    if word.lower() == word.lower()[::-1]:
        return True
    else:
        return False


data = """1881 
Madam, I'm Adam. 
Lisa Bonet ate no basil. 
Taste penne pasta."""

data = data.split("\n")

for line in data:
    # Prüfung und Ausgabe
    print(line)

    if palindrom_testen(line):
        print("\tis a palindrome.")
    else:
        print("\tis *not* a palindrome.")
Den heutigen Teilnehmern an den HP Code wars viel Erfolg. Ich verfolge es gerade über Facebook.

Dienstag, 26. Februar 2013

HTML in Blogger posten

Ich habe hier nun ein nettes kleines Tool gefunden, das mir HTML-Code in SimpleCode umwandelt, der in Blogger angezeigt werden kann. Problematisch scheinen dabei die Zeilenumbrüche zu sein und die eckigen Klammern, also < und >. Daraus ergibt sich für mich, dass sich das (bestimmt) auch einfach als Desktop-Tool mit GUI umsetzen lässt, denn die Korrekturen scheinen momentan nicht der Rede wert.

HTML-CODE Zeichen
< &lt;
> &gt;
\n <br />

Ferner muss oben und unten ein code-Tag ergänzt werden, wobei die Zeile ansonsten freibleibt. Auch das ist in Python nicht kompliziert umzusetzen.

Samstag, 23. Februar 2013

Zeilenumbruch in HTML

Es gibt aktuell drei Möglichkeiten einen Zeilenumbruch in HTML zu codieren, allerdings ist nur die erste Variante mit &shy; auch wirklich zu empfehlen, weil es erstens ein Trennstrich einfügt und zweitens nicht proprietär ist.

Freitag, 22. Februar 2013

Logelei

Im heutigen Zeit Magazin (21. Februar 2013, Nr. 9, S. 76) habe ich ein Rätsel in der Rubrik „Logelei“ gefunden.

Im Kern geht es darum die sechs Wörter TETRAEDER, WUERFELOKTAEDERDODEKAEDER, IKOSAEDER und KUGEL so in einem Gitter von 16 Zellen zu verteilen, dass sie darin Platz finden. Der Start eines Wortes im Gitter ist beliebig und der nächste Buchstabe kann dann waagerecht, senkrecht oder diagonal benachbart sein. Buchstaben dürfen dabei in einem Wort mehrfach verwendet werden. Die Lösung sei eindeutig, so die Erklärung.

Das Gitter ist vorstrukturiert und sieht wie folgt aus:

Das Gitter mit drei eingetragenen Buchstaben
Als Tipp ist formuliert, dass, kommt ein Buchstabe mehrfach vor, dann jedes Mal derselbe Buchstabe im Gitter verwendet werden soll.

Arbeit an einer Lösung

Ein erster Zugriff ergibt, dass es insgesamt 14 Buchstaben gibt, die verteilt werden müssen und zwar:

A, D, E, F, G, I, K, L, O, R, S, T, U, W.

Da das T bereits zweimal gesetzt ist, darf nur ein weiterer Buchstabe ebenfalls zweimal im Gitter vorkommen. Es stellt sich die Frage, ob T besonders häufig vorkommt oder es eine andere Besonderheit gibt.

Zu den Buchstabenhäufigkeiten:

A   4
D   6
E  13
F   1
G   1
I   1
K   4
L   2
O   3
R   6
S   1
T   3
U   2
W   1


Also T ist nicht so häufig, d. h. ADEK und R sind alle häufiger enthalten.

Andererseits kommt T nur in zwei Wörtern vor und zwar in TETRAEDER und OKTAEDER.

Ich habe mir einmal die Bindungen visualisiert, was folgendes Bild ergibt:
Ein Graph mit den Buchstaben und den Nachbarn
Problem ist hier aus meiner Sicht, dass T und E eigentlich doppelt eingetragen werden müssen. T ist bereits doppelt gesetzt und E hat laut Plan 8 Nachbarn, nämlich L, GUDATR und F, das E oben rechts in der Ecke hat aber nur 3 mögliche Nachbarn.


Lösung

Inzwischen ist die Lösung im Netz verfügbar, die wie folgt aussieht:

T
F
L
E
R
E
U
G
D
A
K
W
S
O
T
I

Donnerstag, 14. Februar 2013

CSS

Ich schaue mir augenblicklich etwas CSS an. Ich habe gestern mal eine Website in LibreOffice Draw entwickelt und Gestaltungselemente verbaut und somit ein Mockup gestaltet. Die Umsetzung in CSS und das Erlernen der notwendigen CSS-Elemente stelle ich mir momentan noch schwierig vor.

Im Prinzip stelle ich mir vor, dass CSS etwas wie die Formatvorlagen bei LibreOffice Writer sind. Mein Problem hierbei ist, dass ich noch nicht wirklich durchblicke, ob ich den Sachverhalt damit im ersten Schritt adäquat verstehe.

Ziel der ganzen Übung ist es ein eigenes Wordpress-Theme zu schreiben, dass ich dann später mit Inhalten füllen kann.

Literatur und Ressourcen

Folgende Ressourcen habe ich mir angesehen:

CSS

  • Kai Laborenz: CSS-Praxis. Browserübergreifende Lösungen. Bonn: Galileo Press 42006. ISBN 978-3-89842-765-4
  • Florence Maurice: Jetzt lerne ich CSS3. Modernes Webdesgin verstehen und anwenden. München: Markt+Technik Verlag 2012. ISBN 978-3-8272-4745-2

Wordpress-Theme


Erste Gehversuche

Hier muss noch Text hin.


Mittwoch, 13. Februar 2013

Ringworld

Ringworld (Problem 2, 2012) für 3 Punkte - bei manchen Aufgaben von HP Codewars versteht man die Punkteverteilung nicht. Ringworld ist eine der eher sehr einfachen Aufgaben:
pi = 3.14159265

for data in [(95000000,997000),(92955887.6,131072)]:

    ringworld_radius = data[0]
    ringworld_width = data[1]

    result = 2 * pi * ringworld_radius * ringworld_width

    result_earths = result / 196935000
        
    print("{} EARTHS".format(int(result_earths)))
Einzige Schwierigkeit bei der Aufgabe sind die verschiedenen Einheiten, die man leicht überließt, so dass es zu Fehlern im Programm kommen kann.

Während die Oberfläche der Erde in Millionen Quadratmeilen (million square miles) angegeben ist, werden der Radius und die Weite der Ringworld in Meilen angegeben (miles). Ist das erfasst, ist die Aufgabe mehr als simpel.

Montag, 11. Februar 2013

Secret Code

Eine einfache Verschlüsselungsübung, die ich mir noch einfacher gemacht habe. Das Skript ist zugleich Lösung für eine Aufgabenstellung des HP Codewars 2001:
data = """Hewlett-Packard Company, Year 2000 Financial Report
end"""

data = data.split("\n")

for line in data:
    ''' Das könnte man auch in eine Funktion
    packen, jedenfalls wird hier die Message
    verschlüsselt...

    '''
    # Um es mir einfach zu machen habe ich
    # zusätzlich zu den Großbuchstaben
    # auch die Kleinbuchstaben ins Code-
    # Wörterbuch aufgenommen...
    codes = {"A":"*",
             "a":"*",
             "E":"$",
             "e":"$",
             "I":"#",
             "i":"#",
             "O":"!",
             "o":"!",
             "U":"%",
             "u":"%",
             "Y":"^",
             "y":"^"}
    if line == "end":
        pass
    else:
        for key in codes.keys():
            line = line.replace(key,codes[key])        

        print(line)
Alternativ hätte man die Message Buchstabe für Buchstabe durchlaufen können und prüfen können, ob Buchstabe.upper() im Wörterbuch steht und die Daten dann entsprechend z. B. an eine Liste anhängen können. Das Ergebnis würde dann codiert zurückgegeben werden. Für 2 Punkte darf man es sich wohl einfacher machen.

Graphentheorie

Im Netz gibt es mehr oder weniger brauchbare Videos zum "kürzeste Wege-Problem", zu dessen Lösung der Dijkstra-Algorithmus genutzt wird.

Ein Graph sieht zu Beginn so aus:
Abb. 1: Gefunden werden soll die kürzeste Weg zwischen O und T.

Die Lösung sieht dann etwa so aus:
Abb. 2: Das Problem ist gelöst. Die kürzeste Route ist O, A, B, D, T.
Im Netz habe ich zu diesem Thema auf Anhieb ein Video gefunden:


Eine Beispielimplementierung in Python zum Algorithmus findet sich u. a. hier.

Password Analyzer

Ein Passwort hinsichtlich seiner Qualität zu prüfen, wie es bei HP Codewars 2007 (Problem 7) gefordert war, ist keine Herausforderung. Die Funktion war sehr schnell gecodet:
def passwort_pruefen(passwort):
    ''' Prüft die Qualität eines passworts
    und kann 4 Qualitätsstufen als String
    zurückgeben; die Prüfung umfasst folgende
    Punkte:
    * Länge korrekt len(pw) >= 8?
    * wenigstens ein Großbuchstabe enthalten?
    * wenigstens eine Ziffer enthalten?
    
    '''
    qualitaet = 0
    
    # Ist die Länge korrekt, d. h. >= 8?
    if len(passwort) >= 8:   
        qualitaet += 1
    # Ist wenigstens ein Großbuchstabe enthalten?
    for a in passwort:       
        if a.isupper():
            qualitaet += 1
            break
    # Ist wenigstens eine Zahl enthalten?
    for a in passwort:       
        if a.isdigit():
            qualitaet += 1
            break
    
    # Rückgabe
    if qualitaet == 0:
        return "WEAK"
    elif qualitaet == 1:
        return "ACCEPTABLE"
    elif qualitaet == 2:
        return "GOOD"
    else:
        return "STRONG"


passwoerter = ["lizard","aardvark","Aardvark","Aardvark77"]

for passwort in passwoerter:
    
    # eingabe = input("Enter your password: ")
    print("Enter your password: {}".format(passwort))
    print("This password is {}.\n".format(passwort_pruefen(passwort)))
Auch wenn ich aus Bequemlichkeit auf die Eingabe von Passwörtern verzichtet habe und sie aus einer Liste beziehe, bleibt mein Fazit: Keine Herausforderung. Einige andere Aufgaben aus dem Jahr sind schon eine härtere Nuss.

Sonntag, 10. Februar 2013

Minelayer

Bei HP Codewars war 2010 als Problem 7 ein Minelayer zu coden. Ich habe damit jetzt einige Zeit verbracht das Problem zu verstehen und einen tüchtigen Ansatz zu entwickeln. Hauptproblem bei meinem Skript ist m. E. momentan die Funktion (minenfeld_zahlen_eintragen(felder)), die die Zahlen einträgt, weil ich es persönlich als umständlich wahrnehme, wie das codiert ist.

Ein großer Fortschritt gegenüber meinen bisherigen Ansätzen ist jedenfalls, dass das Spielfeld als eine Liste von Elementen codiert ist und erst in der Ausgabe als Feld (Höhe x Breite) erscheint. Die Repräsentation als Liste ist m. E. einfacher als eine Liste bestehend aus einzelnen Listen zu verwalten.

Hier das Skript:
import random

def minenfeld_erzeugen(breite,hoehe,minen):
    ''' Diese Funktion erzeugt das Spielfeld
    '''
    felder = minen * "*" + (breite * hoehe - minen) * "0"
    felder = [a for a in felder]
    random.shuffle(felder)
    return felder


def minenfeld_ausgeben(felder,breite):
    ''' Das Minenfeld ausgeben
    '''
    
    for position,feld in enumerate(felder,1):
        if feld == "0":
            print(".",end=",")
        else:
            print(feld,end=",")
        if position % breite == 0:
            print()


def minenfeld_zahlen_eintragen(felder):
    ''' Beispiel

    ....    ..11
    ...*    111*
    *...    *322
    *.*.    *3*1

    1 2 3
     \|/ 
    4-X-6
     /|\ 
    7 8 9
    
    '''
    for position,feld in enumerate(felder):

        if feld == "*":
            pass   # Da ist eine Mine drauf
        else:
            
            feld = int(feld)
            '''
            1 2 3
             \|/ 
            4-X-6
             /|\ 
            7 8 9
            '''

            positionen = []
            
            ''' Problem

            Im Augenblick werden Sachen fälschlich ausgewertet,
            m. E. gibt es 5 Sonderfälle, die abgefangen werden müssen

            B CCCC D
            
            A XXXX E
            A XXXX E

            '''
            
            # Fall B
            if position == 0:
                positionen = [position + 1,             # 6
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall A
            elif position % breite == 0:
                positionen = [position - breite,        # 2
                              position - breite + 1,    # 3
                              position + 1,             # 6
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall D
            elif position == breite -1:
                # Muss hier stehen, weil sonst die Bedingng
                # bei Fall C das hier 'überschreibt'
                # War zuvor Ursache für einen Bug
                positionen = [position - 1,             # 4
                              position + breite - 1,    # 7
                              position + breite,]       # 8
            # Fall C
            elif position < breite:
                positionen = [position - 1,             # 4
                              position + 1,             # 6
                              position + breite - 1,    # 7
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall E
            # Hier hatte ich Pkt. (%) vor Strich uebersehen
            elif (position + 1) % breite == 0:
                positionen = [position - breite - 1,    # 1
                              position - breite,        # 2
                              position - 1,             # 4
                              position + breite - 1,    # 7
                              position + breite]        # 8
            # Fall X
            else:
                positionen = [position - breite - 1,    # 1
                              position - breite,        # 2
                              position - breite + 1,    # 3
                              position - 1,             # 4
                              position + 1,             # 6
                              position + breite - 1,    # 7
                              position + breite,        # 8
                              position + breite + 1]    # 9

            for item in positionen:
                try:
                    # print(item,felder[item])
                    if felder[item] == "*":
                       feld += 1
                except IndexError:
                    pass
        
        felder[position] = str(feld)
        
    return felder


# Eigentlich

breite = 30
hoehe = 15
minen = 60

felder = minenfeld_erzeugen(breite,hoehe,minen)

felder = minenfeld_zahlen_eintragen(felder)

minenfeld_ausgeben(felder,breite)
Hauptprobleme in der Entwicklung waren:

  1. Die Abhandlung der verschiedenen Fälle war problematisch, weil ich die if-Bedingungen zunächst von A nach E aufgelistet habe und somit einzelne Bedingungen nicht geprüft wurden, weil eine Bedingung zuvor erfüllt war. Dies führte zu Fehlern beim Minen legen.
     
  2. Die verschiedenen Fälle zu umgehen ist mir nicht gelungen. Ich hätte gerne gehabt, dass die Fälle einfacher abzufangen gewesen wären, um mir unnötige Tipparbeit zu ersparen. Eine sinnvolle Lösung habe ich hier aber nicht gefunden.
     
  3. Eine objektorientierte Umsetzung drängt sich hier m. E. auf. Die drei Funktionen scheinen alle von einer anzulegenden Klasse Spielfeld abhängig.
     
Abschließend müsste eine Spielroutine implementiert werden, dass leere Felder und angrenzende Zahlen aufdeckt und so ein Spielen ermöglicht.

Nachtrag

Ich habe mir gerade die offizielle Musterlösung in Java angesehen, die von Don Brace stammt. Im entsprechenden Code-Abschnitt, der gewisse Ähnlichkeiten zu meinem Ansatz aufweist, schreibt er:
/*
  * This could be better optimized, but I leave it up to you :) */

Zumindest kommt er zum gleichen Ergebnis wie ich.

Freitag, 8. Februar 2013

QWERTY Sort

Bei Problem 7 bei HP Codewars war 2011 ein „QWERTY Sort“ zu implementieren, wofür die Teilnehmer damals 7 Punkte bekamen. Die Besonderheit bei diesem Sortieralgorithmus ist, dass eine Liste von Elementen nicht alphabetisch sortiert wird, sondern die Sortierung der Buchstabenfolge auf einer Tastatur mit US-amerikanischem Layout (Z und Y sind vertauscht) folgen soll.

Die Begründung für diese abwägige Aufgabenstellung ist ziemlich schräg, aber das Problem ist lösbar, wenn es auch etwas länger gedauert hat:
# URL http://www.hpcodewars.org/past/cw14/problems/ProblemSet2011Final.pdf
# Problem: 7. QWERTY Sort (7 P)

# Sortierschluessel
ordnung = [a for a in "QWERTYUIOPASDFGHJKLZXCVBNM"]

data = """ARREST
SUBDIVISION
DISCONTENT
SUPERIOR
TOPOLOGY
DEBUNK
APPENDIX
SUBDUE
TRUNK
."""


def liste_sortieren(liste,ordnung):
    ''' Diese Funktion erledigt die Sortierung
    und gibt die sortierte Liste zurueck

    '''
    while True:
        # Wenn die Liste sortiert ist,
        # bleibt das beim Durchlauf auf True
        sortiert = True
        
        # Iterier über die Liste
        for i in range(len(liste)):
            # Prüfe 2 benachbarte Listenelemente,
            # ihre Buchstaben von vorn nach hinten,
            # wenn die Buchstaben gleich, dann weiter
            # sonst
            # a.) bekomme für den Buchstaben1 den Listenindex,
            # b.) bekomme für den Buchstaben2 den Listenindex,
            # wenn a > b, dann tausche
            # sonst bleibt es so
            try:
                for id in range(len(liste[i])):
                    
                    if liste[i][id] == liste[i+1][id]:
                        pass
                    else:
                        if ordnung.index(liste[i][id]) > ordnung.index(liste[i+1][id]):
                            # print("Tausche!",liste[i],liste[i+1])
                            # print("Pruefe gerade:",liste[i][id],liste[i+1][id])
                            # Dreh die beiden Elemente in der Liste um
                            liste[i], liste[i+1] = liste[i+1], liste[i]
                            # print("Getauscht:",liste[i],liste[i+1])
                            # Liste war noch nicht sortiert (!)
                            sortiert = False
                        break  
            except IndexError:  # Ende der Liste erreicht
                pass
            # gehe zum nächsten Listenpaar

        if sortiert == True:
            return liste             # Brich ab!!!
            # Normal break, dann Ausgabe, hier return,
            # weil in eine Funktion gepackt


# Bekomm die Werte
data = data.split("\n")
liste = []
for zeile in data:
    if not zeile == ".":
        liste.append(zeile)


# Liste soll sortiert werden
# Da es nur "eine" Liste gibt,
# brauche ich die nicht an die Funktion zu binden,
# also nicht liste = liste_sortieren(liste,ordnung)
liste_sortieren(liste,ordnung)

# Elemente der Liste werden ausgegeben        - - - Ausgabe
for item in liste:
    print(item)
Naja, noch frage ich mich, ob ein alternatives Sortierverfahren die Aufgabe performanter lösen könnte. Im Kern kopiere ich hier eine Abwandlung des Bubblesort-Algorithmus, wobei meine Implementierung nicht sehr performant ist, weil - wie mir scheint - nicht jeder mögliche Tausch vorgenommen wird.

Dirt Simple Calculator

Für HP Codewars war 2009 als Problem 4 ein schmutziger, einfacher Rechner zu programmieren. Simple, weil die Division nicht unterstützt wird und schmutzig, weil die Punkt-vor-Strich-Rechnung zu ignorieren war:
def term_auswerten(zeile):
    ''' Die Funktion wertet den Term aus,
    Problem hierbei ist,
    a.) dass die Punkt-vor-Strich-Regel
        ausgeklammert ist, d. h.
    b.) der Term von links nach rechts
        auszuwerten ist
    eval() scheidet aus erst einmal aus;
    ein komplexerer Zugriff ist erforderlich
    

    '''
    zeile = zeile.replace(" =","")  # = brauche ich nicht
    zeile = zeile.split(" ")
    term = ""
    
    for a in zeile:

        if a in ["*","+","-"]:
            term = str(eval(term))
            term += a            
        else:
            term += a


    print(eval(term))


data = """28 - 7 * 3 =
13 * 4 + 8 * 2 + 1 =
4 + 3 * 52 =
0 ="""

zeilen = data.split("\n")

for zeile in zeilen:

    if zeile == "0 =":
        pass
    else:
        term_auswerten(zeile)
Die Aufgabe ließ sich schnell lösen, wenn ich auch für die Möglichkeit zur interaktiven Entwicklung dankbar bin. Ich mache noch zu viele Fehler...

Donnerstag, 7. Februar 2013

Card Counting

„Card Counting“ für 7 Punkte als Teil von HP Codewars 2012. Das Lösungsskript - weder funktional noch objektorientiert lautet:
data = """13
5D KC 3C 3D
10C 7D 4C AH
10S JH 6C 10D
7S 2H JS QH
2C 7C KD 8C
QD QS KH 2S
3H 4S 5H JD
3S 4H 5S 6H 
8H 6S 7H QC
9C 8S 9H 6D
2D 8D AS 5C
JC AH 4D KS
AH 9S 10H 9D"""

# Erste Zeile der Daten auswerten
zeilen, data = data.split("\n",1)

# Daten in Zeilen zerlegen
datenzeilen = data.split("\n")

# Karten aufbauen
ranks = ["2","3","4","5","6","7","8","9","10","J","Q","K","A"]
suits = ["S","H","D","C"]

kartenset = {}  # Das ist ein normales Kartenspiel
kartenkeys = [] # Darueber wird die Ausgabe gesteuert

for suit in suits:
    for rank in ranks:
        karte = rank + suit
        kartenset[karte] = 0
        kartenkeys.append(karte)

# Die Daten auswerten
for zeile in range(int(zeilen)):
    kartenzeile = datenzeilen[zeile].strip(" ").split(" ")
    # print(kartenzeile)
    for karte in kartenzeile:
        kartenset[karte] += 1

# Ausgabe
print("Missing cards: ")
for karte in kartenkeys:
    if kartenset[karte] == 0:
        print(karte,end=" ")

print("\n")

print("Extra cards: ")
for karte in kartenkeys:
    if kartenset[karte] > 1:
        print("{} ({})".format(karte,kartenset[karte]-1),end=" ")   # - 1

print()
Die erwartete Ausgabe stimmt. Es ist zu beachten, dass bei den zusätzlichen Karten die Anzahl der Karten im Deck um 1 verringert wird, weil ja eine Karte im Deck enthalten sein soll.

Mittwoch, 6. Februar 2013

List maker

Eine nette kleine Aufgabe bei HP Codewars aus dem Jahr 2010 und die Lösung:
def abarbeiten(data):
    ''' Bekommt als data eine Reihe von Befehlen
    und fuehrt Aufgaben auf einer Liste aus

    '''
    liste = []
    zeilen = data.split("\n")
    for line in zeilen:

        line = line.strip(" ")
        
        if line == "SHOW":
            print(" ".join(liste))
        else:
            befehl, inhalt = line.split(" ",1)

            if befehl == "ADD":
                # ADD X – puts item X at the end of the list
                liste.append(inhalt)
            elif befehl == "INSERT":
                # INSERT X N – puts item X into the list just before item N
                item, item_alt = inhalt.split(" ")
                position = liste.index(item_alt)
                liste.insert(position,item)
                
            elif befehl == "REMOVE":
                # REMOVE X – removes item X from the list
                liste.remove(inhalt)

                
data = """ADD NEVER
ADD COLLAR 
INSERT CAT COLLAR 
ADD DOG 
ADD SCARES
INSERT ANYTHING CAT
REMOVE CAT
INSERT THAT SCARES
REMOVE COLLAR 
INSERT WEAR ANYTHING
REMOVE DOG  
ADD CAT
INSERT YOUR CAT
SHOW"""

abarbeiten(data)
Eine eher einfache Aufgabe. Neu für mich der Befehl insert() bei einer Liste.

Secure the Perimeter

Hier soll lediglich der Umfang eines Rechtecks berechnet werden. Es war eine Aufgabe (Problem Nr. 1) bei HP Codewars 2010. Die Formel zur Berechnung des Umfang eines Rechtecks lautet:

U = 2 * H + 2 * B

Das ist keine Herausforderung und die Verarbeitung der Eingabewerte ist auch kein Problem:
def umfang_berechnen(breite,hoehe):
    ''' Berechnet den Umfang eines
    Rechtecks

    '''
    return 2 * hoehe + 2 * breite


beispiele = ["4 6","8 5"]
for wertepaar in beispiele:

    breite, hoehe = map(int,wertepaar.split(" "))
    print(umfang_berechnen(breite,hoehe))
Man könnte vielleicht eine Klasse Rechteck schreiben und um weitere Methoden ergänzen.

Queueing Theory (HP Codewars)

Die Lösung des Problems 15 „Queueing Theory“ von HP Codewars 2012 war nicht so kompliziert, sieht man von einigen Details ab. Problematisch vielleicht lediglich, dass eigentlich eine objektorientierte Umsetzung erwartet worden wäre:
''' HP codewars 2012 - problem 15
Queueing Theory, 9 points

Es hatte etwas gedauert sich wieder etwas in die Stringoperationen
hineinzufinden und die Queue aufzubauen. Ansonsten eher Tipparbeit.

'''

def data_verarbeiten(data):
    ''' Bekommt die Daten und soll sie verarbeiten und
    das Ergebnis als String zurueckgeben...

    '''

    # Variablen
    queue = [[] for i in range(9)]    # Liste zum Abarbeiten 
    queueLen = 0                      # Laenge der Queue
    string = ""                       # Nimmt das Ergebnis auf
    stringLen = 0                     # Laenge des Strings
    ablaufplan = []                   # Regelt die Entnahme aus den Schlangen

    # Variablen uebernehmen
    data = data.split("\n")
    stringLen, queueLen = map(int,data[0].split(" "))
    ablaufplan = [int(a[1]) - 1 for a in data[-1].split(" ")]

    # Einrichten der Queue
    string = " " * stringLen

    for zeile in range(1,queueLen+1):
        # Der range ist so gewaehlt, dass die erste Zeile (0)
        # mit den Metadaten und auch die letzte Zeile (queueLen+2)
        # mit dem Ablaufplan nicht geparst wird
        queue_id, position, content = data[zeile].split(" ")
        queue_id = int(queue_id[-1]) - 1
        queue[queue_id].append([position,content])

    # Wir arbeiten die queue ab
    for id in ablaufplan:
        # Ueber die id wird auf die jeweilige Zeile zugegriffen
        start, content = queue[id].pop(0)

        # Start und Ende festlegen
        start = int(start)
        ende = start + len(content)
            
        if start == 0:
            # z. B.
            # 0123456789
            # WORT
            #     456789
            string = content + string[ende:]
        else:
            # z. B.
            # 0123456789
            #    WORT
            # 012    789
            string = string[:start] + content + string[ende:]
            
    return string
    

data = """44 13
Q1 35 KNOWN
Q1 20 IMPORT
Q3 24 GRANT
Q1 4 IN
Q1 15 MADE
Q1 32 AN
Q2 39 LEDGE
Q2 5 NOTION
Q2 6 A
Q2 16 OR
Q3 0 IMAGE
Q3 12 IS
Q3 30 THIS
Q1 Q3 Q3 Q3 Q2 Q1 Q2 Q1 Q3 Q1 Q2 Q2 Q1"""

print(data_verarbeiten(data))
Zumindest wird das Problem so gelöst.

Dienstag, 5. Februar 2013

Return of Spell Binder

Für die Erledigung der Problemstellung 4 „Return of Spell Binder“ (HP codewars 2012) benötigte ich wenige Sekunden:
def reparieren(string):
    ''' Die Funktion repariert die von einem
    speelbot misshandelten Woerter

    word -> das zu reparierende Wort
    a    -> der zu tauschende Buchstabe
    b    -> der richtige Buchstabe
    
    '''
    word, a, b = string.split(" ")
    
    return word.replace(a,b)


for wort in ["MUSTARD M C","JUNK J TR","MONSTER ON A"]:
    print(wort,"-->",reparieren(wort))
Hier ist im Kern nur eine einfache Ersetzung erforderlich; trügt der Eindruck, den die Problembeschreibung macht, nicht, dann richtet sich diese Aufgabe eher an jüngere Semester. Immerhin gab es 4 Punkte für die Erledigung der Aufgabe.

X Liters of Ginger Soda

Eine relativ einfache, weil nicht aufwendige Aufgabe - lediglich soll Liter in Gallonen umgerechnet werden. Missverständlich vielleicht nur, dass der „nearest integer“ als Ergebnis verlangt wird - zumindest die Musterlösung legt ein Aufrunden nicht nahe und es macht ja wohl auch in diesem Zusammenhang keinen Sinn.
def umrechnen_liter_gallonen(liters):
    ''' Erledigt die Umrechnung.

    '''    
    return int(liters / 3.785)

liters = 144

print(umrechnen_liter_gallonen(liters))
Ob die Funktion für die Umrechnung berechtigt ist, darüber ließe sich wohl streiten.

Samstag, 2. Februar 2013

Indexseite aufbessern

Manchmal macht man sich etwas zu viel Arbeit. Die index-Seite fand ich zu umständlich, weil ich nicht direkt einzelne Buchstaben ansteuern konnte, sondern erst einmal scrollen musste. Ich habe mir überlegt die einzelnen Überschriften mit einem Anker zu versehen, um sie ansteuern zu können. SELFHTML hat hier geholfen, weil hier die projektinternen Anker vorgestellt werden.

Im Kern muss man eine Überschrift oder einen Text auf der Seite mit einem Anker der Form

<a name="NAME_DES_ANKERS">TEXT_ZUM_ANKER</a>

versehen, den man anschließend direkt ansteuern kann und zwar in der Form:

http://www...de/seite.html#NAME_DES_ANKERS

Ich habe jetzt für meine Indexseite einen Parser erstellt, der auf den Seiteninhalt in einer Textdatei zugreift und die Seite um die Anker ergänzt. Schöner wäre sicherlich, wenn das Skript direkt auf die Indexseite zugreifen und die nötigen Änderungen dort direkt vornehmen und speichern könnte.

Das Skript in der aktuellen Fassung:
liste = []
item = ""

with open("indexseite.txt") as f:

    for line in f:


        if item != "":
            line = item + line
            liste.append(line.replace("\n",""))
            item = ""
            
        elif line.startswith("<h2>"):
            item = line
            
        else:
            liste.append(line.replace("\n",""))


for item in liste:

    if item.startswith("<h2>"):
        ''' da soll ein Anker rein!
        http://de.selfhtml.org/html/verweise/projektintern.htm#anker
        z. B.

        <h2><a name="kapitel2">Kapitel 2</a></h2>

        also Element zwischen den h2-Tags ermitteln
        und daraus den Anker basteln...
        
        '''

        # <h2>Ziel</h2>
        ziel = item.rsplit("<")
        # ['', 'h2>A', '/h2>']
        ziel = ziel[1].split(">")
        # ['h2', 'A']
        ziel = ziel[1]
        # 'A'
        zeile = '<h2><a name="{0}">{0}</a></h2>\n'.format(ziel)
        
    else:
        zeile = item + "\n"

    with open("zieldatei.txt","a") as g:
            print(zeile)
            g.write(zeile)
Das Skript funktioniert und speichert in die zieldatei.txt die gewünschten Inhalte. Ein weiteres Skript erzeugt ein Inhaltsverzeichnis, das ich oben auf der Seite einfügen kann. Da bereits alle Buchstaben angelegt sind, muss ich nicht erst prüfen, ob die Buchstaben auf der Seite vorhanden sind.
import string


def inhaltsverzeichnis_erzeugen():
    ''' Die Links haben die Form
    http://pixewakb.blogspot.de/p/index.html#C

    '''

    abc = string.ascii_uppercase
    url = '{0}'
    
    for a in abc:
        
        print(url.format(a), end=" • " if not a == abc[-1] else "")


inhaltsverzeichnis_erzeugen()
Wünschenswert wäre noch ein Zurücklink bei jedem Anker, aber erst einmal läuft das so.

Nachtrag 2. Februar 2013


Ich habe gerade manuell einen Anker auf der Seite ergänzt und die Links in den Überschriften dahingehend angepasst, dass die Links jetzt auf diesen Seitenkopf-Anker verweisen. Anscheinend ändert blogger.com die Links der Anker auf die Blog-ID um, was zu Fehlern führt. Das Skript müsste das noch beachten...

Lambda, filter, reduce und map

Der Python-Kurs.eu bietet eine nette Übersichtsseite zu lambda, filter, reduce und map in Python.

Donnerstag, 31. Januar 2013

Tabelle ausgeben

Was ich bislang mit eigenen - sehr unschönen - Funktionen bewerkstelligt habe, kann man auch mittels front(wert,Formatierung) erledigen, was Weigend auf S. 253 vorstellt.

In diesem Zusammenhang ist die Format String Syntax bzw. die Format Specification Mini-Language zu beachten.

Sonntag, 27. Januar 2013

SQLite

Ich überlege wieder einmal etwas mit SQLite zu arbeiten und suche nützliche Ressourcen im Netz, weil die Dokumentation in den Tutorials und Handbüchern für meine Begriffe meist ziemlich schlecht - weil lückenhaft - ist.

Ressourcen

Zunächst nützliche Seiten im Internet, die bei der Arbeit mit SQLite helfen können:
Lose Sammlung (Qualität?):


Jetzt eine Art Notizzettel.

Die Datenbank

Was möchte ich tun:

  • Eine Datenbank anlegen


Tabellen

Was möchte ich tun:

  • Eine Tabelle anlegen
  • Eine Tabelle löschen


Daten

Was möchte ich tun:

  • Daten eintragen
  • Daten updaten
  • Daten löschen
  • Daten abfragen


Mittwoch, 23. Januar 2013

Fibonacci-Folge

Eine kleine Aufgabe für zwischendurch ist die Programmierung einer Funktion, die die Fibonacci-Folge bzw. das Element n dieser Folge ausgibt.

Rekursive Umsetzung

Hier eine rekursive Umsetzung, die auf meinem System für den Zahlenraum bis etwa 30 einigermaßen flott unterwegs ist, sich anschließend aber festfährt:
def fib(n):
    ''' Rekursive Funktion, die fuer eine Ganzzahl n
    die Fibonacci-Zahl zurueckgibt

    '''
    if n < 2:
        return n
    else:
        return fib(n-2) + fib(n-1)



for i in range(0,100):
    print(i,fib(i))

Eine zweite Umsetzung

Bei diesem Skript, befüllt die Funktion eine Liste und nutzt sie für die Berechnung. Nachteil ist, dass die Liste bei jedem Funktionsaufruf neu erzeugt werden muss, was nicht sehr sinnvoll ist. Das Skript wirkt zunächst recht performant:
def fib(n):
    ''' Rekursive Funktion, die fuer eine Ganzzahl n
    die Fibonacci-Zahl zurueckgibt

    '''
    fibfolge = [0,1,1]
    if n < 2:
        return n
    else:
        for i in range(2,n+1):
            result = fibfolge[i-1] + fibfolge[i]
            fibfolge.append(result)
        return result
Man beachte den Artikel „Fibonacci numbers (Python)“ im LiteratePrograms-Wiki.

Dienstag, 22. Januar 2013

Project Euler: Problem 3

Bei Problem 3 des Projects Euler gilt es folgende Aufgabe zu bewältigen:
Die Primfaktoren von 13195 sind 5, 7, 13 und 29. Was ist der größte Primfaktor der Zahl 600851475143?
Hier ist im Kern eine Primfaktorzerlegung gefordert, was für mich ein Skript erledigt und die richtige Antwort zurückgibt. Das Skript war um folgende Zeilen zu ergänzen:
n = 600851475143
print(max(primfaktoren_bestimmen(n)))
Das Skript lieferte die richtige Antwort in Millisekunden zurück. Hier findet sich eine Lösung des Problems vom mathblog.dk.

Sonntag, 20. Januar 2013

Fakultät

Die Fakultät einer Zahl ist „in der Mathematik eine Funktion, die einer natürlichen Zahl das Produkt aller natürlichen Zahlen kleiner und gleich dieser Zahl zuordnet“ (Wikipedia).

n n!
0 1
1 1
2 2
3 6
4 24
5 120
6 720
7 5.040
8 40.320
9 362.880
10 3.628.800


Das Ganze lässt sich rekursiv formulieren:
def fakultaet(x):
    if x > 1:
        return x * fakultaet(x - 1)
    else:
        return 1
Und auch iterativ:
def fakultaet(n):
    ''' Gibt die Fakultaet
    einer Zahl n zurueck

    '''

    if n <= 1:
        return 1
    else:
        for i in range(1,n):
            n *= i
        return n
Was ist performanter? Eigentlich würde ich erwarten, dass die rekursive Lösung weniger performant ist.

Samstag, 19. Januar 2013

Project Euler: Problem 45

Bei Problem 45 des Projects Euler soll diejenige Zahl > 40755 gefunden werden, die sowohl eine Dreieckszahl als auch pentagonal und hexagonal ist.

Die Formeln zur Berechnung der Zahlenfolgen sind der Aufgabenstellung beigegeben.

T(n) = n (n + 1) / 2

P(n) = n (3 n -1) / 2

H(n) = n (2n - 1)

Die erste Zahl, für die diese Bedingung zutrifft ist 40755.

T(285) = P(165) = H(143) = 40755

Die nächste Zahl, die diese Bedingung erfüllt, wird gesucht.

Für die verschiedenen Zahlen ergeben sich folgende Zahlenfolgen:
T(n) → 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, ... 
P(n) → 1, 5, 12, 22, 35, 51, 70, 92, 117, 145, 176, 210, 247, 287, 330, 376, 425, 477, 532, ... 
H(n) → 1, 6, 15, 28, 45, 66, 91, 120, 153, 190, 231, 276, 325, 378, 435, 496, 561, 630, 703, ... 
Auffälligkeiten? Mir fällt auf, dass jedes zweite Element der Dreieckszahlen (T(n)) auch in der Zahlenfolge der hexagonalen Zahlen (H(n)) auftaucht:

1, 6, 15, 28, 45, 66, 91, 120, 153 usw.

Das heißt, dass ich die Dreieckszahlen nicht berechnen muss. Jede hexagonale Zahl ist zugleich immer eine Dreieckszahl. Mit einer Brute-Force-Methode lässt sich und ließ sich das Problem lösen (mir ist gerade aufgefallen, dass ich es schon einmal gelöst hatte):
pzahlen = [int(i * (3 * i - 1) / 2) for i in range(1,1000000)]

hzahlen = [int(i * (2 * i - 1)) for i in range(1,500000)]

print("Suche jetzt ein Element, das in allen drei Listen auftaucht.")

for i in pzahlen:

    if i in hzahlen:

        print(i)
Nachteil an diesem Verfahren ist sicherlich der Lärm, den der Lüfter produziert. Nicht sinnvoll. Ich habe hier beim MathBlog eine Lösung für das Problem gefunden, die ich nun implementieren will.

Der Lösungsweg erscheint recht einfach:
  • Erzeuge hexagonale Zahlen und prüfe, ob es sich dabei um eine pentagonale Zahl handelt.
Verständnis: (1) Dieser Lösungsweg ist besser, weil nicht erst zwei Listen erzeugt werden müssen, sondern nur die Werte einer Liste hinsichtlich ihrer Eigenschaften zu prüfen sind. (2) Die hexagonalen Zahlen wurden ausgewählt, weil hier deutlich weniger Zahlen zu prüfen sind. (3) Ein weiteres, nicht ausgeschöpftes Potential zur Optimierung wäre es, erst die hexagonalen Zahlen ab 143 zu prüfen, weil 143 ja bereits ein Ergebnis (vgl. Aufgabenstellung) war.

In Python-Code könnte da so aussehen:
import math


def is_polygonal(n):
    ''' Pruefung auf Basis der Formel
    der Wikipedia, Rueckgabewerte True and False

    '''
    result = (math.sqrt(24 * n + 1) + 1) / 6
    if int(result) == result:
        return True
    else:
        return False


for i in range(1,100000):
    # Berechne eine hexagonale Zahl
    hexagonale_zahl = i * (2 * i - 1)
    # Pruefe das
    if is_polygonal(hexagonale_zahl):
        # Ausgabe
        print(hexagonale_zahl)
Die Lösung wird jetzt in wenigen Millisekunden ausgespuckt. Da ich die Prüfformel nicht selbst herleiten konnte, war ich auf die Formel in der Wikipedia angewiesen.

Coderian Cipher

Betrachtet man das Problem 9 (2012) bei HP Codewars, so erkennt man schnell, dass es sich letztlich um eine Caesar-Verschlüsselung handelt, mit der ich mich bereits einmal befasst habe und die zunächst einmal nicht sonderlich anspruchsvoll ist.

Ein gewisser Anspruch kommt in die Aufgabe, weil die Verschiebung über den beigegebenen Schlüssel ermittelt werden soll. Eigentlich müsste man hier eine Dechiffrierung über die Buchstabenhäufigkeit erreichen können, andererseits gibt es hierfür zu wenig Material. Nach einiger Zeit (5. Februar 2013) habe ich jetzt eine lauffähige Lösung für das Problem gecodet:
''' Coderian Cipher - HP codewars 2012
Beschreibung:   Loesung fuer das Problem Nr. 9 von HP codewars 2012
Version:        1. Loesung, 1. Ansatz
Autor:          Pixewakb, pixewakb@gmail.com
Startdatum:     04.02.2013
Enddatum:       05.02.2013
Letzte Bearb.:  05.02.2013
Probleme:       Das Skript duerfte einiges an Optimierungspotential haben

'''

message ="XYVOIC ERH FSCWIRFIVVC SR VCI AMXL E WMHI SJ XYRE WEPEH KEY YONR"


def dechiffrieren(message):
    ''' Erledigt das vollstaendige Dechiffrieren
    und gibt die decodierte Message zurueck

    '''
    # Variablen und Messsage splitten
    verschiebung = 0        # beruecksichtigt nur eine positive Verschiebung (!)
    text,keyword = message.split(" KEY ")

    # Text zurichten
    text = text.upper().replace(" ","")

    # Jetzt muss die Verschiebung ermittelt werden
    # (1) Berechne für jeden Buchstaben a in text eine Verschiebung
    # (2) Pruefe die Verschiebung anhand des Schluesselwortes
    # (3) Wenn Pruefung gelingt, dann stoppe das und gib die Verschiebung raus

    for i,a in enumerate(text):
        # a - verschluesselter Buchstabe
        # b - entschluesselter Buchstabe
        b = keyword[0]

        # print(ord(a),ord(b),sep=" - ",end=";")
        if ord(a) > ord(b):
            verschiebung = ord(a) - ord(b)
            # print(verschiebung)
        elif ord(a) < ord(b):
            verschiebung = -1 * (90 - ord(b) + ord(a) - 64)
            # print(verschiebung)
        else:
            verschiebung = 0
            # print(verschiebung)

        # Verschiebung wird bereits korrekt mit -4 ermittelt (!)

        # Pruefe, ob die Verschiebung zum Keyword passt,
        # sonst weiterpruefen

        # Ermittle die Sequenz :D
        testword = text[i:i+4]
        # Das Testword wird mittels Verschiebung umgetauscht und
        # gegen das keyword geprueft, wenn testword == keyword, dann
        # return verschiebung (!)
        test = []
        for a in testword:

            if ord(a) + verschiebung < ord("A"):
                wert = chr(ord(a) + verschiebung + 26)
            elif ord(a) + verschiebung > ord("Z"):
                wert = chr(ord(a) + verschiebung - 26)    
            else:
                wert = chr(ord(a) + verschiebung)
            test.append(wert)
            
        # print(test,keyword)
        if "".join(test) == keyword:
            print("Verschiebung gefunden! >>>",verschiebung)
            break
    
    # Verschiebung - der Wert sollte berechnet werden (!)    :-((
    # verschiebung = -4   # Muss Negativ sein (!)

    # Dechiffriere mit der Verschiebung den Satz
    # und gib das Ergebnis zurueck!
    result = []
    for a in message[:-9]:

        if a == " ":
            pass
        elif ord(a) + verschiebung < ord("A"):
            a = chr(ord(a) + verschiebung + 26)
        elif ord(a) + verschiebung > ord("Z"):
            a = chr(ord(a) + verschiebung - 26)
        else:
            a = chr(ord(a) + verschiebung)
        result.append(a)
            
    return "".join(result)


print(dechiffrieren(message))
Problem aus meiner Sicht ist, dass mir der Quelltext etwas lang vorkommt und es wahrscheinlich noch deutliches Optimierungspotential gibt.