kleine Änderungen
This commit is contained in:
parent
0f9c815b86
commit
dc088fa214
47 changed files with 183868 additions and 573 deletions
168
bilder_04/erzeuge_propagation_ablauf.py
Normal file
168
bilder_04/erzeuge_propagation_ablauf.py
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# erzeuge_propagation_ablauf.py
|
||||
"""
|
||||
Erzeugt die Ablaufgrafik zur Propagation im Kapitel CP-SAT:
|
||||
|
||||
bilder_04/kap_cpsat_propagation_ablauf.svg
|
||||
|
||||
ERGAENZT das vorhandene kap06_cp_sat_propagation.svg, ersetzt es nicht: Jenes
|
||||
zeigt schematisch das Zusammenspiel von Propagation und Konfliktlernen. Dieses
|
||||
zeigt die Propagation selbst - Runde fuer Runde, an Zahlen.
|
||||
|
||||
Gezeigt wird GENAU die Handrechnung des Kapitels:
|
||||
|
||||
x1 in {1,2,3,4}, x2 in {2,3,4,5}, Bedingung x1 + x2 = 7
|
||||
|
||||
Ausgangslage 4 x 4 = 16 Kombinationen
|
||||
nach Runde 1 x1 -> {2,3,4} 12 Kombinationen
|
||||
nach Runde 2 x2 -> {3,4,5} 9 Kombinationen
|
||||
|
||||
Bewusst nicht das Beispiel aus der Auftragsbeschreibung ([0,10] mit x1+x2<=6):
|
||||
Die Handrechnung steht unmittelbar neben dem Bild. Ein Bild mit anderen Zahlen
|
||||
als der Text daneben zwingt den Leser, beide zu vergleichen, statt eines zu
|
||||
verstehen.
|
||||
|
||||
Die Domains werden PROPAGIERT, nicht eingetragen: revidiere() streicht jeden
|
||||
Wert, fuer den es im Partner keinen Partner gibt - das ist Kantenkonsistenz,
|
||||
und zwar in derselben Reihenfolge wie in der Handrechnung. Der Selbsttest
|
||||
vergleicht das Ergebnis mit den drei im Kapitel abgedruckten Zahlen.
|
||||
|
||||
Aufruf (aus dem Repository-Wurzelverzeichnis):
|
||||
python3 bilder_04/erzeuge_propagation_ablauf.py
|
||||
|
||||
Benoetigt: matplotlib
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from stil_04 import FARBEN, speichere # noqa: E402
|
||||
import matplotlib.pyplot as plt # noqa: E402
|
||||
from matplotlib.patches import Rectangle # noqa: E402
|
||||
|
||||
# Die Aufgabe der Handrechnung.
|
||||
START_X1 = {1, 2, 3, 4}
|
||||
START_X2 = {2, 3, 4, 5}
|
||||
SUMME = 7
|
||||
ANZEIGE = range(1, 7) # 1 bis 6 - alle je vorkommenden Werte
|
||||
|
||||
# Was das Kapitel abdruckt; nur zum Gegenpruefen.
|
||||
KOMBINATIONEN_IM_BUCH = [16, 12, 9]
|
||||
ENDDOMAINS_IM_BUCH = ({2, 3, 4}, {3, 4, 5})
|
||||
|
||||
|
||||
def revidiere(eigene: set[int], partner: set[int]) -> set[int]:
|
||||
"""Streicht jeden Wert, zu dem es im Partner keinen Partner gibt."""
|
||||
return {v for v in eigene if (SUMME - v) in partner}
|
||||
|
||||
|
||||
def propagiere():
|
||||
"""Die zwei Runden der Handrechnung, jede als eigener Zustand."""
|
||||
d1, d2 = set(START_X1), set(START_X2)
|
||||
verlauf = [("Ausgangslage", set(d1), set(d2), None)]
|
||||
|
||||
d1 = revidiere(d1, d2)
|
||||
verlauf.append((f"Runde 1 — von $x_1$ aus", set(d1), set(d2), 0))
|
||||
|
||||
d2 = revidiere(d2, d1)
|
||||
verlauf.append((f"Runde 2 — von $x_2$ aus", set(d1), set(d2), 1))
|
||||
return verlauf
|
||||
|
||||
|
||||
def zeichne(verlauf) -> None:
|
||||
figur, achse = plt.subplots(figsize=(8.6, 4.6))
|
||||
achse.set_axis_off()
|
||||
|
||||
zellbreite, zellhoehe = 1.0, 0.62
|
||||
zeilenabstand = 1.05
|
||||
blockabstand = 2.6
|
||||
|
||||
for stufe, (titel, d1, d2, geaendert) in enumerate(verlauf):
|
||||
oben = -stufe * blockabstand
|
||||
achse.text(-0.4, oben + 0.75, titel, fontsize=10,
|
||||
color=FARBEN["text"], fontweight="bold", ha="left",
|
||||
va="center")
|
||||
|
||||
for reihe, (name, domain, vorher) in enumerate((
|
||||
("$x_1$", d1, verlauf[stufe - 1][1] if stufe else d1),
|
||||
("$x_2$", d2, verlauf[stufe - 1][2] if stufe else d2))):
|
||||
y = oben - reihe * zeilenabstand
|
||||
achse.text(-0.4, y + zellhoehe / 2, name, fontsize=11,
|
||||
color=FARBEN["text"], ha="left", va="center")
|
||||
for spalte, wert in enumerate(ANZEIGE):
|
||||
x = spalte * zellbreite
|
||||
gerade_gestrichen = wert in vorher and wert not in domain
|
||||
if wert in domain:
|
||||
flaeche, rand, schrift = (FARBEN["gut"], FARBEN["gut"],
|
||||
"white")
|
||||
alpha = 0.85
|
||||
elif gerade_gestrichen:
|
||||
flaeche, rand, schrift = (FARBEN["fehler"],
|
||||
FARBEN["fehler"], "white")
|
||||
alpha = 0.75
|
||||
else:
|
||||
flaeche, rand, schrift = (FARBEN["flaeche"],
|
||||
FARBEN["linie"],
|
||||
FARBEN["linie"])
|
||||
alpha = 1.0
|
||||
achse.add_patch(Rectangle((x, y), zellbreite * 0.9, zellhoehe,
|
||||
facecolor=flaeche, edgecolor=rand,
|
||||
alpha=alpha, linewidth=1.0))
|
||||
achse.text(x + zellbreite * 0.45, y + zellhoehe / 2, str(wert),
|
||||
fontsize=10, color=schrift, ha="center",
|
||||
va="center", fontweight="bold")
|
||||
if gerade_gestrichen:
|
||||
achse.plot([x + 0.1, x + zellbreite * 0.8],
|
||||
[y + zellhoehe - 0.08, y + 0.08], "-",
|
||||
color="white", linewidth=1.6)
|
||||
|
||||
anzahl = len(d1) * len(d2)
|
||||
achse.text(len(ANZEIGE) * zellbreite + 0.35, oben - 0.22,
|
||||
f"{len(d1)} × {len(d2)} = {anzahl}\nKombinationen",
|
||||
fontsize=9.5, color=FARBEN["text"], va="center")
|
||||
if geaendert is not None:
|
||||
gestrichen = sorted(verlauf[stufe - 1][1 + geaendert]
|
||||
- (d1, d2)[geaendert])
|
||||
achse.text(len(ANZEIGE) * zellbreite + 0.35, oben - 1.15,
|
||||
"gestrichen: "
|
||||
+ ", ".join(str(w) for w in gestrichen),
|
||||
fontsize=9, color=FARBEN["fehler"], va="center")
|
||||
|
||||
achse.text(-0.4, -2 * blockabstand - 1.65,
|
||||
f"Bedingung $x_1 + x_2 = {SUMME}$ — kein einziger Wert wurde "
|
||||
f"ausprobiert. Gestrichen wird,\nwofür es im Partner keinen "
|
||||
f"Partner gibt.",
|
||||
fontsize=9.5, color=FARBEN["text"], va="top")
|
||||
|
||||
achse.set_xlim(-0.6, len(ANZEIGE) * zellbreite + 2.6)
|
||||
achse.set_ylim(-2 * blockabstand - 2.5, 1.2)
|
||||
achse.set_aspect("equal")
|
||||
|
||||
speichere(figur, "kap_cpsat_propagation_ablauf")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
verlauf = propagiere()
|
||||
for titel, d1, d2, _ in verlauf:
|
||||
reintitel = titel.replace("$", "").replace("\\", "")
|
||||
print(f" {reintitel:<24} x1 = {sorted(d1)} x2 = {sorted(d2)} "
|
||||
f"-> {len(d1) * len(d2)} Kombinationen")
|
||||
|
||||
anzahlen = [len(d1) * len(d2) for _, d1, d2, _ in verlauf]
|
||||
if anzahlen != KOMBINATIONEN_IM_BUCH:
|
||||
raise SystemExit(
|
||||
f"Propagations-Ablauf: Die Propagation ergibt {anzahlen} "
|
||||
f"Kombinationen, das Kapitel druckt {KOMBINATIONEN_IM_BUCH} ab.")
|
||||
if (verlauf[-1][1], verlauf[-1][2]) != ENDDOMAINS_IM_BUCH:
|
||||
raise SystemExit(
|
||||
f"Propagations-Ablauf: Enddomains {sorted(verlauf[-1][1])} / "
|
||||
f"{sorted(verlauf[-1][2])} weichen von der Handrechnung "
|
||||
f"{sorted(ENDDOMAINS_IM_BUCH[0])} / "
|
||||
f"{sorted(ENDDOMAINS_IM_BUCH[1])} ab.")
|
||||
print(" stimmt mit der Handrechnung überein: 16 → 12 → 9")
|
||||
|
||||
zeichne(verlauf)
|
||||
Loading…
Reference in a new issue