Matheprogramme (ggT, kgV, Primzahlen)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Hallo Python Freunde,
ich hatte Langeweile und habe zwei kleine Programme geschrieben.

1.Einfaches Programm zum Primzahlen berechnen:

Code: Alles auswählen

def PrimeBerechnen(bis, primzahlen):
    if len(primzahlen) < 2:
        primzahlen = [2]
        datei.write(str(2) + ",")
        von = 3
    else:
        von = primzahlen[len(primzahlen) - 1] + 2
    print("Berechne Primzahlen von", von, "bis", bis)
    for zahl in range(von, bis, 2):
        halb = zahl ** 0.5
        try:
            for primzahl in primzahlen:
                if primzahl > halb:
                    break
                rest = (zahl % primzahl)
                if not rest:
                    raise ZeroDivisionError
        except ZeroDivisionError:
            continue
        primzahlen.append(zahl)
        datei.write(str(zahl) + ",")
    print("Berechnung fertig!")
    return primzahlen


if __name__ == "__main__":
    import time, sys
    berechnen_bis = int(input("Bitte größte zu berechnene Zahl eingeben: "))
    primzahlen = []
    try:
        datei = open("prmzhln.txt", "r+")
    except:
        datei = open("prmzhln.txt", "w+")
    dateir = datei.read()
    daten = dateir.split(",")
    for zahl in daten:
        try:
            primzahlen.append(int(zahl))
        except:
            break
    startzeit = time.time()
    try:
        primzahlen = PrimeBerechnen(berechnen_bis, primzahlen)
    except KeyboardInterrupt:
        print("Berechnung abgebrochen!")
        print("Letzte Primzahl:", primzahlen[len(primzahlen)-1])
        datei.close()
        sys.exit()
    endzeit = time.time()
    zeitdauer = endzeit - startzeit
    print("Dauer:", zeitdauer, "Sekunden")
    print("Anzahl Primzahlen:", len(primzahlen))
    #print("Primzahlen:", primzahlen)
    datei.close()
2. Zahlen eingeben (nichts eingeben zum berechen). Berechnet kgV, ggT und sortiert die Liste auf und absteigend.

Code: Alles auswählen

def kgV(zahlen):
    mal = 1
    while True:
        vielfaches = zahlen[0] * mal
        try:
            for zahl in zahlen:
                rest = (vielfaches % zahl)
                if not rest: pass
                else:
                    raise
            break
        except: pass
        mal += 1
    return vielfaches

def ggT(zahlen):
    teiler = zahlen[0]
    while True:
        try:
            for zahl in zahlen:
                rest = (zahl % teiler)
                if not rest: pass
                else:
                    raise
            break
        except: pass
        teiler -= 1
    return teiler
    
    
if __name__ == "__main__":
    zahlen = []
    while True:
        try:
            zahlen.append(int(input("Zahl eingeben: ")))
        except:
            print("Berechnung beginnt!")
            break

    z_e_sortiert = sorted(zahlen)
    z_u_sortiert = sorted(zahlen, reverse=True)
    print("Aufsteigend sortiert: " + str(z_e_sortiert))
    print("Absteigend sortier: " + str(z_u_sortiert))
    a_kgV = kgV(z_u_sortiert)
    a_ggT = ggT(z_e_sortiert)
    print("kgV:", a_kgV)
    print("ggT:", a_ggT)
Über Verbesserungen, Anregungen und Ideen würde ich mich freuen.
Zuletzt geändert von Lasse am Sonntag 27. März 2011, 16:39, insgesamt 3-mal geändert.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich finde deine Funktionen etwas zu aufwendig gestaltet. Man könnte den ggT und kgV etwas leichter ausdrücken z.B

Code: Alles auswählen

def ggt(a, b):
    while b != 0:
        c = a % b
        a, b = b, c
    return a

def kgv(a, b):
    return (a * b) / ggt(a, b) 
Und diese dann in einer Schleife nutzen, statt alle zahlen an die Funktionen zu geben.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

@ Xynon1:
Jetzt hast du es dir aber etwas zu leicht gemacht, man soll den ggT und kgV von einer beliebigen Anzahl von Zahlen ausrechnen können. Dies können deine Funktionen aber offensichtlich nicht.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Deswegen schrieb ich ja auch, das man diese dann in einer Schleife nutzen kann. Deine Funktionen erfüllen hier im eigentlich Sinn nicht ihre Funktion. Du mischst die Aufgabenbereiche, vorallem bei deinen Primzahlen schibst du sehr viel Text mit in die Funktion. Man sollte eine Funktion nur Daten zurückgeben lassen und dann dafür eine Auswertung erstellen, so kannst du die Funktionen nur so nutzen und später nicht wiederverwenden.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Lasse hat geschrieben:man soll den ggT und kgV von einer beliebigen Anzahl von Zahlen ausrechnen können. Dies können deine Funktionen aber offensichtlich nicht.
Dann eben so:

Code: Alles auswählen

>>> from fractions import gcd
>>> ggt = lambda ns: reduce(lambda x,y:gcd(x,y),ns)
>>> kgv = lambda ns: reduce(lambda x,y:x*y/gcd(x,y),ns)
>>> ggt([12,24,18,126])
6
>>> kgv([3,5,6,15])
30
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Mis missfällt in der Lösung von Lasse der "Missbrauch" von Excpetions. Imho sieht das unschön aus, wenn man planbare Ereignisse durch Exceptions behandelt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo,

ganz ehrlich: bei dem Code bekomme ich fast schon Angstzustände ;-) Mal in ungeordneter Reihenfolge für die Primzahlberechnung:

- Wirf mal einen Blick in PEP 8 bezüglich Namensgebung von Funktionen
- Auf das letze Element einer Liste l kann man mit l[-1] zugreifen
- Wenn der Parameter "primzahlen" auf [2] gesetzt wird, dann wird deine Methode nicht das gewünschte Ergebnis liefern
- Du solltest entweder Primzahlen in der Funktion berechnen oder ausgeben. Vermische nicht beides.
- "halb" ist an sich bereits ein schlecht gewählter Name; besonders schlimme wird es erst wenn nicht einmal die Hälfte von "irgend etwas" darin steckt.
- Leere "except"s sind eine ganz schlechte Idee. Damit wird wirklich jeder Fehler abgefangen und du kanns kaum noch Abläufe nachvollziehen. Gib immer genau die Ausnahmen an, die auch behandelt werden sollen.
- Die Klammern bei "(zahl % primzahl)" sind überflüssig und tragen auch sicher nicht zur Lesbarkeit bei.
- Die Verwendung von Exceptions ist bei dir mehr als abenteuerlich. Sie sind nicht dafür gedacht, um beliebige Sprünge durchzuführen. Und dann sollte man natürlich nicht sich irgend eine Exception packen, hier ZeroDivisionError und diese missbrauchen.
- Warum gehst du überhaupt den weg über die Exception? Du weist doch bereits beim "raise", dass du eine Primzahl gefunden hast. Hänge sid dort an die Liste an und verlasse einfach mit break die Schleife.
- Die Liste der übergebenen Primzahlen wird verwendet. Fraglich, ob das das erwartete Verhalten der Schnittstelle sein sollte.
- Wenn du mit Dateien hantierst, solltest du das with-Statement benutzen.
- "dateir" ist ein sehr seltsamer Name.
- Du verdeckst alle Fehler beim umwandeln in Integers. Wenn eine Nicht-Zahl angetroffen wird, wird kommentarlos die Schleife abgebrochen und fortgefahren.
- Die Umwandlung in Integers kann man mit einer List Comprehension oder mit "map" viel einfacher machen.
- "prmzhln.txt", sind dir die Vokale ausgegangen? ^^

Sebastian
Das Leben ist wie ein Tennisball.
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Hallo,

ich habe das Primzahlenprogramm nocheinmal überarbeitet. Es wäre nett wenn jemand über den Code schauen könnte.

Verbesserungen:
-PEP 8 bei den Funktionsnamen beachtet
-Zugriff auf das letzte Element einer Liste mit [-1]
-Ausgabe und Berechnung sind getrennt
-"halb" wurde in "zweite_wurzel" umbenannt
-keine leeren "except"s mehr
-"except" in "primeBerechnen" ersetzt
-überflüssige Klammern entfernt
-"dateir" wurde in "datei_gelesen" umbenannt
EyDu hat geschrieben:- Warum gehst du überhaupt den weg über die Exception? Du weist doch bereits beim "raise", dass du eine Primzahl gefunden hast. Hänge sid dort an die Liste an und verlasse einfach mit break die Schleife.
Beim raise weiß ich das diese Zahl keine Primzahl ist. (Zahl wurde aussortiert weil teilbar)
Aber egal, das Problem wurde eh neu gelöst.

Code: Alles auswählen

def primeBerechnen(von, bis, primzahlen):
    #Berechnet Primzahlen zwischen "von" und "bis" aus.
    #Fügt neue Primzahlen der Liste "primzahlen" hinzu.
    #Die komplette Liste wird zurückgegeben.
    #Bei vorzeitigem Abbruch werden alle schon berechneten Zahlen zurückgegeben.
    try:
        for zahl in range(von, bis, 2):
            zweite_wurzel = zahl ** 0.5
            for primzahl in primzahlen:
                if primzahl > zweite_wurzel:
                   primzahlen.append(zahl)
                   break
                rest = zahl % primzahl
                if not rest:
                    break
                        
    except KeyboardInterrupt: pass

    #Liste zurückgeben
    return primzahlen

def primZahlBereich(bis, primzahlen):
    #Findet den passenden Suchbereich mithilfe schon bestehender Primzahlen
    #Und gibt die geforderten Primzahlen zurück
    if len(primzahlen) < 2: #Wenn Liste praktisch leer
        primzahlen = [2]
        von = 3
        ergebnis = primeBerechnen(von, bis, primzahlen)
    else:
        if primzahlen[-1] >= bis: #Wenn schon alle Zahlen ausgerechnet wurden
            ergebnis = []
            for primzahl in primzahlen:
                if primzahl > bis:
                    break
                ergebnis.append(primzahl)
        else:
            von = primzahlen[- 1] + 2
            ergebnis = primeBerechnen(von, bis, primzahlen)
    return ergebnis


if __name__ == "__main__":
    import time #Import vom Time Modul um die Rechendauer anzeigen zu können
    primzahlen = [] #Liste der Primzahlen
    dateiname = "primzahlen.txt" #Datei der Primzahlen
    berechnen_bis = int(input("Bitte größte zu berechnene Zahl eingeben: "))
    #Primzahldatei suchen
    try:
        datei = open(dateiname, "r+") #Wenn vorhanden übernehmen
    except IOError:
        datei = open(dateiname, "w+") #Falls nicht vorhanden, neue erstellen
    datei_gelesen = datei.read() #Datei lesen
    daten = datei_gelesen.split(',')
    for zahl in daten: #Liste erstellen
        try:
            primzahlen.append(int(zahl))
        except ValueError: pass

    startzeit = time.time()#Startzeit holen
    
    primzahlen = primZahlBereich(berechnen_bis, primzahlen) #Berechnen
    
    endzeit = time.time()#Endzeit holen
    zeitdauer = endzeit - startzeit #Differenz berechnen
    print("Dauer:", zeitdauer, "Sekunden")
    print("Anzahl Primzahlen:", len(primzahlen))
    print("Primzahlen:", primzahlen)
    datei = open(dateiname, "w")
    for zahl in primzahlen:
        datei.write(str(zahl) + ',')
    datei.close()
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Für Wurzel ziehen solltest du "math.sqrt()" nutzen. Zudem gerade bei sowas rechenintensiven, sollte man auf keinen Fall "KeyboardInterrupt" abfangen, überleg mal du willst wirklich einfach mal sauber abbrechen. Ein weiterer Punkt wäre einen Generator statt einer Liste zu nutzen, das wäre bei sehr sehr großen Mengen besser.
Ich würde so was vorschlagen:

Code: Alles auswählen

def is_prime(z):
    if z <= 1:
        return False
    
    k = 2
    while k**2 <= z:
        if z % k == 0:
            return False
        k += 1
        
    return True

def prime_to(maximum, start=0):
    return (z for z in xrange(start, maximum) if is_prime(z))
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten