Ich möchte ein Programm schreiben, welches, wie im Spiel Rechen Fuchs, aus 5 Ziffern eine Zahl berechnet mit Addition, Subtraktion, Division und Multiplikation. Ich habe bereits angefangen. Jedoch kann ich erst mit 2 Ziffern eine bestimmte Zahl berechnen. Hätte jemand eine Idee, wie man das Ganze mit 5 Ziffern machen kann ohne alle möglichen Kombinationen zu schreiben?
ziel=input("Welche Zahl soll errechnet werden?")
z1=input("1. Zahl, die zur Verfügung steht:")
z2=input("2. Zahl, die zur Verfügung steht:")
z3=input("3. Zahl, die zur Verfügung steht:")
z4=input("4. Zahl, die zur Verfügung steht:")
def kombzwei(a,b,zielzahl):
if a+b==zielzahl:
print(str(a)+" + "+str(b)+" = "+str(zielzahl))
if a-b==zielzahl:
print(str(a)+" - "+str(b)+" = "+str(zielzahl))
if b-a==zielzahl:
print(str(b)+" - "+str(a)+" = "+str(zielzahl))
if a*b==zielzahl:
print(str(a)+" * "+str(b)+" = "+str(zielzahl))
if a/b==zielzahl:
print(str(a)+" / "+str(b)+" = "+str(zielzahl))
if b/a==zielzahl:
print(str(b)+" / "+str(a)+" = "+str(zielzahl))
def kombdrei(a,b,c,zielzahl):
if a+b+c==zielzahl:
print(str(a)+" + "+str(b)+" + "+str(c)+" = "+str(zielzahl))
kombzwei(z1,z2,ziel)
kombzwei(z1,z3,ziel)
kombzwei(z1,z4,ziel)
kombzwei(z2,z3,ziel)
kombzwei(z2,z4,ziel)
kombzwei(z3,z4,ziel)
kombdrei(z1,z2,z3,ziel)
Rechner
@Steazy,
ich bin mir nicht ganz sicher was die genauen Regeln von Rechen Fuchs sind.
Folgende Lösung ist unter der Annahme entstanden, dass
mindestens zwei, maximal fünf einstellige Ganzzahlen eingegeben werden können,
jeder Rechenoperator mindestens null, maximal ein mal vorkommen darf.
Außerdem wird nicht berücksichtig, dass das:
2 · 5 = 10
5 · 2 = 10
eigentlich nur eine Rechenaufgabe ist.
Die Idee beruht darauf, dass man durch Python's itertools
https://docs.python.org/3/library/itert ... -itertools
Die möglichen Permutationen aller eingegebenen Zahlen und
die möglichen Permutationen aller Rechenoperatoren bestimmt.
Dann wird aus diesen das kartesische Produkt gebildet.
Theoretisch hat man dann schon alle möglichen Rechenaufgaben.
Mit dem sympy Paket für symbolische Mathematik
https://www.sympy.org/en/index.html
kann man dann die Rechenaufgaben ausrechnen. Das hat den Vorteil, dass zum Beispiel Punkt-vor-Strichrechnung beachtet wird.
Es werden nur die Rechenaufgaben ausgegeben, die als Ergebnis die gewünschte Zielzahl berechnen.
Die Hälfte des Programms besteht nur daraus die Benutzereingabe zu holen und einigermaßen zu validieren. Daher sieht es vielleicht etwas komplizierter aus als es ist.
ich bin mir nicht ganz sicher was die genauen Regeln von Rechen Fuchs sind.
Folgende Lösung ist unter der Annahme entstanden, dass
mindestens zwei, maximal fünf einstellige Ganzzahlen eingegeben werden können,
jeder Rechenoperator mindestens null, maximal ein mal vorkommen darf.
Außerdem wird nicht berücksichtig, dass das:
2 · 5 = 10
5 · 2 = 10
eigentlich nur eine Rechenaufgabe ist.
Die Idee beruht darauf, dass man durch Python's itertools
https://docs.python.org/3/library/itert ... -itertools
Die möglichen Permutationen aller eingegebenen Zahlen und
die möglichen Permutationen aller Rechenoperatoren bestimmt.
Dann wird aus diesen das kartesische Produkt gebildet.
Theoretisch hat man dann schon alle möglichen Rechenaufgaben.
Mit dem sympy Paket für symbolische Mathematik
https://www.sympy.org/en/index.html
kann man dann die Rechenaufgaben ausrechnen. Das hat den Vorteil, dass zum Beispiel Punkt-vor-Strichrechnung beachtet wird.
Es werden nur die Rechenaufgaben ausgegeben, die als Ergebnis die gewünschte Zielzahl berechnen.
Die Hälfte des Programms besteht nur daraus die Benutzereingabe zu holen und einigermaßen zu validieren. Daher sieht es vielleicht etwas komplizierter aus als es ist.
Code: Alles auswählen
from math import isclose
from itertools import permutations, product, zip_longest
from sympy import sympify
OPERATOR_MAPPING = {
"+": "+",
"-": "-",
"*": "\u00b7",
"/": "\u00f7",
}
def get_target_input():
while True:
try:
target = int(input("Welche Zahl soll errechnet werden? "))
except ValueError:
print("Bitte nur Zahlen eingeben!")
else:
return target
def get_number_input():
while True:
number_input = input(
"Durch Leerzeichen getrennte Ganzzahlen von 1 bis 9 eingeben: "
)
numbers = number_input.split()
if len(numbers) < 2:
print("Die Eingabe muss mindestens 2 Ganzzahlen enthalten!")
elif len(numbers) > 5:
print("Die Eingabe darf nicht mehr als 5 Ganzzahlen enthalten!")
else:
try:
map(int, numbers)
except ValueError:
print("Bitte nur Zahlen eingeben!")
else:
if not all(len(number) == 1 for number in numbers):
print("Bitte nur einstellige Zahlen eingeben!")
return numbers
def find_calculations():
target_number = get_target_input()
digits = get_number_input()
operators = ["+", "-", "*", "/"]
for digit_permutation, operator_permutation in product(
permutations(digits), permutations(operators, r=len(digits) - 1)
):
str_expr = " ".join(
f"{digit} {operator}"
for digit, operator in zip_longest(
digit_permutation,
operator_permutation,
fillvalue="",
)
)
expr = sympify(str_expr)
result = expr.evalf()
if isclose(result, target_number, abs_tol=0):
output_expr = " ".join(
f"{digit} {OPERATOR_MAPPING[operator]}"
for digit, operator in zip(
digit_permutation[:-1],
operator_permutation,
)
)
yield f"{output_expr} {digit_permutation[-1]} = {result:0.0f}"
def main():
for calculation in find_calculations():
print(calculation)
if __name__ == "__main__":
main()
"""
Beispiel Ausgabe:
Welche Zahl soll errechnet werden? 10
Durch Leerzeichen getrennte Ganzzahlen von 1 bis 9 eingeben: 1 2 3 4 5
2 ÷ 1 · 4 - 3 + 5 = 10
2 ÷ 1 · 4 + 5 - 3 = 10
2 · 4 ÷ 1 - 3 + 5 = 10
2 · 4 ÷ 1 + 5 - 3 = 10
2 · 4 - 3 ÷ 1 + 5 = 10
2 · 4 - 3 + 5 ÷ 1 = 10
2 · 4 + 5 ÷ 1 - 3 = 10
2 · 4 + 5 - 3 ÷ 1 = 10
3 ÷ 2 · 4 - 1 + 5 = 10
3 ÷ 2 · 4 + 5 - 1 = 10
3 · 4 ÷ 2 - 1 + 5 = 10
3 · 4 ÷ 2 + 5 - 1 = 10
4 ÷ 1 · 2 - 3 + 5 = 10
4 ÷ 1 · 2 + 5 - 3 = 10
4 · 2 ÷ 1 - 3 + 5 = 10
4 · 2 ÷ 1 + 5 - 3 = 10
4 · 2 - 3 ÷ 1 + 5 = 10
4 ÷ 2 · 3 - 1 + 5 = 10
4 · 2 - 3 + 5 ÷ 1 = 10
4 ÷ 2 · 3 + 5 - 1 = 10
4 · 2 + 5 ÷ 1 - 3 = 10
4 · 2 + 5 - 3 ÷ 1 = 10
4 · 3 ÷ 2 - 1 + 5 = 10
4 · 3 ÷ 2 + 5 - 1 = 10
5 ÷ 1 + 2 · 4 - 3 = 10
5 - 1 + 3 ÷ 2 · 4 = 10
5 ÷ 1 - 3 + 2 · 4 = 10
5 - 1 + 3 · 4 ÷ 2 = 10
5 ÷ 1 - 3 + 4 · 2 = 10
5 - 1 + 4 ÷ 2 · 3 = 10
5 ÷ 1 + 4 · 2 - 3 = 10
5 - 1 + 4 · 3 ÷ 2 = 10
5 + 2 ÷ 1 · 4 - 3 = 10
5 + 2 · 4 ÷ 1 - 3 = 10
5 + 2 · 4 - 3 ÷ 1 = 10
5 - 3 ÷ 1 + 2 · 4 = 10
5 - 3 ÷ 1 + 4 · 2 = 10
5 - 3 + 2 ÷ 1 · 4 = 10
5 + 3 ÷ 2 · 4 - 1 = 10
5 - 3 + 2 · 4 ÷ 1 = 10
5 - 3 + 4 ÷ 1 · 2 = 10
5 + 3 · 4 ÷ 2 - 1 = 10
5 - 3 + 4 · 2 ÷ 1 = 10
5 + 4 ÷ 1 · 2 - 3 = 10
5 + 4 · 2 ÷ 1 - 3 = 10
5 + 4 · 2 - 3 ÷ 1 = 10
5 + 4 ÷ 2 · 3 - 1 = 10
5 + 4 · 3 ÷ 2 - 1 = 10
"""
@Steazy: Das ist ein perfektes Beispiel für Rekursion. Du weißt, wie du das Problem für 2 Zahlen lösen kannst (Rekursionsende), und um das Problem für 3 (bzw. allgemein n) Zahlen zu lösen, musst du das Problem für 2 (bzw. allgemein n - 1) Zahlen lösen, und dann nochmal für die eine übrig gebliebene Zahl zusammen mit dem Ergebnis des Rekursionsschrittes:
Ich bin dabei davon ausgegangen, dass beliebige Klammerungen erlaubt sind und dass `a * b` und `b * a` unterschiedliche Aufgaben sind:
Wenn du das nicht willst, müsstest du `iter_terms` das Kommutativ- und Assoziativgesetz beibringen.
@rogerb: `map` ist lazy, wie kann da jemals ein `ValueError` geworfen werden? Und das `return` danach sollte doch eigentlich auch im `else`-Block stehen?
Code: Alles auswählen
#!/usr/bin/env python3
from contextlib import suppress
from fractions import Fraction
from operator import add
from operator import mul
from operator import sub
from operator import truediv
from attr import attrib
from attr import attrs
OPERATORS = {"+": add, "-": sub, "*": mul, "/": truediv}
@attrs(frozen=True)
class Term:
lhs = attrib()
operator = attrib()
rhs = attrib()
def eval(self):
return OPERATORS[self.operator](self.lhs.eval(), self.rhs.eval())
def __str__(self):
return f"({self.lhs} {self.operator} {self.rhs})"
@attrs(frozen=True)
class Literal:
value = attrib()
def eval(self):
return self.value
def __str__(self):
return str(self.value)
def iter_terms(lhs, rhs):
for op in OPERATORS:
yield Term(lhs, op, rhs)
yield Term(rhs, op, lhs)
def combinations(numbers):
if len(numbers) == 2:
yield from iter_terms(*numbers)
else:
for i, number in enumerate(numbers):
rest = numbers[:i] + numbers[i + 1:]
for term in combinations(rest):
yield from iter_terms(number, term)
def main():
numbers = [Literal(Fraction(n)) for n in range(1, 6)]
for term in combinations(numbers):
with suppress(ZeroDivisionError):
if term.eval() == 10:
print(term)
if __name__ == "__main__":
main()
Code: Alles auswählen
$ ./main.py | wc -l
3580
$ ./main.py | shuf | head
(5 - (2 - (4 + (1 * 3))))
((5 / (4 - (1 * 3))) * 2)
((2 * (4 / (3 + 1))) * 5)
((((3 / 1) + 4) - 2) + 5)
(5 * (2 / (4 - (3 / 1))))
(4 + (((3 * 1) + 5) - 2))
(1 + ((5 - (4 - 2)) * 3))
(((3 - (4 - 5)) + 1) * 2)
(5 * ((2 * (1 * 3)) - 4))
(((2 + (4 / 1)) / 3) * 5)
@rogerb: `map` ist lazy, wie kann da jemals ein `ValueError` geworfen werden? Und das `return` danach sollte doch eigentlich auch im `else`-Block stehen?
@narpfel,
Danke, hatte ich übersehen. So müsste es passen:
Danke, hatte ich übersehen. So müsste es passen:
Code: Alles auswählen
from math import isclose
from itertools import permutations, product, zip_longest
from sympy import sympify
OPERATOR_MAPPING = {
"+": "+",
"-": "-",
"*": "\u00b7",
"/": "\u00f7",
}
def get_target_input():
while True:
try:
target = int(input("Welche Zahl soll errechnet werden? "))
except ValueError:
print("Bitte nur Zahlen eingeben!")
else:
return target
def get_number_input():
while True:
number_input = input(
"Durch Leerzeichen getrennte Ganzzahlen von 1 bis 9 eingeben: "
)
numbers = number_input.split()
if len(numbers) < 2:
print("Die Eingabe muss mindestens 2 Ganzzahlen enthalten!")
elif len(numbers) > 5:
print("Die Eingabe darf nicht mehr als 5 Ganzzahlen enthalten!")
else:
try:
list(map(int, numbers))
except ValueError:
print("Bitte nur Zahlen eingeben!")
else:
if not all(len(number) == 1 for number in numbers):
print("Bitte nur einstellige Zahlen eingeben!")
return numbers
def find_calculations():
target_number = get_target_input()
digits = get_number_input()
operators = ["+", "-", "*", "/"]
for digit_permutation, operator_permutation in product(
permutations(digits), permutations(operators, r=len(digits) - 1)
):
str_expr = " ".join(
f"{digit} {operator}"
for digit, operator in zip_longest(
digit_permutation,
operator_permutation,
fillvalue="",
)
)
expr = sympify(str_expr)
result = expr.evalf()
if isclose(result, target_number, abs_tol=0):
output_expr = " ".join(
f"{digit} {OPERATOR_MAPPING[operator]}"
for digit, operator in zip(
digit_permutation[:-1],
operator_permutation,
)
)
yield f"{output_expr} {digit_permutation[-1]} = {result:0.0f}"
def main():
for calculation in find_calculations():
print(calculation)
if __name__ == "__main__":
main()
"""
Beispiel Ausgabe:
Welche Zahl soll errechnet werden? 10
Durch Leerzeichen getrennte Ganzzahlen von 1 bis 9 eingeben: 1 2 3 4 5
2 ÷ 1 · 4 - 3 + 5 = 10
2 ÷ 1 · 4 + 5 - 3 = 10
2 · 4 ÷ 1 - 3 + 5 = 10
2 · 4 ÷ 1 + 5 - 3 = 10
2 · 4 - 3 ÷ 1 + 5 = 10
2 · 4 - 3 + 5 ÷ 1 = 10
2 · 4 + 5 ÷ 1 - 3 = 10
2 · 4 + 5 - 3 ÷ 1 = 10
3 ÷ 2 · 4 - 1 + 5 = 10
3 ÷ 2 · 4 + 5 - 1 = 10
3 · 4 ÷ 2 - 1 + 5 = 10
3 · 4 ÷ 2 + 5 - 1 = 10
4 ÷ 1 · 2 - 3 + 5 = 10
4 ÷ 1 · 2 + 5 - 3 = 10
4 · 2 ÷ 1 - 3 + 5 = 10
4 · 2 ÷ 1 + 5 - 3 = 10
4 · 2 - 3 ÷ 1 + 5 = 10
4 ÷ 2 · 3 - 1 + 5 = 10
4 · 2 - 3 + 5 ÷ 1 = 10
4 ÷ 2 · 3 + 5 - 1 = 10
4 · 2 + 5 ÷ 1 - 3 = 10
4 · 2 + 5 - 3 ÷ 1 = 10
4 · 3 ÷ 2 - 1 + 5 = 10
4 · 3 ÷ 2 + 5 - 1 = 10
5 ÷ 1 + 2 · 4 - 3 = 10
5 - 1 + 3 ÷ 2 · 4 = 10
5 ÷ 1 - 3 + 2 · 4 = 10
5 - 1 + 3 · 4 ÷ 2 = 10
5 ÷ 1 - 3 + 4 · 2 = 10
5 - 1 + 4 ÷ 2 · 3 = 10
5 ÷ 1 + 4 · 2 - 3 = 10
5 - 1 + 4 · 3 ÷ 2 = 10
5 + 2 ÷ 1 · 4 - 3 = 10
5 + 2 · 4 ÷ 1 - 3 = 10
5 + 2 · 4 - 3 ÷ 1 = 10
5 - 3 ÷ 1 + 2 · 4 = 10
5 - 3 ÷ 1 + 4 · 2 = 10
5 - 3 + 2 ÷ 1 · 4 = 10
5 + 3 ÷ 2 · 4 - 1 = 10
5 - 3 + 2 · 4 ÷ 1 = 10
5 - 3 + 4 ÷ 1 · 2 = 10
5 + 3 · 4 ÷ 2 - 1 = 10
5 - 3 + 4 · 2 ÷ 1 = 10
5 + 4 ÷ 1 · 2 - 3 = 10
5 + 4 · 2 ÷ 1 - 3 = 10
5 + 4 · 2 - 3 ÷ 1 = 10
5 + 4 ÷ 2 · 3 - 1 = 10
5 + 4 · 3 ÷ 2 - 1 = 10
"""
@rogerb: Jetzt kann man immer noch mehrstellige Zahlen eingeben. Und dass da eine Liste erzeugt wird, mit der dann nichts gemacht wird, ist IMHO auch nicht so schön. Ich würde das so schreiben:
Und `get_target_input` kann man auch einfacher schreiben:
Code: Alles auswählen
def get_number_input():
while True:
number_input = input(
"Durch Leerzeichen getrennte Ganzzahlen von 1 bis 9 eingeben: "
)
numbers = number_input.split()
if len(numbers) < 2:
print("Die Eingabe muss mindestens 2 Ganzzahlen enthalten!")
elif len(numbers) > 5:
print("Die Eingabe darf nicht mehr als 5 Ganzzahlen enthalten!")
else:
try:
numbers = [int(number) for number in numbers]
except ValueError:
print("Bitte nur Zahlen eingeben!")
else:
if not all(number in range(10) for number in numbers):
print("Bitte nur einstellige Zahlen eingeben!")
else:
return numbers
Code: Alles auswählen
def get_target_input():
while True:
try:
return int(input("Welche Zahl soll errechnet werden? "))
except ValueError:
print("Bitte nur Zahlen eingeben!")