Datenstruktur für Lottozahl

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo,

Ich bräuchte mal einen Tipp für eine geeignete Datenstruktur um Lottozahlen inkl. Zusatzzahl zu speichern, damit diese mit den Gewinnzahlen verglichen werden können. Momentan benutze ich verschachtelte Listen:

Code: Alles auswählen

gewinn_zahlen = [14, 20, 28, 29, 31, 41, [5]]
meine_zahlen = [
                [22, 26, 29, 33, 35, 47, [2]],
                [12, 32, 2, 26, 3, 36, [6]],
                ]
Mir geht es darum, wie ich das mit der Zusatzzahl am besten handhaben könnte. Was wäre eine bessere Alternative?

mfg
BlackJack

@lackschuh: Ich würde das je eher anders herum machen, die 6 normalen in einer Liste zusammenfassen und die Zusatzzahl einzeln lassen: ``[[14, 20, 28, 29, 31, 41], 5]``. Dann lässt sich das deutlich einfacher vergleichen ohne Teillisten per slicing kopieren zu müssen. Ohne Zusatzzahl ``a[0] == b[0]`` und mit Zusatzzahl ``a == b``. Insbesondere eine Liste die immer nur ein Element enthält mach nicht so viel Sinn. Ansonsten könnte man auch ein Wörterbuch oder gar eine Klasse schreiben, oder zumindest ein `collections.namedtupel` erzeugen.
fail
User
Beiträge: 122
Registriert: Freitag 11. Januar 2013, 09:47

Ich würde ein Set nehmen, da dort Objekte nur einmal vorkommen dürfen und man einfach sehen kann, ob die gespielten Zahlen ein Subset der gezogenen Zahlen ist
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Danke mal für die Tipps. Vergleichen tue ich momentan es so:

Code: Alles auswählen

for tip in meine_zahlen:
    print set(tip[0]) & set(gewinn_zahlen[0]), tip[1] == gewinn_zahlen[1]
Ist aber nicht sehr elegant und zudem gibt es erst ab 3 richtige Zahlen Kohle. Was müsste ich beachten, so dass nur ab 3 übereinstimmenden Zahlen eine 'print' Anweisung gemacht wird und kann man die 'print' Ausgabe der sets formatieren(?), denn so sieht es nicht übersichtlich aus:

Code: Alles auswählen

>> set([14]) False
>> set([41]) False
>> set([41]) True
>> set([14, 31]) True
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@lackschuh: hast Du schonmal was von len gehört?
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Ja, ist der Börsenkürzel von Lennar Corporation :wink:

Code: Alles auswählen

if len(set(tip[0]) & set(gewinn_zahlen[0])) >= 3:
Aber wie lasse ich 'set([])' verschwinden und stattdessen einfach die Zahlen anzeigen lassen?

Danke
BlackJack

@lackschuh: Falls ich die Frage richtig verstanden habe: In dem Du Code schreibst der aus den Werten eine Zeichenkette erstellt die Deinen Vorstellungen entspricht, statt einfach Datenstrukturen auszugeben und deren Zeichenkettenrepräsentation zu verwenden. Das ist für Programmierer und die Fehlersuche gedacht, nicht um dem Endbenutzer etwas zu zeigen.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Danke, anbei sieht es nun so aus:

Code: Alles auswählen

# coding: utf-8

import requests
from bs4 import BeautifulSoup

URL = "https://www.swisslos.ch/swisslotto/home.do"
lotto_zahl = []
lotto_zahlen = []
count = 0

html = requests.get(URL)
soup = BeautifulSoup(html.text)

for label in soup.select('li.numberLarge'):
     lotto_zahl.append(int(label.string))

zusatz_zahl = lotto_zahl[-1]
lotto_zahlen.append(lotto_zahl)
del lotto_zahl[-1]
lotto_zahlen.append(zusatz_zahl)

#gewinn_zahlen = [[14, 20, 28, 29, 31, 41], 5]

meine_zahlen = [
                [[9, 17, 20, 25, 26, 29], 2],
                [[1, 8, 16, 21, 32, 37], 6],
                [[15, 20, 34, 35, 36, 39], 5],
                [[14, 20, 29, 12, 14, 25], 5],
                ]


for tipp in meine_zahlen:
    if len(set(tipp[0]) & set(lotto_zahlen[0])) >= 3:
        count = count + 1
        richtige = set(tipp[0]) & set(lotto_zahlen[0])
        print '%d. Tipp:' % count, "%d richtige Zahlen %s," % (len(list(richtige)), list(richtige)), 'Zusatzzahl', tipp[1] == lotto_zahlen[1]
    else:
        count = count + 1
        print '%d. Tipp:' % count, 'Leider kein Gewinn'
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also da sind einige Dinge noch unschön...

- Statt

Code: Alles auswählen

zusatz_zahl = lotto_zahl[-1]
del lotto_zahl[-1]
würde man ``list.pop`` nutzen:

Code: Alles auswählen

zusatz_zahl = lotto_zahlen.pop(-1)
- Wenn man einen zusätzlichen Zähler/Index in einer Schleife braucht, dann sollte man ``enumerate`` nutzen!

Code: Alles auswählen

for count, tipp in enumerate(meine_zahlen, 1):
- Wenn man Strings mit Platzhaltern nutzt, dann doch bitte "am Stück". Bisher sind die Platzhalter wenig sinnvoll und tragen nicht zur Lesbarkeit bei.

Code: Alles auswählen

'%d. Tipp: %d richtige Zahlen %s, Zusatzzahl %d' % ...
- Wieso die umständliche Umwandlung``(len(list(richtige))``? ``len`` kann man auch auf ``sets`` anwenden ;-)

- Der ``len`` Ausdruck in der ``if``-Bedingung wird darunter *exakt* noch einmal wiederholt. Zieh die Prüfung doch vor das ``if`` und schon hast Du ein wenig Redundanz weniger.

- Die Namensgebung ist imho inkonsistent: Wenn doch ``meine_zahlen`` eigentlich Tipps sind, wieso dann nicht ``meine_tipps``? Oder andersherum in der ``for``-Schleife eben ``for zahl in meine_zahlen``.

- Generell Code von Modulebene in Funktionen verlagern.

- Du vermischst hier noch zu viel Code miteinander. Ich würde mir für die Auswertung ggf. auch eine Struktur überlegen und die Ausgabe davon separieren. Dazu dann eine Funktion zum Holen und Parsen der richtigen Ergebnisse und schon bist Du viel flexibler bezüglich der Nutzung.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@lackschuh: Zu den Namen wurde ja schon etwas gesagt. `lotto_zahl` (Einzahl) für eine Liste mit Zahlen (Mehrzahl) ist irreführend. Etwas präzisere Bezeichnungen wie Tipp und Ziehung statt allgemein Zahl(en) würden das Programm verständlicher machen.

Auch ungünstig ist das definieren von Namen lange bevor sie tatsächlich benötigt werden. Dann muss man dort nämlich erst wieder weiter oben suchen was die eigentlich für Startwerte haben. Es erschwert auch das spätere aufteilen in einzelne Funktionen wenn nicht der zusammengehörende Code beisammen steht und man sich erst alles über den Code verteilt zusammen suchen muss.

Die Tipps umgesetzt könnte das ungefähr so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import print_function
import requests
from bs4 import BeautifulSoup
 
URL = 'https://www.swisslos.ch/swisslotto/home.do'


def hole_ziehung():
    soup = BeautifulSoup(requests.get(URL).text)
    lottozahlen = [
        int(n.string) for n in soup.select('div#actualNumbers li.numberLarge')
    ]
    result = [lottozahlen]
    result.append(lottozahlen.pop())
    return result


def vergleiche(tipp, ziehung):
    return (sorted(set(tipp[0]) & set(ziehung[0])), tipp[1] == ziehung[1])


def main():
    ziehung = hole_ziehung()
    tipps = [
        [[9, 17, 20, 25, 26, 29], 2],
        [[1, 8, 16, 21, 32, 37], 6],
        [[15, 20, 34, 35, 36, 39], 5],
        [[14, 20, 29, 12, 14, 25], 5],
    ]

    for tipp_nr, tipp in enumerate(tipps, 1):
        print('{0}. Tipp:'.format(tipp_nr), end=' ')
        richtige, ist_zusatzzahl_richtig = vergleiche(tipp, ziehung)
        if len(richtige) >= 3:
            print(
                '{0} richtige Zahlen {1}, Zusatzzahl: {2}.'.format(
                    len(richtige),
                    ', '.join(richtige),
                    'ja' if ist_zusatzzahl_richtig else 'nein'
                )
            )
        else:
            print('Leider kein Gewinn')


if __name__ == '__main__':
    main()
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Vielen Dank für die Tipps.

Ich benutze Python 2.7.
Was bedeutet ''from __future__ import print_function''?
Wenn ich dies auskommentiere, dann wird mir zB bei ''print('Leider kein Gewinn')'' ein Fehler angezeigt, wenn ich bei dieser print-Anweisung die Klammern entferne. Ist dies (__future__) vereinfacht gesagt das, was bei Python3 die print-Funktion neu ist?

Des Weiteren wurde noch gemeckert, dass bei ''''.join(richtige)'' ein String erwartet wird. ''''.join(str(richtige))''
BlackJack

@lackschuh: Der `__future__`-Import macht aus ``print`` eine Funktion.

Ich hätte es mit Treffern testen sollen. :-) Nein, natürlich darf man nicht die Datenstruktur `richtige` in *eine* Zeichenkette umwandeln, sondern muss die einzelnen Elemente umwandeln die man dann mit `join()` zusammenfügen möchte:

Code: Alles auswählen

                    ', '.join(map(str, richtige)),
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Ich habe mir auch schon mal Gedanken über ein Lottoprogramm gemacht, vielleicht kannst du daraus neue Erkenntnisse gewinnen:

https://github.com/MarkusHackspacher/pyLottoverwaltung
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Ich würde das Projekt sicher nicht als Beispiel für guten Code sehen.
Besser er schaut nicht da rein, sonst schaut er sich wirklich noch was ab.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

Ich hab das Programm für ein anderes Lotto-System ein wenig umgeschrieben. Bei diesem System gibt es folgendes zu beachten für ein Gewinn:
Zahl (max 5) + Stern (max 2)
Gewinn ab 1 Zahl + 2 Sterne, zwei Zahlen + 0 Stern.

Nun habe ich zweimal die gleiche Print-Anweisung und eine 'if', 'elif' und 'else' Abfrage. Gäbe es eine Möglichkeit, dies alles zu verkürzen bzw zu vereinfachen?

mfg

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

from __future__ import print_function
import requests
from bs4 import BeautifulSoup

URL = 'http://www.euro-millionen.org'


def hole_ziehung():
    soup = BeautifulSoup(requests.get(URL).text)
    lottozahlen = [
                   int(n.string) for n in soup.select('div#results-content li')
                   ]
    result = [lottozahlen]
    zusatz_zahlen = []
    zusatz_zahlen.append(lottozahlen.pop(-2,))
    zusatz_zahlen.append(lottozahlen.pop(-2,))
    result.append(zusatz_zahlen)
    return result


def vergleiche(tipp, ziehung):
    return sorted(set(tipp[0]) & set(ziehung[0])), sorted(set(tipp[1]) & set(ziehung[1]))


def main():
    #ziehung = hole_ziehung()
    ziehung = [[20, 21, 27, 33, 40], [2, 6]]
    print(ziehung)
    tipps = [
        [[1, 5, 21, 33, 48], [2, 6]],
        [[14, 20, 32, 33, 40], [3, 7]],
      ]

    for tipp_nr, tipp in enumerate(tipps, 1):
        print('{0:>1}. Tipp:'.format(tipp_nr), end=' ')
        richtige, sind_sterne_richtig = vergleiche(tipp, ziehung)

        if len(richtige) >= 1 and len(sind_sterne_richtig) == 2:
            print('{0} richtige Zahlen: "{1}": Sterne: "{2}"'.format(
                                                                     len(richtige),
                                                                     ', '.join(map(str, richtige)),
                                                                     '{0}'.format(', '.join(map(str, sind_sterne_richtig))) if sind_sterne_richtig else 'falsch'
                                                                     )
                  )

        elif len(richtige) >= 2:
            print('{0} richtige Zahlen: "{1}": Sterne: "{2}"'.format(
                                                                     len(richtige),
                                                                     ', '.join(map(str, richtige)),
                                                                     '{0}'.format(', '.join(map(str, sind_sterne_richtig))) if sind_sterne_richtig else 'falsch'
                                                                     )
                  )

        else:
            print('Leider kein Gewinn')


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@lackschuh: in dem Du die beiden if per or in eines packst. Noch besser schreibst Du Dir eine Funktion, die ermittelt, ob gewonnen wurde oder nicht.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

Hallo

Die Seite http://www.euro-millionen.org/ wurde umgebaut.
Die Sterne bekomme ich als Liste mit:

Code: Alles auswählen

[int(n.string) for n in soup.select('li.star')]
Da in der untergeordneterer Liste nochmals eine Liste ``<li class="star">`` drinsteht, habe ich gerade Mühe, alles auf einmal zu parsern. Ideal wäre als Resultat eine verschachtelte Liste [[1, 10, 17, 20, 42], [8, 9]]. Für einen Tipp wäre ich dankbar :wink:

Code: Alles auswählen

<ul class="numbers">
<li>
                        1                    </li>
<li>
                        10                    </li>
<li>
                        17                    </li>
<li>
                        20                    </li>
<li>
                        42                    </li>
<li class="star">
                        8                    </li>
<li class="star">
                        9                    </li>
</ul>
Antworten