Alle Permutationen mehrerer Listen mit Wiederholungen, aber mit bestimmten Voraussetzungen

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
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

Hallo zusammen.

Nach mehreren erfolglosen Versuchen, wende ich mich nun hier an die Mitglieder des Forums mit der Bitte um Hilfe.
Ich möchte gerne aus mehreren Listen von Zahlen alle möglichen Permutationen erhalten, allerdings unter bestimmten Vorgaben.
Bei den Listen handelt es sich um Zahlen und ihre Vielfache, also z.B. Vorgabe Wert = 1 und dazu die Vielfachen (z.B. 10 und 100), d.h. die Listen sind dann:
liste1 = [1, 10, 100]
liste2 = [2, 20, 200]
liste3 = [3, 30, 300]

Hier hätte ich nun gerne alle Permutationen, allerdings sollen Vielfache nur jeweils einmal vorkommen, d.h.
[1,2,3], [1,20,3], ... [200,1,3], [200,10,3], [200,100,3] ...
Die Liste soll aber NICHT die Elemente [1,10,2], [1,100,2], [10,1,2], ... [20,200,3], [200,20,3] usw. enthalten.

Ich kann mit den Standardbefehlen lediglich alle Permutationen ausrechnen lassen. Dann sind aber wirklich ALLE möglichen Einzellisten vorhanden, also auch Einzelwert und deren Vielfache. Wie kann ich das in Python umsetzen?

Es geht um folgende Idee:
Man hat drei Würfelwerte zwischen 1 und 6, dazu eine feste Zahl zwischen 1 und 99. Jetzt möchte ich alle möglichen Permutationen der drei Würfelwerte und deren Vielfachen von 10 und 100 berechnen lassen. Die Reihenfolge der Zahlen muss dabei beachtet werden, da im nachfolgenden Schritt auch noch jeweils eine bestimmte Rechenoperation zwischen den Einzelwerten erfolgen soll.

Vorab schon mal vielen Dank für Eure Hilfe.

Grüße
Peter
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Sind das Vielfache oder handelt es sich immer um das Zehn- bzw. Hundertfache?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Bei der Beschreibung der Idee steht “[…] dazu eine feste Zahl zwischen 1 und 99” – aber was mit dieser Zahl passieren soll, wird nicht weiter beschrieben‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast also eine Faltung der Permutation von 1 bis 6 mit Wiederholungen und der Faktoren 1, 10, 100 ohne Wiederholung.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich habe da mal was in (Un)Icon programmiert:

Code: Alles auswählen

link lists

procedure lproduct(L, r)
    if r = 0 then suspend []
    else every xs := lproduct(L, r - 1) do suspend xs ||| [!L]
end

procedure whatever(L)
    every P := lpermute(L) & E := lproduct([: 0 to *L - 1 :], *L) do
        suspend [: i := 1 to *L & P[i] * 10^E[i] :]
end

procedure main(args)
    every write(limage(whatever([1, 2, 3])))
end
Ist diese Ausgabe das was Du willst?

Code: Alles auswählen

[1,2,3]
[1,2,30]
[1,2,300]
[1,20,3]
[1,20,30]
[1,20,300]
[1,200,3]
[1,200,30]
[1,200,300]
[10,2,3]
[10,2,30]
[10,2,300]
[10,20,3]
[10,20,30]
[10,20,300]
[10,200,3]
[10,200,30]
[10,200,300]
[100,2,3]
[100,2,30]
[100,2,300]
[100,20,3]
[100,20,30]
[100,20,300]
[100,200,3]
[100,200,30]
[100,200,300]
[1,3,2]
[1,3,20]
[1,3,200]
[1,30,2]
[1,30,20]
[1,30,200]
[1,300,2]
[1,300,20]
[1,300,200]
[10,3,2]
[10,3,20]
[10,3,200]
[10,30,2]
[10,30,20]
[10,30,200]
[10,300,2]
[10,300,20]
[10,300,200]
[100,3,2]
[100,3,20]
[100,3,200]
[100,30,2]
[100,30,20]
[100,30,200]
[100,300,2]
[100,300,20]
[100,300,200]
[2,1,3]
[2,1,30]
[2,1,300]
[2,10,3]
[2,10,30]
[2,10,300]
[2,100,3]
[2,100,30]
[2,100,300]
[20,1,3]
[20,1,30]
[20,1,300]
[20,10,3]
[20,10,30]
[20,10,300]
[20,100,3]
[20,100,30]
[20,100,300]
[200,1,3]
[200,1,30]
[200,1,300]
[200,10,3]
[200,10,30]
[200,10,300]
[200,100,3]
[200,100,30]
[200,100,300]
[2,3,1]
[2,3,10]
[2,3,100]
[2,30,1]
[2,30,10]
[2,30,100]
[2,300,1]
[2,300,10]
[2,300,100]
[20,3,1]
[20,3,10]
[20,3,100]
[20,30,1]
[20,30,10]
[20,30,100]
[20,300,1]
[20,300,10]
[20,300,100]
[200,3,1]
[200,3,10]
[200,3,100]
[200,30,1]
[200,30,10]
[200,30,100]
[200,300,1]
[200,300,10]
[200,300,100]
[3,1,2]
[3,1,20]
[3,1,200]
[3,10,2]
[3,10,20]
[3,10,200]
[3,100,2]
[3,100,20]
[3,100,200]
[30,1,2]
[30,1,20]
[30,1,200]
[30,10,2]
[30,10,20]
[30,10,200]
[30,100,2]
[30,100,20]
[30,100,200]
[300,1,2]
[300,1,20]
[300,1,200]
[300,10,2]
[300,10,20]
[300,10,200]
[300,100,2]
[300,100,20]
[300,100,200]
[3,2,1]
[3,2,10]
[3,2,100]
[3,20,1]
[3,20,10]
[3,20,100]
[3,200,1]
[3,200,10]
[3,200,100]
[30,2,1]
[30,2,10]
[30,2,100]
[30,20,1]
[30,20,10]
[30,20,100]
[30,200,1]
[30,200,10]
[30,200,100]
[300,2,1]
[300,2,10]
[300,2,100]
[300,20,1]
[300,20,10]
[300,20,100]
[300,200,1]
[300,200,10]
[300,200,100]
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Kleine Korrektur/Erweiterung des Programms. Man kann jetzt angeben wie viele Elemente permutiert werden (`n`) und mit wie vielen 10er-Potenzen das kombiniert werden soll (`m`). Voreinstellung bleibt 3 und 2.

Code: Alles auswählen

link lists

procedure lproduct(L, r)
    /r := *L
    if r = 0 then suspend []
    else every xs := lproduct(L, r - 1) do suspend xs ||| [!L]
end

procedure whatever(L, m)
    every P := lpermute(L) & E := lproduct([: 0 to m :]) do
        suspend [: i := 1 to *L & P[i] * 10^E[i] :]
end

procedure main(args)
    n := \args[1] | 3
    m := \args[2] | 2
    every write(limage(whatever([: 1 to n :], m)))
end
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

Vielen Dank für die Antworten. Ich werde das mal ausprobieren und mich wieder melden, wird aber leider erst nächste Woche werden.

Die Verarbeitung der Zahl zwischen 1 und 99 werde ich später behandeln. Zunächst ist die korrekte Auflistung aller möglichen Kombinationen wichtig.

Nochmals vielen Dank und bis spätestens nächste Woche 👍🏻
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

Hallo zusammen.

Ich konnte die Ausgabe oben mittlerweile durchschauen und es sind genau die richtigen Kombinationen, die ich gerne erhalten würde (insgesamt 162 Stück). Da ich aber ein absoluter Python-Neuling bin, kann mir jemand helfen, wie ich das Programm in Python umgesetzt kriege?

Mein weiterer Plan als Übung für mich:
Ich möchte dann mit diesen Ergebnissen jeweils alle Möglichkeiten betrachten, die sich ergeben, wenn auf diese Liste jeweils zwei der vier Grundrechenarten (+, -, *, /) angewendet werden. Das heißt zum Beispiel "(1 + 2) * 3" usw. Hierbei gilt nicht Punkt vor Strich sondern nur die Reihenfolge der Zahlen in den Listen. Die möglichen Kombinationen sind dann 16 * 162 = 2592 Stück.
Diese Zahlen werden dann mit der Zahl zwischen 1 und 99 verglichen und diese Kombinationen ausgegeben, die möglichst nah an dieser Zahl liegen. Die Differenz wird notiert und sollte möglichst Null sein.

Wir spielen dieses Spiel öfter in Urlauben und man trainiert sehr gut sein Kopfrechnen. Irgendwann kam dann die Idee auf, es wäre schön, alle möglichen Kombinationen, die zum Ziel führen zu finden. Deshalb nun dieses Programm ...

Vielen Dank für Eure Hilfe und Grüße
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@hohlinger: bisher hast Du noch keine einzige Zeile Code gezeigt. Wie soll man da wissen, wo Du nicht weiter kommst?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Einen Generator für das Kreuzprodukt muss man sich in Python nicht selbst schreiben, den gibt's schon als `itertools.product`. In `itertools` findet man auch etwas für die Permutationen.

Icon's ``suspend`` ist sowohl so etwas wie Python's ``yield`` als auch ein Schleifenkonstrukt wie ``every`` in Icon. Die Idee für Generatorfunktionen haben die Python-Entwickler übrigens von Icon übernommen.

Icon's ``[: … :]`` ist ähnlich wie eine „list comprehension“ in Python. Für eine Lösung in Python ist auch die `zip()`-Funktion nützlich, denn in idiomatischem Python hantiert man weniger bis gar nicht mit Laufindexvariablen.

Das (Un)Icon-Programm mal um eine Klasse für Rechenausdrücke erweitert und die Ausgabe ist nach dem Ergebnis der möglichen Rechenausdrücke sortiert:

Code: Alles auswählen

link lists

global OPERANDS

class Expression(operands, operators)

    method evaluate()
        x := real(operands[1])
        every i := 1 to *operators do x := operators[i](x, operands[i + 1])
        return x
    end
    
    method toString()
        s := operands[1]
        every i := 1 to *operators do
            s ||:= " " || operators[i] || " " || operands[i + 1]
        return s
    end
end

procedure lproduct(L, r)
    if r = 0 then suspend []
    else every xs := lproduct(L, r - 1) do suspend xs ||| [!L]
end

procedure generateOperands(L, m)
    every P := lpermute(L) & E := lproduct([: 0 to m :], *L) do
        suspend [: i := 1 to *L & P[i] * 10^E[i] :]
end

procedure generateExpressions(O)
    suspend Expression(ox := O, lproduct(OPERANDS, *ox - 1))
end

procedure main(args)
    OPERANDS := ["+", "-", "*", "/"]
    
    n := \args[1] | 3
    maxExp := \args[2] | 2
    expressions := [:
        e := generateExpressions(generateOperands([: 1 to n :], maxExp)) &
        [e, e.evaluate()]
    :]
    every r := !sortf(expressions, 2) do write(r[1].toString(), " = ", r[2])
end
Ausgabe: https://pastebin.com/ENALS7wc
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

Okay, sorry, hier ist der aktuelle Code mit der entsprechenden Ausgabe:


linie = 30 * ("-")

# from itertools import permutations
import itertools

# Zahl zwischen 1 und 99 eingeben:
zahl1 = input("Bitte eine Zahl zwischen 1 und 99 eingeben:")

# gewürfelte Zahlen eingeben:
w1 = input("Würfel 1:")
w2 = input("Würfel 2:")
w3 = input("Würfel 3:")

# gewürfelte Zahlen ausgeben:
print ("Sollwert:",zahl1)
print ("Gewürfelte Werte:",w1, w2, w3)
print ("")
print linie
print ("")

# Liste aus den gewürfelten Zahlen generieren:
# (d.h. alle Werte mal 10 bzw. mal 100 nehmen)
w10 = w1 * 10
w20 = w2 * 10
w30 = w3 * 10
w100 = w1 * 100
w200 = w2 * 100
w300 = w3 * 100

#w_liste = [w1,w2,w3]
a = [w1,w2,w3]
b = [w10,w20,w30]
c = [w100,w200,w300]

# Alle möglichen Rechen-Kombinationen:
rech_liste = ["+","-","*","/"]
j = 1
for entry_rech in list(itertools.product(rech_liste, repeat=2)):
print str(j) + ":", entry_rech
j += 1

# mögliche Kombinationen der Würfelergebnisse:
i = 1

# aus Liste a:
for entry_a in list(itertools.permutations(a, 3)):
print str(i) + ":", entry_a
i += 1

# aus Liste b:
for entry_b in list(itertools.permutations(b, 3)):
print str(i) + ":", entry_b
i += 1

# aus Liste c:
for entry_c in list(itertools.permutations(c, 3)):
print str(i) + ":", entry_c
i += 1

# Kreuzprodukte aus a, b und c:
for entry_kreuzabc in list(itertools.product(a, b, c)):
print str(i) + ":", entry_kreuzabc
i += 1

for entry_kreuzacb in list(itertools.product(a, c, b)):
print str(i) + ":", entry_kreuzacb
i += 1

for entry_kreuzbac in list(itertools.product(b, a, c)):
print str(i) + ":", entry_kreuzbac
i += 1

for entry_kreuzbca in list(itertools.product(b, c, a)):
print str(i) + ":", entry_kreuzbca
i += 1

for entry_kreuzcab in list(itertools.product(c, a, b)):
print str(i) + ":", entry_kreuzcab
i += 1

for entry_kreuzcba in list(itertools.product(c, b, a)):
print str(i) + ":", entry_kreuzcba
i += 1
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

Und die Ausgabe:

('Sollwert:', 66)
('Gewürfelte Werte:', 1, 2, 3)

------------------------------

1: ('+', '+')
2: ('+', '-')
3: ('+', '*')
4: ('+', '/')
5: ('-', '+')
6: ('-', '-')
7: ('-', '*')
8: ('-', '/')
9: ('*', '+')
10: ('*', '-')
11: ('*', '*')
12: ('*', '/')
13: ('/', '+')
14: ('/', '-')
15: ('/', '*')
16: ('/', '/')
1: (1, 2, 3)
2: (1, 3, 2)
3: (2, 1, 3)
4: (2, 3, 1)
5: (3, 1, 2)
6: (3, 2, 1)
7: (10, 20, 30)
8: (10, 30, 20)
9: (20, 10, 30)
10: (20, 30, 10)
11: (30, 10, 20)
12: (30, 20, 10)
13: (100, 200, 300)
14: (100, 300, 200)
15: (200, 100, 300)
16: (200, 300, 100)
17: (300, 100, 200)
18: (300, 200, 100)
19: (1, 10, 100)
20: (1, 10, 200)
21: (1, 10, 300)
22: (1, 20, 100)
23: (1, 20, 200)
24: (1, 20, 300)
25: (1, 30, 100)
26: (1, 30, 200)
27: (1, 30, 300)
28: (2, 10, 100)
29: (2, 10, 200)
30: (2, 10, 300)
31: (2, 20, 100)
32: (2, 20, 200)
33: (2, 20, 300)
34: (2, 30, 100)
35: (2, 30, 200)
36: (2, 30, 300)
37: (3, 10, 100)
38: (3, 10, 200)
39: (3, 10, 300)
40: (3, 20, 100)
41: (3, 20, 200)
42: (3, 20, 300)
43: (3, 30, 100)
44: (3, 30, 200)
45: (3, 30, 300)
46: (1, 100, 10)
47: (1, 100, 20)
48: (1, 100, 30)
49: (1, 200, 10)
50: (1, 200, 20)
51: (1, 200, 30)
52: (1, 300, 10)
53: (1, 300, 20)
54: (1, 300, 30)
55: (2, 100, 10)
56: (2, 100, 20)
57: (2, 100, 30)
58: (2, 200, 10)
59: (2, 200, 20)
60: (2, 200, 30)
61: (2, 300, 10)
62: (2, 300, 20)
63: (2, 300, 30)
64: (3, 100, 10)
65: (3, 100, 20)
66: (3, 100, 30)
67: (3, 200, 10)
68: (3, 200, 20)
69: (3, 200, 30)
70: (3, 300, 10)
71: (3, 300, 20)
72: (3, 300, 30)
73: (10, 1, 100)
74: (10, 1, 200)
75: (10, 1, 300)
76: (10, 2, 100)
77: (10, 2, 200)
78: (10, 2, 300)
79: (10, 3, 100)
80: (10, 3, 200)
81: (10, 3, 300)
82: (20, 1, 100)
83: (20, 1, 200)
84: (20, 1, 300)
85: (20, 2, 100)
86: (20, 2, 200)
87: (20, 2, 300)
88: (20, 3, 100)
89: (20, 3, 200)
90: (20, 3, 300)
91: (30, 1, 100)
92: (30, 1, 200)
93: (30, 1, 300)
94: (30, 2, 100)
95: (30, 2, 200)
96: (30, 2, 300)
97: (30, 3, 100)
98: (30, 3, 200)
99: (30, 3, 300)
100: (10, 100, 1)
101: (10, 100, 2)
102: (10, 100, 3)
103: (10, 200, 1)
104: (10, 200, 2)
105: (10, 200, 3)
106: (10, 300, 1)
107: (10, 300, 2)
108: (10, 300, 3)
109: (20, 100, 1)
110: (20, 100, 2)
111: (20, 100, 3)
112: (20, 200, 1)
113: (20, 200, 2)
114: (20, 200, 3)
115: (20, 300, 1)
116: (20, 300, 2)
117: (20, 300, 3)
118: (30, 100, 1)
119: (30, 100, 2)
120: (30, 100, 3)
121: (30, 200, 1)
122: (30, 200, 2)
123: (30, 200, 3)
124: (30, 300, 1)
125: (30, 300, 2)
126: (30, 300, 3)
127: (100, 1, 10)
128: (100, 1, 20)
129: (100, 1, 30)
130: (100, 2, 10)
131: (100, 2, 20)
132: (100, 2, 30)
133: (100, 3, 10)
134: (100, 3, 20)
135: (100, 3, 30)
136: (200, 1, 10)
137: (200, 1, 20)
138: (200, 1, 30)
139: (200, 2, 10)
140: (200, 2, 20)
141: (200, 2, 30)
142: (200, 3, 10)
143: (200, 3, 20)
144: (200, 3, 30)
145: (300, 1, 10)
146: (300, 1, 20)
147: (300, 1, 30)
148: (300, 2, 10)
149: (300, 2, 20)
150: (300, 2, 30)
151: (300, 3, 10)
152: (300, 3, 20)
153: (300, 3, 30)
154: (100, 10, 1)
155: (100, 10, 2)
156: (100, 10, 3)
157: (100, 20, 1)
158: (100, 20, 2)
159: (100, 20, 3)
160: (100, 30, 1)
161: (100, 30, 2)
162: (100, 30, 3)
163: (200, 10, 1)
164: (200, 10, 2)
165: (200, 10, 3)
166: (200, 20, 1)
167: (200, 20, 2)
168: (200, 20, 3)
169: (200, 30, 1)
170: (200, 30, 2)
171: (200, 30, 3)
172: (300, 10, 1)
173: (300, 10, 2)
174: (300, 10, 3)
175: (300, 20, 1)
176: (300, 20, 2)
177: (300, 20, 3)
178: (300, 30, 1)
179: (300, 30, 2)
180: (300, 30, 3)


Hier sind eben immer noch die Möglichkeiten mit den Wiederholungen der Vielfachen drin, z.B. (300, 30, 3). Über die weitere Behandlung habe ich mir noch keine Gedanken gemacht, ich möchte zunächst eine korrekte Ausgabe der 162 Möglichkeiten.

Vielen Dank im Voraus für Eure Hilfe.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@hohlinger: Man sollte heute keine Programme mehr mit Python 2 anfangen.

``print`` ist in Python 2 eine Anweisung und keine Funktion, da gehören also keine Klammern um die ”Argumente” die ja keine sind. Sieht man auch schön an den ersten beiden Ausgaben, wo Du genau *einen* Wert ausgibst, nämlich ein Tupel.

In Python 2 sollte man kein `input()` verwenden sondern `raw_input()` und die Eingabe dann explizit in einen anderen Datentyp umwandeln. `input()` führt beliebige Python-Ausdrücke aus, die der Benutzer eingibt. Hatte ich schon erwähnt das man kein Python 2 mehr verwenden sollte?

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Konstanten schreibt man KOMPLETT_GROSS.

Importe gehören an den Anfang des Moduls. Gefolgt von Konstanten.

`zahl1` ist ein blöder Name. Total nichtssagend und eine überflüssige 1. Namen sollte man sowieso nicht nummerieren. Das ist in der Regel ein Zeichen das man sich bessere Namen ausdenken sollte oder eine Datenstruktur verwenden sollte. Oft ist das eine Liste.

Zu `zahl1`: Wenn das der Sollwert ist, wäre `sollwert` ein guter Name.

Und für `w1` bis `w3` wäre eine Liste geeigneter als Einzelnamen. Du packst Die Ergebnisse ja am Ende sowieso in eine Liste, warum also nicht gleich von Anfang an. Dann hätte man schon mal `a`. `b` und `c` lassen sich daraus berechnen. Und man sollte das nicht an einzelne Namen binden, sondern das ist doch auch wieder eine Liste.

Grunddatentypen gehören nicht in Namen. Wenn man den Datentyp im Laufe der Programmentwicklung ändert, muss man dann überall die betroffenen Namen ändern, oder man hat falsche und irreführende Namen im Quelltext stehen.

Man sollte auch keine Abkürzungen verwenden. Der Leser soll nicht raten müssen ob mit `rech` nun `rechenart`, `rechensymbol`, oder was ganz anderes gemeint ist.

Die ganzen `list()`-Aufrufe sind überflüssig. Das schöne an den Iteratoren auf die Du das anwendest ist ja gerade das die keine Liste mit allen Ergebnissen erstellen, sondern jedes Element erst dann erzeugen wenn man es anfragt. Und Du sammelst die Werte alle in Listen ohne tatsächlich jemals die komplette Liste auf einmal zu benötigen.

Wenn man zusätzlich zu einem iterarierbaren Objekt noch eine laufende Zahl benötigt, gibt es `enumerate()`. Da muss man sich nicht selbst einen über den Code verteilten Zähler basteln.

Zeichenkettenliterale und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC als Python. In Python verwendet man dafür Zeichenkettenformatierung mit der `format()`-Methode auf Zeichenketten.

Das mit dem ``i = 1`` und ``i += 1`` ist auch ziemlich nervig. Diesen Code würde man besser in eine Printfunktion kapseln die automatisch nummerierte Ausgaben erzeugt.

Der `entry_`-Präfix ist irreführend wenn man die überflüssigen Listen weg lässt, denn Iteratoren haben keine ”Einträge”. Der Präfix bringt aber auch mit Listen nicht wirklich einen Mehrwert.

Zwischenergebnis:

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import count, permutations, product

LINIE = '-' * 30


def create_numbering_print(start=1):
    numbers = count(start)
    def numbering_print(*args, **kwargs):
        print('{}:'.format(next(numbers)), *args, **kwargs)
    return numbering_print


def main():
    sollwert = int(input('Bitte eine Zahl zwischen 1 und 99 eingeben: '))
    wuerfelergebnisse = [
        int(input('Würfel {}: '.format(i))) for i in range(1, 4)
    ]

    print('Sollwert:', sollwert)
    print('Gewürfelte Werte:', wuerfelergebnisse)
    print()
    print(LINIE)
    print()

    multiplizierte_wuerfelergebnisse = [
        [w * 10**e for w in wuerfelergebnisse] for e in range(3)
    ]
    a, b, c = multiplizierte_wuerfelergebnisse

    rechensymbole = ['+', '-', '*', '/']
    for i, rechensymbol in enumerate(product(rechensymbole, repeat=2), 1):
        print('{}: {}'.format(i, rechensymbol))
    
    # mögliche Kombinationen der Würfelergebnisse:
    print(LINIE)
    numbering_print = create_numbering_print()

    for permutation in permutations(a):
        numbering_print(permutation)

    for permutation in permutations(b):
        numbering_print(permutation)

    for permutation in permutations(c):
        numbering_print(permutation)

    # Kreuzprodukte aus a, b und c:
    print(LINIE)
    numbering_print = create_numbering_print()

    for kreuzabc in product(a, b, c):
        numbering_print(kreuzabc)

    for kreuzacb in product(a, c, b):
        numbering_print(kreuzacb)

    for kreuzbac in product(b, a, c):
        numbering_print(kreuzbac)
        
    for kreuzbca in product(b, c, a):
        numbering_print(kreuzbca)
        
    for kreuzcab in product(c, a, b):
        numbering_print(kreuzcab)
        
    for kreuzcba in product(c, b, a):
        numbering_print(kreuzcba)


if __name__ == '__main__':
    main()
Ich habe hier die Namen `a`, `b`, und `c` gelassen, aber diese Einzelwerte sind ja genau der Grund warum Du da so verdammt viel schreiben bzw. kopieren und einfügen musstest. Bei den Kreuzprudukten der einzelnen Listen ist das ganz einfach durch eine Schleife über `multiplizierte_wuerfelergebnisse` lösbar, denn die Liste enthält `a`, `b`, und `c` ja als Elemente.

Bei den letzten sechs ``for``-Schleifen hast Du von Hand die Permutationen gebildet – dafür gibt es doch `permutations()`.

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import count, permutations, product

LINIE = '-' * 30


def create_numbering_print(start=1):
    numbers = count(start)
    def numbering_print(*args, **kwargs):
        print('{}:'.format(next(numbers)), *args, **kwargs)
    return numbering_print


def main():
    sollwert = int(input('Bitte eine Zahl zwischen 1 und 99 eingeben: '))
    wuerfelergebnisse = [
        int(input('Würfel {}: '.format(i))) for i in range(1, 4)
    ]

    print('Sollwert:', sollwert)
    print('Gewürfelte Werte:', wuerfelergebnisse)
    print()
    print(LINIE)
    print()

    multiplizierte_wuerfelergebnisse = [
        [w * 10**e for w in wuerfelergebnisse] for e in range(3)
    ]

    rechensymbole = ['+', '-', '*', '/']
    for i, rechensymbol in enumerate(product(rechensymbole, repeat=2), 1):
        print('{}: {}'.format(i, rechensymbol))
    
    # Mögliche Kombinationen der Würfelergebnisse:
    print(LINIE)
    numbering_print = create_numbering_print()

    for multipliertes_ergebnis_pro_wuerfel in multiplizierte_wuerfelergebnisse:
        for permutation in permutations(multipliertes_ergebnis_pro_wuerfel):
            numbering_print(permutation)

    # Kreuzprodukte aus multiplizierten Würfelergebnissen:
    print(LINIE)
    numbering_print = create_numbering_print()

    for permutation in permutations(multiplizierte_wuerfelergebnisse):
        for kreuzprodukt in product(*permutation):
            numbering_print(kreuzprodukt)


if __name__ == '__main__':
    main()
Ich würde ja dringend raten das in Funktionen aufzuteilen und nicht zu versuchen das alles in einem grossen klumpen Code zu lösen. Und auch keine mehrdimensionalen Datenstrukturen verwenden wenn das nicht nötig ist. Also `multiplizierte_wuerfelergebnisse` ist mir schon zu komplex als Zwischenergebnis. Ausgehend von einer Liste mit den drei Würfelergebnissen eine oder mehrere Generatorfunktionen die Zahlentripel generieren. Das geht analog zum Icon-Code auch in Python in einer Funktion mit zwei Schleifen und einer „list comprehension“. Damit kann man alle Operanden für die Rechnungen erzeugen.

Da die Würfe nicht verschieden sein müssen, im Extremfall kann man ja drei mal die gleiche Zahl würfeln, müsste man da trotzdem noch mal Duplikate raus filtern, also die generierten Zahlentripel in einem `set()` sammeln, beispielsweise, oder durch `more_itertools.unique_ever_seen()` filtern.

Und wenn man die Tripel alle hat, kann man sich eine Funktion schreiben, die daraus Ausdrücke generiert, also jeden Operanden-Satz mit den Rechenoperationen kombiniert. Spätestens hier wird dann eine Klasse interessant, wie man am Icon-Code sehen sehen kann.

In Python lohnt sich eventuell auch eine Klasse für die Rechenoperationen weil man hier nicht so wie in Icon einfach eine Zeichenkette mit dem Operatorsymbol auch als binäre Funktion verwenden kann. Die Operatoren als Funktionen kann man bei Python im `operator`-Modul finden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

@__blackjack__
Vielen Dank für Deine Hilfe und sorry für die vielen Fehler, aber wie gesagt versuche ich gerade mit Python anzufangen und habe mich wohl auf die falschen Tutorials verlassen. Ich versuche, das in Zukunft zu verbessern und Deine Tips zu berücksichtigen.
Mit Deinem Code kann ich erstmal starten und versuche nun als Nächstes die "Duplikate", d.h. beispielsweise [1,10,2] oder [30,30,1], also Zahlen, die sich oder mit ihren Vielfachen wiederholen (zweimal den gleichen Würfel verwendet), herauszufiltern. Mal sehen, ob ich das irgendwie hinkriege?!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Code: Alles auswählen

#!/usr/bin/env python3
from enum import Enum
from fractions import Fraction
from functools import total_ordering
from itertools import permutations, product
from operator import add, mul, sub

from attr import attrib, attrs
from more_itertools import interleave_longest, unique_everseen


@total_ordering
class Operators(Enum):
    ADD = ('+', add)
    SUB = ('-', sub)
    MUL = ('*', mul)
    DIV = ('/', Fraction)
    
    def __init__(self, symbol, function):
        self.symbol = symbol
        self.function = function
    
    def __str__(self):
        return self.symbol

    def __lt__(self, other):
        return self.value < other.value
    
    def apply(self, operand_a, operand_b):
        return self.function(operand_a, operand_b)


@attrs(frozen=True)
class Expression:
    operands = attrib(factory=lambda: (0,))
    operators = attrib(factory=tuple)
    result = attrib(init=False)

    def __attrs_post_init__(self):
        if len(self.operators) != len(self.operands) - 1:
            raise ValueError(
                'there must be exactly one more operand than operators'
            )
        operands = iter(self.operands)
        result = next(operands)
        for operator, operand in zip(self.operators, operands):
            result = operator.apply(result, operand)
        object.__setattr__(self, 'result', result)
    
    def __str__(self):
        return ' '.join(
            map(str, interleave_longest(self.operands, self.operators))
        )
    
    def distance(self, value):
        return abs(self.result - value)


def iter_operands(values, max_exponent=2):
    for permutation in permutations(values):
        for exponents in product(range(max_exponent + 1), repeat=len(values)):
            yield tuple(n * 10**e for n, e in zip(permutation, exponents))


def iter_expressions(operands, operators=Operators):
    for expression_operands in operands:
        for expression_operators in product(
            operators, repeat=len(expression_operands) - 1
        ):
            yield Expression(expression_operands, expression_operators)


def main():
    reference_value = 42
    die_rolls = [1, 2, 3]
    
    expressions = sorted(
        iter_expressions(unique_everseen(iter_operands(die_rolls, 2))),
        key=lambda e: (e.distance(reference_value), e)
    )
    for expression in expressions:
        print(
            f'{expression} = {expression.result}'
            f' (Abweichung {expression.distance(reference_value)})'
        )


if __name__ == '__main__':
    main()
Anfang der Ausgabe:

Code: Alles auswählen

2 + 10 + 30 = 42 (Abweichung 0)
2 + 30 + 10 = 42 (Abweichung 0)
10 + 2 + 30 = 42 (Abweichung 0)
10 + 30 + 2 = 42 (Abweichung 0)
30 + 2 + 10 = 42 (Abweichung 0)
30 + 10 + 2 = 42 (Abweichung 0)
3 - 1 * 20 = 40 (Abweichung 2)
20 - 10 + 30 = 40 (Abweichung 2)
20 + 30 - 10 = 40 (Abweichung 2)
20 + 100 / 3 = 40 (Abweichung 2)
30 - 10 * 2 = 40 (Abweichung 2)
30 - 10 + 20 = 40 (Abweichung 2)
30 + 20 - 10 = 40 (Abweichung 2)
100 + 20 / 3 = 40 (Abweichung 2)
10 - 2 + 30 = 38 (Abweichung 4)
10 + 30 - 2 = 38 (Abweichung 4)
30 - 2 + 10 = 38 (Abweichung 4)
30 + 10 - 2 = 38 (Abweichung 4)
100 / 2 - 3 = 47 (Abweichung 5)
2 + 10 * 3 = 36 (Abweichung 6)
10 + 2 * 3 = 36 (Abweichung 6)
100 - 3 / 2 = 97/2 (Abweichung 13/2)
100 / 3 + 2 = 106/3 (Abweichung 20/3)
10 / 2 + 30 = 35 (Abweichung 7)
20 - 1 + 30 = 49 (Abweichung 7)
20 + 30 - 1 = 49 (Abweichung 7)
30 - 1 + 20 = 49 (Abweichung 7)
30 + 20 - 1 = 49 (Abweichung 7)
100 / 20 + 30 = 35 (Abweichung 7)
100 - 30 / 2 = 35 (Abweichung 7)
1 * 20 + 30 = 50 (Abweichung 8)
1 * 30 + 20 = 50 (Abweichung 8)
2 + 3 * 10 = 50 (Abweichung 8)
2 * 10 + 30 = 50 (Abweichung 8)
2 * 30 - 10 = 50 (Abweichung 8)
2 + 100 / 3 = 34 (Abweichung 8)
3 + 2 * 10 = 50 (Abweichung 8)
3 * 10 + 20 = 50 (Abweichung 8)
3 * 20 - 10 = 50 (Abweichung 8)
10 * 2 + 30 = 50 (Abweichung 8)
10 * 3 + 20 = 50 (Abweichung 8)
20 * 1 + 30 = 50 (Abweichung 8)
20 / 1 + 30 = 50 (Abweichung 8)
20 * 3 - 10 = 50 (Abweichung 8)
20 + 30 * 1 = 50 (Abweichung 8)
20 + 30 / 1 = 50 (Abweichung 8)
30 * 1 + 20 = 50 (Abweichung 8)
30 / 1 + 20 = 50 (Abweichung 8)
30 * 2 - 10 = 50 (Abweichung 8)
30 + 20 * 1 = 50 (Abweichung 8)
30 + 20 / 1 = 50 (Abweichung 8)
100 + 2 / 3 = 34 (Abweichung 8)
100 - 20 - 30 = 50 (Abweichung 8)
100 - 30 - 20 = 50 (Abweichung 8)
200 / 10 + 30 = 50 (Abweichung 8)
200 + 300 / 10 = 50 (Abweichung 8)
300 / 2 - 100 = 50 (Abweichung 8)
300 / 10 + 20 = 50 (Abweichung 8)
300 + 200 / 10 = 50 (Abweichung 8)
200 - 100 / 3 = 100/3 (Abweichung 26/3)
1 + 2 + 30 = 33 (Abweichung 9)
1 + 20 + 30 = 51 (Abweichung 9)
1 + 30 + 2 = 33 (Abweichung 9)
1 + 30 + 20 = 51 (Abweichung 9)
2 + 1 + 30 = 33 (Abweichung 9)
2 + 30 + 1 = 33 (Abweichung 9)
3 + 10 + 20 = 33 (Abweichung 9)
3 + 20 + 10 = 33 (Abweichung 9)
10 + 3 + 20 = 33 (Abweichung 9)
10 + 20 + 3 = 33 (Abweichung 9)
20 + 1 + 30 = 51 (Abweichung 9)
20 + 3 + 10 = 33 (Abweichung 9)
20 + 10 + 3 = 33 (Abweichung 9)
20 + 30 + 1 = 51 (Abweichung 9)
30 + 1 + 2 = 33 (Abweichung 9)
30 + 1 + 20 = 51 (Abweichung 9)
30 + 2 + 1 = 33 (Abweichung 9)
30 + 20 + 1 = 51 (Abweichung 9)
100 - 2 / 3 = 98/3 (Abweichung 28/3)
3 + 100 / 2 = 103/2 (Abweichung 19/2)
100 + 3 / 2 = 103/2 (Abweichung 19/2)
1 * 2 + 30 = 32 (Abweichung 10)
1 * 30 + 2 = 32 (Abweichung 10)
2 * 1 + 30 = 32 (Abweichung 10)
2 / 1 + 30 = 32 (Abweichung 10)
2 + 30 * 1 = 32 (Abweichung 10)
2 + 30 / 1 = 32 (Abweichung 10)
3 * 10 + 2 = 32 (Abweichung 10)
10 * 3 + 2 = 32 (Abweichung 10)
20 / 10 + 30 = 32 (Abweichung 10)
20 + 300 / 10 = 32 (Abweichung 10)
30 * 1 + 2 = 32 (Abweichung 10)
30 / 1 + 2 = 32 (Abweichung 10)
30 + 2 * 1 = 32 (Abweichung 10)
30 + 2 / 1 = 32 (Abweichung 10)
200 / 100 + 30 = 32 (Abweichung 10)
300 / 10 + 2 = 32 (Abweichung 10)
300 + 20 / 10 = 32 (Abweichung 10)
…
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
hohlinger
User
Beiträge: 7
Registriert: Mittwoch 22. Mai 2019, 07:22

@ __blackjack__ :
Super! Vielen Dank für Deine Hilfe. Ich versuche gerade den Code zu verstehen ...
Antworten