Klammern in Ausgabe einfügen (Python 3.4)

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.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Hallo, ich programmiere in letzter Zeit aus Spaß in meiner Freizeit und möchte im Moment ein Programm zur Primfaktorenzerlegung schreiben.
Das funktioniert auch schon alles, aber ich möchte die Ausgabe noch perfektionieren, ich habe das dann einzeln angegangen.
Der Code findet mehrache Faktoren und bringt sie in die übliche Schreibweise, nur fehlen mir noch die Klammern um die Zahlen mit Exponenten. Diese könnten natürlich auch noch klein und oben stehend sein...

Code: Alles auswählen

eingabe=[1, 2, 2, 3, 5, 5, 7]
eingabesort = list(set(eingabe))
ausgabe=""
finalausgabe=[]
s=0
str0=""

for i in eingabesort:
    expo=eingabe.count(i)
    if expo>=2:
        ausgabe=eingabesort[s],expo
        str0 =('^'.join(str(e) for e in ausgabe))
        finalausgabe.append(str0)
    else:
        finalausgabe.append(eingabesort[s])
    s=s+1

print('*'.join(str(e) for e in finalausgabe)) 

Ausgabe: 1*2^2*3*5^2*7

vielen Dank!

PS: ich würde das ganze auch am liebsten als funktion haben, die eine Liste als Argument hat...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Kannst Du mal eine "Soll"-Ausgabe zeigen?

Von sonderbaren Namen mal abgesehen ist das gezeigte auch ziemlich ineffizient! Zeile 9 bewirkt, dass Du quasi quadratische Laufzeit hast... Schau Dir doch mal ``collections.Counter`` als Datenstruktur an!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: join ist dazu da, eine beilibige Anzahl an Strings miteinander zu verbinden. Wenn Du aber immer genau zwei Elemente hast und diese auch noch eigens dafür in eine Liste packen mußt und diese auch noch eigens für join in einen String umwandelst, ...
Schonmal was von Stringformatierung gehört?

Code: Alles auswählen

"%d^%d" % (base, expo)
In finalausgabe würde ich auch nur Strings stecken und kein Mischmasch aus Strings und Zahlen.
Was ist i? Das ist ein schlechter Name und verdeckt damit auch, dass Du den Wert von i nochmal auf umständliche Weise mit antipythonischem Indexzählen ermittelst.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Hi, vielen danke für die guten Tipps! :D
Die Stringformatierung hat meine eigentliche Frage ja gleich mit gelöst. -Jetzt habe ich folgende Ausgabe: 1*(2^2)*3*(5^2)*7

Code: Alles auswählen

import collections

eingabe=[1, 2, 2, 3, 5, 5, 7]
ausgabe=[]

cnt=collections.Counter()

for int in (eingabe):
    cnt[int] +=1

for k, v in cnt.items():
    if v==1:
        ausgabe.append(str(k))
    else:
        ausgabe.append("(%d^%d)" % (k, v))

print('*'.join(e for e in ausgabe))
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: warum glaubst Du heißt Counter Counter :wink:

Code: Alles auswählen

cnt = collections.Counter(eingabe)
Das for im join ist überflüssig.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

copy und paste... mitdenken und so...

Code: Alles auswählen

def countexp(*args):

    import collections
    ausgabe=[]

    for k, v in collections.Counter(args).items():
        if v==1:
            ausgabe.append(str(k))
        else:
            ausgabe.append("(%d^%d)" % (k, v))
    return('*'.join(ausgabe))
Ist Zeile 6 okay oder ist collections.Counter(args).items() unschön?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: args ist so ziemlich der nichtssagenste Name für eine Argument. Was soll die Magie mit *?
Importe sollten immer am Anfang der Datei stehen. Die zwei "append" sind irgendwie unschön. Eine Funktion, die ein Base-Exponenten-Pärchen in einen String umwandelt wäre schöner.
return ist keine Funktion, also warum die Klammern?
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Für die Base-Exponenten Funktion bräuchte ich noch einen Tipp.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: wie man Funktionen macht, weißt Du? Dann vereinfacht sich Deine Schleife zu

Code: Alles auswählen

ausgabe = []
for base, exponent in collections.Counter(primefactors).items():
    ausgabe.append(factor_to_str(base, exponent))
oder mit LC:

Code: Alles auswählen

factors = collections.Counter(primefactors)
ausgabe = [factor_to_str(base, exponent) for base, exponent in factors.items()]
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Die Magie mit dem * soll bewirken, dass die Funktion sich die Parameter aus einer Liste mit den Primfaktoren nimmt.

Aus factor_to_str bin ich noch nicht ganz schlau geworden.

Das ist übrigens der gesamte Code. Ich bin mir dessen bewusst, dass es noch einiges Verbesserungspotential gibt, ich gedenke noch das Sieb des Eratosthenes in der Zerlegung zu implementieren...
Ich bin das Problem absichtlich erstmal komplett intuitiv, oder "naiv" angegangen. Ist mein erstes "eigenes" Programm.

Code: Alles auswählen

import math
import collections


def prim(self):
    global a  
    a=1
    if self<2:
        a=0
    for i in range(2,int((math.sqrt(self))+1)):
        if self%i==0:
            a=0
            break

    return a


def countexp(*primefactors):
    ausgabe=[]

    for k, v in collections.Counter(primefactors).items():
        if v==1:
            ausgabe.append(str(k))
        else:
            ausgabe.append("(%d^%d)" % (k, v))
        r='*'.join(ausgabe)

    return(r)


z=0 
while z==0: 
    
    x=int(input("Bitte geben sie eine Zahl ein: ")) 
   
    b=[] 
    i=2  
    
    if prim(x)!=0: 
        print(x,"ist schon eine Primzahl")
    else:
        while prim(x)==0:  
            if x%i==0 and prim(i)!=0:  
                x=x/i
                b.append(i) 
                i=1 
            i=i+1
    b.append((int(x))) 

    print("Die Primfaktoren Zerlegung lautet", countexp(*b))
    z=int(input("0 für nochmal "))                
                
Zuletzt geändert von nyxx am Dienstag 31. März 2015, 16:25, insgesamt 2-mal geändert.
BlackJack

@nyxx: Die ``*``-Magie ist doch aber vollkommen überflüssig. Wenn die Funktion eine Liste bekommen soll, dann übergib eine Liste. Das machst Du ja im Grunde auch. Der ``*`` beim Aufruf ist ja nur notwendig weil er unnötigerweise auch bei der Funktionsdefinition steht.

Edit: Sonstige Anmerkungen: Bitte vergiss sofort das es ``global`` gibt. Werte sollten Funktionen als Argumente betreten und als Rückgabewerte verlassen. Das ist hier ja auch gar nicht nötig denn die Funktion *hat* ja einen Rückgabewert.

`self` ist ein sehr eigenartiger Name für das Argument einer Funktion.

`a` ist im Grunde überflüssig wenn man an den entsprechenden Stellen gleich ein ``return`` mit dem jeweiligen Wert schreibt. Und `True` und `False` statt 1 und 0 bitte.

``return`` ist keine Funktion, sollte also auch nicht so geschrieben werden als wäre es eine.

Das Hauptprogramm sollte auch in einer Funktion stehen und nicht auf Modulebene. `z` würde ich weglassen und eine ”Endlosschleife” schreiben die mit ``break`` verlassen wird wenn der Benutzer keinen weiteren Durchgang wünscht.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Tatsache. :D Ich hatte irgendwelche Probleme bei der Übergabe einer Liste, warum kann ich aber auch nicht mehr sagen.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Habe noch aufgeräumt:

Code: Alles auswählen

import math
import collections

def Primtest(kandidat):
    if kandidat<2:
        return False
    for i in range(2,int((math.sqrt(kandidat))+1)):
        if kandidat%i==0:
            return False
            break
    else:
        return True


def Basehochexp(primefactors):
    ausgabe=[]
    if type(primefactors)==int:
        return primefactors
    for k, v in sorted(collections.Counter(primefactors).items()):
        if v==1:
            ausgabe.append(str(k))
            print(k)
        else:
            ausgabe.append("(%d^%d)" % (k, v))
        r='*'.join(ausgabe)

    return r

def Primfaktorzerlegung(eingabe):
    primfaktoren=[] 
    i=2  
    if Primtest(eingabe)==True: 
        return eingabe
        
    else:
        while Primtest(eingabe)==False:  
            if eingabe%i==0 and Primtest(i)==True:  
                eingabe=eingabe/i
                primfaktoren.append(i) 
                i=1 
            i=i+1
    primfaktoren.append((int(eingabe)))
    return primfaktoren


while 2:
    x=int(input("Bitte geben sie eine Zahl ein: "))
    print("Die Primfaktoren Zerlegung lautet", Basehochexp(Primfaktorzerlegung(x)))
    z=int(input("0 für nochmal "))
    if z!=0:
        break
Momentan habe ich nur das Problem das das Programm z.B. bei der Eingabe 22 die Primfaktoren vor der eigentlichen Ausgabe noch einzeln druckt.
BlackJack

@nyxx: Bezüglich der Namenschreibweise und Leerzeichensetzung könntest mal einen Blick in den Style Guide for Python Code werfen.

Funktionen und Methoden werden oft nach Tätigkeiten benannt um sie leichter von Namen unterscheiden zu können die passive Werte bezeichnen. Funktionen oder Methoden die etwas testen haben oft Präfixe wie `is_` oder `has_`, also zum Beispiel `is_prime()` für eine Funktion die eine Zahl darauf testet ob sie eine Primzahl ist.

Code der nach einem ``return`` steht wird niemals ausgeführt weil das ``return`` die Ausführung der Funktion ja beendet.

`Primtest()` ist fehlerhaft, was man eigentlich ziemlich schnell bemerkt haben sollte:

Code: Alles auswählen

In [14]: [(i, Primtest(i)) for i in range(10)]
Out[14]: 
[(0, False),
 (1, False),
 (2, None),
 (3, None),
 (4, False),
 (5, True),
 (6, False),
 (7, True),
 (8, False),
 (9, True)]
Die Funktion sollte nicht `None` zurück geben und 9 ist sicher keine Primzahl.

In `Basehochexp()` wird `r` viel zu oft berechnet. Ausserdem ist das kein guter Name.

Vergleiche mit literalen `True`- und `False`-Werten sind unnötig. Dabei kommt entweder immer der Wert heraus mit dem man vergleicht, also kann man auch gleich *den* verwenden, oder immer genau das Gegenteil von dem Wert mit dem man vergleicht, in dem Falle hätte man den mit ``not`` einfach negieren können.

Die `Primfaktorzerlegung()` sieht mir sehr komisch aus. Kann sein das die funktioniert, kann sein dass sie es nicht tut, auf jeden Fall ist das alles viel zu kompliziert. Ein Primzahltest braucht man dazu zum Beispiel überhaupt nicht. Man fängt einfach an bei 2 aufwärts zu testen ob man teilen kann und falls ja tut man das so oft wie es geht und merkt sich die Teiler. Fertig ist man, wenn die Zahl die man noch zerlegen muss die 1 ist. Und eigentlich könnte man an der Stelle auch schon zählen wie oft man durch eine Zahl teilen konnte, dann braucht man den `Counter` später gar nicht erst.

``while 2:``? WTF‽
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: was soll das Prüfen auf int in Basehochexp? Du solltest die Funktion immer mit einer Liste füttern. Außerdem ist dann der Typ des Rückgabewerts ein anderer, als wenn Du eine Liste übergibst, was sehr überraschend ist und Überraschungen mögen Programmierer gar nicht gern. Das Problem kommt von Primfaktorzerlegung, das nicht immer eine Liste liefert. Warum die Sonderbehandlung, falls eingabe eine Primzahl ist? Und warum gehst Du auf 1 zurück, wenn Du einen Primfaktor gefunden hast. Glaubst Du, dass bei der kleineren Zahl sich wieder ein kleinerer Primfaktor eingeschlichen hat? Das verschlechtert die Laufzeit deines Algorithmus' nochmal. Wenn Du Ganzzahldivision // nimmst mußt Du das Ergebnis auch nicht mehr explizit in int umwandeln. In der Zeile sind sowieso zu viele Klammern.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Danke nochmal! Bei mir gibt print([(i, Primtest(i)) for i in range(10)]) folgendes aus:

Code: Alles auswählen

[(0, False), (1, False), (2, True), (3, True), (4, False), (5, True), (6, False), (7, True), (8, False), (9, False)]
BlackJack hat geschrieben:@nyxx: Bezüglich der Namenschreibweise und Leerzeichensetzung könntest mal einen Blick in den Style Guide for Python Code werfen.

`Primtest()` ist fehlerhaft, was man eigentlich ziemlich schnell bemerkt haben sollte:

Code: Alles auswählen

In [14]: [(i, Primtest(i)) for i in range(10)]
Out[14]: 
[(0, False),
 (1, False),
 (2, None),
 (3, None),
 (4, False),
 (5, True),
 (6, False),
 (7, True),
 (8, False),
 (9, True)]
Die Funktion sollte nicht `None` zurück geben und 9 ist sicher keine Primzahl.
@ Sirius3: Wieder bei i=1 anzufangen ist tatsächlich merkwürdig.

Ich habe versucht alle Bemerkungen zu berücksichtigen: (außer den Funktionsnamenskonventionen.)

Code: Alles auswählen

import math
import collections


def Primtest(kandidat):
    if kandidat<2:
        return False
    for i in range(2,int((math.sqrt(kandidat))+1)):
        if kandidat%i==0:
            return False
    else:
        return True


def Basehochexp(primefactors):
    ausgabe=[]
    for k, v in sorted(collections.Counter(primefactors).items()):
        if v==1:
            ausgabe.append(str(k))
            print(k)
        else:
            ausgabe.append("(%d^%d)" % (k, v))
    exponentenschreibweise='*'.join(ausgabe)

    return exponentenschreibweise

def Primfaktorzerlegung(eingabe):
    primfaktoren=[] 
    i=2  
    while not Primtest(eingabe):  
        if eingabe%i==0 and Primtest(i):  
            eingabe=eingabe//i
            primfaktoren.append(i) 
        i=i+1
    primfaktoren.append(eingabe)
    return primfaktoren

while(True):
    x=int(input("Bitte geben sie eine Zahl ein:"))
    print("Die Primfaktoren Zerlegung lautet", Basehochexp(Primfaktorzerlegung(x)))
    z=int(input("0 für nochmal "))
    if z!=0:
        break
Ein Primzahltest braucht man dazu zum Beispiel überhaupt nicht. Man fängt einfach an bei 2 aufwärts zu testen ob man teilen kann und falls ja tut man das so oft wie es geht und merkt sich die Teiler. Fertig ist man, wenn die Zahl die man noch zerlegen muss die 1 ist. Und eigentlich könnte man an der Stelle auch schon zählen wie oft man durch eine Zahl teilen konnte, dann braucht man den `Counter` später gar nicht erst.
Worin würdest du die Basis Exponenten Paare speichern? In einem "Array" oder einem Dicitionary?
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Habe jetzt mal BlackJacks Vorschlag umgesetzt.

Code: Alles auswählen

def Primfaktorzerlegung(eingabe):
    primfaktoren=[]
    ausgabe=[]
    i=2
    j=1
    while eingabe!=1:
        if eingabe%i==0:
            
            eingabe=eingabe//i
            while eingabe%i==0:
                eingabe=eingabe//i
                j=j+1
            if j>1:
                primfaktoren.append("(%d^%d)" % (i, j))
                j=1
            else:
                primfaktoren.append(str(i))

        i=i+1
        ausgabe='*'.join(primfaktoren)
    return ausgabe
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: schau mal wo ausgabe erzeugt wird und ob es da hingehört. Es ist ja löblich, dass Du versuchst, so wenig Zuweisungen zu j zu machen wie möglich, aber besser lesbar wäre es, j for der zweiten while-Schleife auf 1 zu setzen und nicht an Stellen, wo j gar nicht mehr gebraucht wird.
nyxx
User
Beiträge: 11
Registriert: Sonntag 29. März 2015, 23:51

Danke für die Anmerkung, ich habe auch nochmal eine andere Variante geschrieben:

Code: Alles auswählen

def Primfaktorzerlegung(eingabe):
    primfaktoren=[0]* eingabe
    faktorenundexponenten=[]
    ausgabe=[]
    i=2
    j=1
    
    while eingabe!=1:
        if eingabe%i==0:
            eingabe=eingabe//i
            while eingabe%i==0:
                eingabe=eingabe//i
                j=j+1
            primfaktoren[i]=j
        j=1
        i=i+1

    for i in (i for i, t in enumerate(primfaktoren) if t):
        faktorenundexponenten.append("(%d^%d)" % (i, primfaktoren[i]))
        
    ausgabe='*'.join(faktorenundexponenten)

    return ausgabe

Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@nyxx: jetzt hast Du das Über-Index-Iterieren-Antipattern schön versteckt umgesetzt. Die passende Datenstruktur für primfaktoren wäre hier ein Wörterbuch, oder gleich ein Generator, weil die Zwischenspeicherung eigentlich unnötig ist.
Antworten