Funktionierender Generator für Addieren / Subtrahieren ungleichnamiger Brüche
This commit is contained in:
parent
8f0c9ab93c
commit
f2797ae8d2
@ -15,13 +15,15 @@ ops = {
|
|||||||
|
|
||||||
# Konstanten
|
# Konstanten
|
||||||
# Anzahl der zu generierenden Fragen.
|
# Anzahl der zu generierenden Fragen.
|
||||||
NUM_QUESTIONS = 1
|
NUM_QUESTIONS = 5
|
||||||
# Algebraische Struktur der Aufgaben
|
# Algebraische Struktur der Aufgaben
|
||||||
STRUCTURE = "-(+)-"
|
STRUCTURE = "(-)-(+(-))"
|
||||||
|
# Gleiche Nenner erlauben
|
||||||
|
# TODO
|
||||||
# Größter Zähler
|
# Größter Zähler
|
||||||
MAX_NUMERATOR = 20
|
MAX_NUMERATOR = 10
|
||||||
# Größter Nenner
|
# Größter Nenner
|
||||||
MAX_DENOMINATOR = 20
|
MAX_DENOMINATOR = 8
|
||||||
# Größter erlaubter gemeinsamer Nenner
|
# Größter erlaubter gemeinsamer Nenner
|
||||||
MAX_COMMON_DENOMINATOR = 100
|
MAX_COMMON_DENOMINATOR = 100
|
||||||
# Dateiname für Ausgabe
|
# Dateiname für Ausgabe
|
||||||
@ -36,18 +38,18 @@ def parse_expression(expression):
|
|||||||
max_depth = 0
|
max_depth = 0
|
||||||
|
|
||||||
for i, char in enumerate(expression):
|
for i, char in enumerate(expression):
|
||||||
if char == '(':
|
# if char == '(':
|
||||||
paren_count += 1
|
# paren_count += 1
|
||||||
elif char == ')':
|
# elif char == ')':
|
||||||
paren_count -= 1
|
# paren_count -= 1
|
||||||
|
#
|
||||||
# Update max depth when a closed parenthesis is encountered
|
# # Update max depth when a closed parenthesis is encountered
|
||||||
max_depth = max(max_depth, paren_count)
|
# max_depth = max(max_depth, paren_count)
|
||||||
|
|
||||||
if char in ops.keys():
|
if char in ops.keys():
|
||||||
op_count += 1
|
op_count += 1
|
||||||
|
|
||||||
return max_depth + op_count
|
return max_depth + op_count + 1
|
||||||
|
|
||||||
# Anzahl der benötigten Brüche
|
# Anzahl der benötigten Brüche
|
||||||
NUM_FRACTIONS = parse_expression(STRUCTURE)
|
NUM_FRACTIONS = parse_expression(STRUCTURE)
|
||||||
@ -87,7 +89,7 @@ def add_placeholders(expression):
|
|||||||
need_placeholder = False # Nach einem Platzhalter sollten wir keinen weiteren setzen
|
need_placeholder = False # Nach einem Platzhalter sollten wir keinen weiteren setzen
|
||||||
|
|
||||||
# Platzhalter am Beginn hinzufügen
|
# Platzhalter am Beginn hinzufügen
|
||||||
if output and output[0] != f'n{0}':
|
if output and not (output[0] == f'n{0}' or output[0] == '('):
|
||||||
result = [placeholder_generator()] + output
|
result = [placeholder_generator()] + output
|
||||||
else:
|
else:
|
||||||
result = output
|
result = output
|
||||||
@ -99,6 +101,7 @@ def add_placeholders(expression):
|
|||||||
|
|
||||||
# Zusammensetzen des finalen Ausdrucks
|
# Zusammensetzen des finalen Ausdrucks
|
||||||
final_expression = ''.join(result).strip()
|
final_expression = ''.join(result).strip()
|
||||||
|
print(final_expression)
|
||||||
return final_expression
|
return final_expression
|
||||||
|
|
||||||
|
|
||||||
@ -119,6 +122,54 @@ def calculate(expression, numbers):
|
|||||||
# Evaluate the result
|
# Evaluate the result
|
||||||
return eval(expression)
|
return eval(expression)
|
||||||
|
|
||||||
|
def gen_calculate_string(expression :str, liststring :str):
|
||||||
|
# Replace placeholders with actual number values
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
placeholder = f'n{i}'
|
||||||
|
expression = re.sub(rf'\{placeholder}\s*([+-/*])', rf'{liststring}[{i}] \1', expression)
|
||||||
|
expression = re.sub(rf'({placeholder})\s*', rf'{liststring}[{i}]', expression)
|
||||||
|
|
||||||
|
print(expression)
|
||||||
|
# Return the changed expression
|
||||||
|
return expression
|
||||||
|
|
||||||
|
def gen_latex_calculate_string(expression :str, liststring: str):
|
||||||
|
# Replace placeholders with actual number values
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
placeholder = f'n{i}'
|
||||||
|
replacement = liststring[i]
|
||||||
|
print(replacement)
|
||||||
|
expression = re.sub(rf'\{placeholder}\s*([+-/*])', replacement, expression)
|
||||||
|
expression = re.sub(rf'({placeholder})\s*', replacement, expression)
|
||||||
|
|
||||||
|
# Klammern anpassen
|
||||||
|
expression = re.sub(rf'\(', u'\\\\left(', expression)
|
||||||
|
expression = re.sub(rf'\)', u'\\\\right)', expression)
|
||||||
|
# print(expression)
|
||||||
|
# Return the changed expression
|
||||||
|
return expression
|
||||||
|
|
||||||
|
def create_html_table_for_fracture_input(rows: list):
|
||||||
|
table_rows = []
|
||||||
|
# Tabellenzeilen festlegen
|
||||||
|
for i, row in enumerate(rows):
|
||||||
|
if (i % 2) == 0:
|
||||||
|
table_row = "<tr style='border-bottom: 1px solid black;'>"
|
||||||
|
else:
|
||||||
|
table_row = "<tr>"
|
||||||
|
|
||||||
|
for cell in row:
|
||||||
|
table_row += f"<td style='padding: 5px;'>{cell}</td>"
|
||||||
|
|
||||||
|
table_row += "</tr>"
|
||||||
|
table_rows.append(table_row)
|
||||||
|
|
||||||
|
# Tabelle zusammenbauen
|
||||||
|
table_html = f"<table style='border-collapse: collapse; display: inline-table;'>\n" + "\n".join(table_rows) + "</table>"
|
||||||
|
|
||||||
|
return table_html
|
||||||
|
|
||||||
|
|
||||||
def create_formula_question(ex_number: int):
|
def create_formula_question(ex_number: int):
|
||||||
numerators = []
|
numerators = []
|
||||||
denominators = []
|
denominators = []
|
||||||
@ -132,7 +183,12 @@ def create_formula_question(ex_number: int):
|
|||||||
same_denominators = [k for k,v in Counter(denominators).items() if v>1]
|
same_denominators = [k for k,v in Counter(denominators).items() if v>1]
|
||||||
while len(same_denominators) > 1:
|
while len(same_denominators) > 1:
|
||||||
new_denom = random.randint(2, MAX_DENOMINATOR)
|
new_denom = random.randint(2, MAX_DENOMINATOR)
|
||||||
denominators.replace(same_denominators[0], new_denom)
|
try:
|
||||||
|
index = denominators.index(same_denominators[0])
|
||||||
|
denominators[index] = new_denom
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
# denominators.replace(same_denominators[0], new_denom)
|
||||||
same_denominators = [k for k,v in Counter(denominators).items() if v>1]
|
same_denominators = [k for k,v in Counter(denominators).items() if v>1]
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
print(numerators)
|
print(numerators)
|
||||||
@ -165,11 +221,12 @@ def create_formula_question(ex_number: int):
|
|||||||
result_numerator = calculate(STRUCTURE_PH, expanded_numerators)
|
result_numerator = calculate(STRUCTURE_PH, expanded_numerators)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
print(result_numerator)
|
print(result_numerator)
|
||||||
|
print(cd)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
|
|
||||||
gcd_result = gcd(result_numerator, cd)
|
gcd_result = gcd(result_numerator, cd)
|
||||||
if gcd_result > 1:
|
if gcd_result > 1:
|
||||||
# print("kürzbar")
|
print("kürzbar")
|
||||||
shortable = True
|
shortable = True
|
||||||
shortened_numerator = result_numerator // gcd_result
|
shortened_numerator = result_numerator // gcd_result
|
||||||
shortened_denominator = common_denominator // gcd_result
|
shortened_denominator = common_denominator // gcd_result
|
||||||
@ -186,16 +243,16 @@ def create_formula_question(ex_number: int):
|
|||||||
has_div = '/' in STRUCTURE
|
has_div = '/' in STRUCTURE
|
||||||
|
|
||||||
if has_plus and has_minus:
|
if has_plus and has_minus:
|
||||||
expression_type = "Addition/Subtraktion"
|
expression_type = "Addition/Subtraktion" + f" ({STRUCTURE})"
|
||||||
exercise_text = "Berechne den Term"
|
exercise_text = "Berechne den Term"
|
||||||
elif has_plus:
|
elif has_plus:
|
||||||
expression_type = "Addition"
|
expression_type = "Addition" + f" ({STRUCTURE})"
|
||||||
exercise_text = "Addiere die Brüche"
|
exercise_text = "Addiere die Brüche"
|
||||||
elif has_minus:
|
elif has_minus:
|
||||||
expression_type = "Subtraktion"
|
expression_type = "Subtraktion" + f" ({STRUCTURE})"
|
||||||
exercise_text = "Subtrahiere die Brüche"
|
exercise_text = "Subtrahiere die Brüche"
|
||||||
else:
|
else:
|
||||||
expression_type = "Rechnung"
|
expression_type = "Rechnung" + f" ({STRUCTURE})"
|
||||||
exercise_text = "Berechne den Term"
|
exercise_text = "Berechne den Term"
|
||||||
name_elem = ET.SubElement(question, 'name')
|
name_elem = ET.SubElement(question, 'name')
|
||||||
text_elem = ET.SubElement(name_elem, 'text')
|
text_elem = ET.SubElement(name_elem, 'text')
|
||||||
@ -272,16 +329,160 @@ def create_formula_question(ex_number: int):
|
|||||||
ET.SubElement(answertype_elem, 'text').text = '0'
|
ET.SubElement(answertype_elem, 'text').text = '0'
|
||||||
numbox_elem = ET.SubElement(answers_elem, 'numbox')
|
numbox_elem = ET.SubElement(answers_elem, 'numbox')
|
||||||
ET.SubElement(numbox_elem, 'text').text = '2'
|
ET.SubElement(numbox_elem, 'text').text = '2'
|
||||||
return question
|
|
||||||
|
|
||||||
# Berechnungsvariablen
|
# Berechnungsvariablen
|
||||||
|
# common divisor
|
||||||
|
cdtext = ""
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
if i == 0:
|
||||||
|
pass
|
||||||
|
elif i == 1:
|
||||||
|
cdtext += f"cd{i} = lcm(d[{i}],d[{i-1}]);"
|
||||||
|
else:
|
||||||
|
cdtext += f"cd{i} = lcm(cd{i-1}, d[{i}]);"
|
||||||
|
cdtext += f"cd = cd{NUM_FRACTIONS-1};"
|
||||||
|
|
||||||
|
# expansions
|
||||||
|
extext = "e = ["
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
extext += f"cd / d[{i}],"
|
||||||
|
extext = extext[:-1]
|
||||||
|
extext += "];"
|
||||||
|
|
||||||
|
# expanded_numerators
|
||||||
|
entext = "ens = ["
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
entext += f"n[{i}]*e[{i}],"
|
||||||
|
entext = entext[:-1]
|
||||||
|
entext += "];"
|
||||||
|
|
||||||
|
# result numerator
|
||||||
|
resnum = "resnum = " + gen_calculate_string(STRUCTURE_PH, "ens") + ";"
|
||||||
|
|
||||||
|
# gcd of result numerator and result (common) denominator
|
||||||
|
resgcd = "resgcd = gcd(resnum, cd);"
|
||||||
|
|
||||||
|
# shortened result numerator
|
||||||
|
sresnum = "sresnum = resnum / resgcd;"
|
||||||
|
|
||||||
|
# shortened result denominator
|
||||||
|
sresden = "sresden = cd / resgcd;"
|
||||||
|
|
||||||
vars1_elem = ET.SubElement(answers_elem, 'vars1')
|
vars1_elem = ET.SubElement(answers_elem, 'vars1')
|
||||||
if shortable:
|
if shortable:
|
||||||
vars1_elem_text = "<![CDATA[ g = gcd(d[0],d[1]);\n cd = abs(d[0] * d[1]) / g;\n e = [cd / d[0], cd / d[1]];\n nums = [n[0]*e[0], n[1]*e[1]];\n thesumnum = nums[0] - nums[1];\n gcdres = gcd(thesumnum,cd);\n snum = thesumnum / gcdres;\n sdenom = cd / gcdres;]]>"
|
vars1_elem_text = f"<![CDATA[{cdtext}\n {extext}\n {entext}\n {resnum}\n {resgcd}\n {sresnum}\n {sresden}]]>"
|
||||||
else:
|
else:
|
||||||
vars1_elem_text = "<![CDATA[ g = gcd(d[0],d[1]);\ncd = abs(d[0] * d[1]) / g;\ne = [cd / d[0], cd / d[1]];\nnums = [n[0]*e[0], n[1]*e[1]];\nthesumnum = nums[0] - nums[1];]]>"
|
vars1_elem_text = f"<![CDATA[{cdtext}\n {extext}\n {entext}\n {resnum}]]>"
|
||||||
ET.SubElement(vars1_elem, 'text').text = vars1_elem_text
|
ET.SubElement(vars1_elem, 'text').text = vars1_elem_text
|
||||||
|
|
||||||
|
# Antwort
|
||||||
|
answer_elem = ET.SubElement(answers_elem, 'answer')
|
||||||
|
if shortable:
|
||||||
|
answer_text = '[resnum, cd, sresnum, sresden]'
|
||||||
|
else:
|
||||||
|
answer_text = '[resnum, cd]'
|
||||||
|
ET.SubElement(answer_elem, 'text').text = answer_text
|
||||||
|
|
||||||
|
answernotunique_elem = ET.SubElement(answers_elem, 'answernotunique')
|
||||||
|
ET.SubElement(answernotunique_elem, 'text').text = '0'
|
||||||
|
|
||||||
|
vars2_elem = ET.SubElement(answers_elem, 'vars2')
|
||||||
|
if shortable:
|
||||||
|
vars2_elem_text = "<![CDATA[ ga = _0 == resnum && _1 == cd;\n gb = (_0 / _1) == (resnum / cd);\n gsa = _2 == sresnum && _3 == sresden;\n gsb = (_2 / _3) == (sresnum / sresden); ]]>"
|
||||||
|
else:
|
||||||
|
vars2_elem_text = "<![CDATA[ ga = _0 == resnum && _1 == cd; gb = (_0 / _1) == (resnum / cd); ]]>"
|
||||||
|
ET.SubElement(vars2_elem, 'text').text = vars2_elem_text
|
||||||
|
|
||||||
|
correctness_elem = ET.SubElement(answers_elem, 'correctness')
|
||||||
|
if shortable:
|
||||||
|
ET.SubElement(correctness_elem, 'text').text = '0.25*ga + 0.25*gb + 0.25*gsa + 0.25*gsb'
|
||||||
|
else:
|
||||||
|
ET.SubElement(correctness_elem, 'text').text = '0.5*ga + 0.5*gb'
|
||||||
|
|
||||||
|
unitpenalty_elem = ET.SubElement(answers_elem, 'unitpenalty')
|
||||||
|
ET.SubElement(unitpenalty_elem, 'text').text = '1'
|
||||||
|
|
||||||
|
postunit_elem = ET.SubElement(answers_elem, 'postunit')
|
||||||
|
ET.SubElement(postunit_elem, 'text').text = ' '
|
||||||
|
|
||||||
|
ruleid_elem = ET.SubElement(answers_elem, 'ruleid')
|
||||||
|
ET.SubElement(ruleid_elem, 'text').text = '1'
|
||||||
|
|
||||||
|
otherrule_elem = ET.SubElement(answers_elem, 'otherrule')
|
||||||
|
ET.SubElement(otherrule_elem, 'text').text = ' '
|
||||||
|
|
||||||
|
# Unterfragen-Text
|
||||||
|
subqtext_elem = ET.SubElement(answers_elem, 'subqtext', attrib={'format': 'html'})
|
||||||
|
|
||||||
|
fractions_latex_strings = []
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
l_str = "\\\\frac{{"+ f"n[{i}]" +"}}{{"+ f"d[{i}]" +"}}"
|
||||||
|
fractions_latex_strings.append(l_str)
|
||||||
|
latex_calculation = gen_latex_calculate_string(STRUCTURE_PH, fractions_latex_strings)
|
||||||
|
|
||||||
|
|
||||||
|
sub_q_t_begin = "<![CDATA[ <p>Berechne:</p>"
|
||||||
|
sub_q_t_math = "<p>\\(\\Large \\displaystyle " + f"{latex_calculation}" + "=\\)</p>"
|
||||||
|
rows_list = [
|
||||||
|
["Zähler:", "{_0}"],
|
||||||
|
["Nenner:", "{_1}"]
|
||||||
|
]
|
||||||
|
|
||||||
|
sub_q_t_input = '<div style="display: inline-table"> ='
|
||||||
|
if shortable:
|
||||||
|
|
||||||
|
sub_q_t_input += create_html_table_for_fracture_input(rows_list)
|
||||||
|
|
||||||
|
rows_list_s = [["Gekürzter Zähler:", "{_2}"],
|
||||||
|
["Gekürzter Nenner:", "{_3}"]]
|
||||||
|
sub_q_t_input += " = "
|
||||||
|
sub_q_t_input += create_html_table_for_fracture_input(rows_list_s)
|
||||||
|
|
||||||
|
else:
|
||||||
|
sub_q_t_input += create_html_table_for_fracture_input(rows_list)
|
||||||
|
sub_q_t_input += '</div>'
|
||||||
|
sub_q_t_end = "]]>"
|
||||||
|
|
||||||
|
sub_question_text = sub_q_t_begin + sub_q_t_math + sub_q_t_input + sub_q_t_end
|
||||||
|
ET.SubElement(subqtext_elem, 'text').text = sub_question_text
|
||||||
|
|
||||||
|
expanded_fractions_latex_strings = []
|
||||||
|
for i in range(NUM_FRACTIONS):
|
||||||
|
l_str = "\\\\frac{{"+ f"ens[{i}]" +"}}{{"+ f"cd" +"}}"
|
||||||
|
expanded_fractions_latex_strings.append(l_str)
|
||||||
|
expanded_latex_calculation = gen_latex_calculate_string(STRUCTURE_PH, expanded_fractions_latex_strings)
|
||||||
|
|
||||||
|
# Lösungshinweis
|
||||||
|
feedback_elem = ET.SubElement(answers_elem, 'feedback', attrib={'format': 'html'})
|
||||||
|
if shortable:
|
||||||
|
feedback_text = '''<![CDATA[ <h4>Lösungshinweis</h4>
|
||||||
|
<p>Die korrekte Lösung ist:</p>
|
||||||
|
<p>\\(\\displaystyle''' + f"{latex_calculation}" +" = " + f"{expanded_latex_calculation}" + ''' = \\frac{{resnum}}{{cd}} = \\frac{{sresnum}}{{sresden}} \\) </p> ]]>'''
|
||||||
|
else:
|
||||||
|
feedback_text = '''<![CDATA[ <h4>Lösungshinweis</h4>
|
||||||
|
<p>Die korrekte Lösung ist:</p>
|
||||||
|
<p>\\( \\displaystyle ''' + f"{latex_calculation}" +" = " + f"{expanded_latex_calculation}" + ''' = \\frac{{resnum}}{{cd}} \\) </p> ]]>'''
|
||||||
|
ET.SubElement(feedback_elem, 'text').text = feedback_text
|
||||||
|
|
||||||
|
# correctfeedback
|
||||||
|
|
||||||
|
correctfeedback_elem = ET.SubElement(answers_elem, 'correctfeedback', attrib={'format': 'html'})
|
||||||
|
correctfeedback_text = '''<![CDATA[ <p>Richtig!</p>]]>'''
|
||||||
|
ET.SubElement(correctfeedback_elem, 'text').text = correctfeedback_text
|
||||||
|
|
||||||
|
# partially correct feedback
|
||||||
|
|
||||||
|
pcorrectfeedback_elem = ET.SubElement(answers_elem, 'partiallycorrectfeedback', attrib={'format': 'html'})
|
||||||
|
pcorrectfeedback_text = '''<![CDATA[ <p>Teilweise richtig.</p>]]>'''
|
||||||
|
ET.SubElement(pcorrectfeedback_elem, 'text').text = pcorrectfeedback_text
|
||||||
|
|
||||||
|
# incorrect feedback
|
||||||
|
incorrectfeedback_elem = ET.SubElement(answers_elem, 'incorrectfeedback', attrib={'format': 'html'})
|
||||||
|
incorrectfeedback_text = '''<![CDATA[ <p>Leider nicht richtig.</p>]]>'''
|
||||||
|
ET.SubElement(incorrectfeedback_elem, 'text').text = incorrectfeedback_text
|
||||||
|
|
||||||
|
return question
|
||||||
|
|
||||||
|
|
||||||
def generate_questions(num_questions):
|
def generate_questions(num_questions):
|
||||||
@ -328,6 +529,7 @@ def save_to_file(xml_element, filename):
|
|||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
tree = ET.ElementTree(xml_element)
|
tree = ET.ElementTree(xml_element)
|
||||||
|
ET.indent(tree, space="\t", level=0)
|
||||||
with open(filename, 'wb') as f:
|
with open(filename, 'wb') as f:
|
||||||
tree.write(f, encoding='utf_8', xml_declaration=True)
|
tree.write(f, encoding='utf_8', xml_declaration=True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user