Proporcje w Pythonie – Krótkie Skrypty
Ta część to praktyczne skrypty w Pythonie do obliczeń na proporcjach. Wszystko jest napisane po polsku, z jasnymi komentarzami i przykładami danych wejściowych. Dostajesz gotowe funkcje do proporcji bezpośredniej i odwrotnej, bezpieczne parsowanie liczb z przecinkiem lub kropką, obsługę błędów oraz krótkie testy.
Założenia i zapis
Proporcja bezpośrednia to równość dwóch stosunków: \( A:B = C:D \). Z tego wynika \( A \cdot D = B \cdot C \) oraz wzory na wartość brakującą: \( D = \frac{B \cdot C}{A} \), \( C = \frac{A \cdot D}{B} \), \( B = \frac{A \cdot D}{C} \), \( A = \frac{B \cdot C}{D} \).
Proporcja odwrotna opisuje sytuację, w której iloczyn pozostaje stały: \( x \cdot y = k \). Wtedy \( y = \frac{k}{x} \) oraz \( x = \frac{k}{y} \). Przed liczeniem zawsze upewnij się, że to właściwy typ zależności.
Funkcje do proporcji bezpośredniej i odwrotnej
Poniższy moduł zawiera bezpieczne parsowanie liczb, obliczenia proporcji oraz czytelną obsługę błędów. Do obliczeń używamy typu dziesiętnego, aby uniknąć przypadkowych zaokrągleń binarnych.
# proporcje.py
from decimal import Decimal, getcontext
from typing import Optional, Tuple, Dict
# Ustawienia precyzji dziesiętnej
getcontext().prec = 28
def liczba_z_tekstu(txt: Optional[str]) -> Optional[Decimal]:
"""
Zwraca Decimal z napisu.
Dopuszcza przecinek i kropkę w zapisie dziesiętnym.
Puste pole lub None daje None.
"""
if txt is None:
return None
s = str(txt).strip()
if s == "":
return None
s = s.replace(",", ".")
return Decimal(s)
def formatuj(d: Decimal, miejsca: int = 6, przecinek: bool = True) -> str:
"""
Zwraca napis z sensownym zaokrągleniem.
"""
q = Decimal(10) ** -miejsca
val = d.quantize(q)
out = format(val, f"f")
# obetnij zbędne zera
if "." in out:
out = out.rstrip("0").rstrip(".")
if przecinek:
out = out.replace(".", ",")
return out
def proporcja_bezposrednia(A: Optional[Decimal], B: Optional[Decimal],
C: Optional[Decimal], D: Optional[Decimal]) -> Tuple[str, Decimal]:
"""
Rozwiązuje A:B = C:D. Dokładnie jedno pole ma być None.
Zwraca nazwę brakującego pola i jego wartość jako Decimal.
"""
dane = {"A": A, "B": B, "C": C, "D": D}
brak = [k for k,v in dane.items() if v is None]
if len(brak) != 1:
raise ValueError("Wymagane jest dokładnie jedno puste pole.")
# skrót
A, B, C, D = dane["A"], dane["B"], dane["C"], dane["D"]
nazwa = brak[0]
if nazwa == "D":
if A == 0:
raise ZeroDivisionError("A równe 0 uniemożliwia obliczenie D.")
wart = (B * C) / A
elif nazwa == "C":
if B == 0:
raise ZeroDivisionError("B równe 0 uniemożliwia obliczenie C.")
wart = (A * D) / B
elif nazwa == "B":
if C == 0:
raise ZeroDivisionError("C równe 0 uniemożliwia obliczenie B.")
wart = (A * D) / C
else: # nazwa == "A"
if D == 0:
raise ZeroDivisionError("D równe 0 uniemożliwia obliczenie A.")
wart = (B * C) / D
return nazwa, wart
def proporcja_odwrotna(k: Decimal, x: Optional[Decimal], y: Optional[Decimal]) -> Tuple[str, Decimal]:
"""
Zależność odwrotna: x * y = k. Dokładnie jedno z x, y ma być None.
"""
if (x is None) == (y is None):
raise ValueError("Podaj dokładnie jedną niewiadomą w zależności odwrotnej.")
if x is None:
if y == 0:
raise ZeroDivisionError("y równe 0 uniemożliwia obliczenie x.")
return "x", k / y
else:
if x == 0:
raise ZeroDivisionError("x równe 0 uniemożliwia obliczenie y.")
return "y", k / x
def zgodnosc_stosunkow(A: Decimal, B: Decimal, C: Decimal, D: Decimal, eps: Decimal = Decimal("1e-9")) -> bool:
"""
Sprawdza czy A/B i C/D są równe w granicach tolerancji.
"""
if B == 0 or D == 0:
return False
lewy = A / B
prawy = C / D
return abs(lewy - prawy) <= eps
# Przykładowe użycie z poziomu programu
if __name__ == "__main__":
# przykład 1
A = liczba_z_tekstu("600")
B = liczba_z_tekstu("4")
C = liczba_z_tekstu("7")
D = None
nazwa, wart = proporcja_bezposrednia(A,B,C,D)
print(nazwa, "=", formatuj(wart)) # oczekiwane: D = 1050
# przykład 2
k = Decimal("24")
x = liczba_z_tekstu("3")
y = None
nazwa2, wart2 = proporcja_odwrotna(k, x, y)
print(nazwa2, "=", formatuj(wart2)) # oczekiwane: y = 8
Skrypt wiersza poleceń
Ten skrypt przyjmuje cztery wartości A B C D, z których jedno pole może pozostać puste. Wartości mogą zawierać przecinek lub kropkę. Na końcu program drukuje wynik oraz krótką kontrolę ilorazów.
# cli_proporcje.py
import sys
from decimal import Decimal
from proporcje import liczba_z_tekstu, proporcja_bezposrednia, zgodnosc_stosunkow, formatuj
def main(argv):
"""
Użycie:
python cli_proporcje.py A B C D
Przykład:
python cli_proporcje.py "" 600 7 4 # puste A
python cli_proporcje.py 600 4 7 "" # puste D
"""
if len(argv) != 4:
print("Podaj dokładnie 4 pola A B C D. Jedno z nich może być puste.")
sys.exit(1)
A = liczba_z_tekstu(argv[0])
B = liczba_z_tekstu(argv[1])
C = liczba_z_tekstu(argv[2])
D = liczba_z_tekstu(argv[3])
nazwa, wart = proporcja_bezposrednia(A,B,C,D)
# uzupełnij brakujące pole w lokalnym słowniku dla kontroli
pola = {"A":A, "B":B, "C":C, "D":D}
pola[nazwa] = wart
ok = zgodnosc_stosunkow(pola["A"], pola["B"], pola["C"], pola["D"])
print(f"{nazwa} = {formatuj(wart)}")
print("kontrola:", "zgodne" if ok else "niezgodne")
if __name__ == "__main__":
main(sys.argv[1:])
Krótki moduł z zadaniami i testami
W tym pliku znajdują się zadania zapisane jako asercje. To prosta forma sprawdzenia, czy funkcje działają zgodnie z oczekiwaniem. Jeżeli asercja nie przejdzie, program zgłosi błąd.
# testy_proporcji.py
from decimal import Decimal
from proporcje import liczba_z_tekstu, proporcja_bezposrednia, proporcja_odwrotna, zgodnosc_stosunkow
def test_bezposrednia():
A = Decimal("600"); B = Decimal("4"); C = Decimal("7"); D = None
nazwa, wart = proporcja_bezposrednia(A,B,C,D)
assert nazwa == "D"
assert str(wart) == "1050"
A = None; B = Decimal("750"); C = Decimal("16"); D = Decimal("1000")
nazwa, wart = proporcja_bezposrednia(A,B,C,D) # A = B*C/D = 12
assert nazwa == "A"
assert str(wart.normalize()) == "12"
def test_odwrotna():
nazwa, wart = proporcja_odwrotna(Decimal("24"), Decimal("3"), None) # y = 8
assert nazwa == "y" and str(wart.normalize()) == "8"
def test_zgodnosc():
A = Decimal("600"); B = Decimal("4"); C = Decimal("1050"); D = Decimal("7")
assert zgodnosc_stosunkow(A,B,C,D)
if __name__ == "__main__":
test_bezposrednia()
test_odwrotna()
test_zgodnosc()
print("ok")
Tablica przykładowych uruchomień
Poniższa tabela pokazuje dane wejściowe i oczekiwany wynik. Możesz użyć ich do szybkiej weryfikacji skryptu w wierszu poleceń lub do własnych notatek.
| Opis | Dane | Wzór | Oczekiwany wynik |
|---|---|---|---|
| Skalowanie porcji | A = 600, B = 4, C = 7, D puste | \( D = \frac{B \cdot C}{A} = \frac{4 \cdot 7}{600} \cdot 600 \) | D = 1050 g |
| Cena jednostkowa | A puste, B = 750, C = 12, D = 1000 | \( A = \frac{B \cdot C}{D} = \frac{750 \cdot 12}{1000} \) | A = 12 zł |
| Skala rysunku | A = 4,6, B = 1, C puste, D = 50 | \( C = \frac{A \cdot D}{B} = 4.6 \cdot 50 \) | C = 230 cm |
| Mieszanka 1 do 5 | x puste, y = 600, k = 3000 | \( x = \frac{k}{y} = \frac{3000}{600} \) | x = 5 |
Walidacja danych wejściowych
Najczęstsze błędy to dzielenie przez zero, dwie niewiadome naraz oraz mieszanie jednostek. Poniższa wersja funkcji parsuje napisy i zgłasza komunikaty w języku polskim, aby łatwiej było znaleźć przyczynę problemu.
# walidacja.py
from decimal import Decimal
from proporcje import liczba_z_tekstu, proporcja_bezposrednia
def oblicz_z_napisow(a_txt, b_txt, c_txt, d_txt):
A = liczba_z_tekstu(a_txt)
B = liczba_z_tekstu(b_txt)
C = liczba_z_tekstu(c_txt)
D = liczba_z_tekstu(d_txt)
try:
nazwa, wart = proporcja_bezposrednia(A,B,C,D)
except ZeroDivisionError as e:
return {"blad": str(e)}
except ValueError as e:
return {"blad": str(e)}
return {"pole": nazwa, "wartosc": str(wart)}
Prosty interfejs tekstowy
Ten skrypt pyta kolejno o cztery wartości. Zostaw dokładnie jedno pole puste, aby obliczyć brakującą liczbę. Na końcu dostajesz wynik i krótką informację o zgodności ilorazów.
# interfejs_tekstowy.py
from proporcje import liczba_z_tekstu, proporcja_bezposrednia, zgodnosc_stosunkow, formatuj
def uruchom():
print("Podaj A (puste jeśli nieznane): ")
A = liczba_z_tekstu(input().strip())
print("Podaj B (puste jeśli nieznane): ")
B = liczba_z_tekstu(input().strip())
print("Podaj C (puste jeśli nieznane): ")
C = liczba_z_tekstu(input().strip())
print("Podaj D (puste jeśli nieznane): ")
D = liczba_z_tekstu(input().strip())
nazwa, wart = proporcja_bezposrednia(A,B,C,D)
pola = {"A":A, "B":B, "C":C, "D":D}
pola[nazwa] = wart
ok = zgodnosc_stosunkow(pola["A"], pola["B"], pola["C"], pola["D"])
print(f"Wynik: {nazwa} = {formatuj(wart)}")
print("Zgodność:", "tak" if ok else "nie")
if __name__ == "__main__":
uruchom()
Wskazówki praktyczne
Jednostki
Ujednolicaj jednostki przed obliczeniem. Gram i kilogram to różne skale. Najpierw konwersja, potem wzór. To ogranicza ryzyko błędu.
Precyzja
Do obliczeń używaj typu dziesiętnego. Dzięki temu wyniki nie będą zniekształcone przez reprezentację binarną. Zaokrąglaj na końcu.
Kontrola
Po obliczeniu wstaw wynik z powrotem do równości i porównaj ilorazy. Jeżeli są równe w rozsądnej tolerancji, wszystko jest w porządku.
Rodzaj zależności
Zanim policzysz, zdecyduj czy to proporcja bezpośrednia, czy odwrotna. Jeżeli rośnie jedna wielkość i rośnie druga w tym samym stosunku, to proporcja bezpośrednia. Jeżeli rośnie jedna, a druga maleje przy stałym iloczynie, to zależność odwrotna.