from dual_numbers import DualNumber, epsilon
funcje_zaimplementowane = False
# Utwórz kilka liczb dualnych
a = DualNumber(2, 3) # 2 + 3ε
b = DualNumber(1, 4) # 1 + 4ε
c = DualNumber(5, 0) # 5 + 0ε (odpowiednik liczby rzeczywistej 5)
d = 1 + 2 * epsilon # 1 + 2ε
# Wypisz liczby dualne
print("Liczba dualna a:", a)
print("Liczba dualna b:", b)
print("Epsilon:", epsilon)
# Podstawowe operacje arytmetyczne
print("Dodawanie:", a + b)
print("Odejmowanie:", a - b)
print("Mnożenie:", a * b)
print("Dzielenie:", a / b)
print("Potęgowanie:", a**2)
print("Negacja", -a)
# Operacje prawostronne
print("Dodawanie prawe:", 1 + a)
print("Odejmowanie prawe:", 5 - a)
print("Mnożenie prawe:", 2 * a)
print("Dzielenie prawe:", 10 / a)
print("Potęgowanie prawe", 2**a)
# Operacje porównania
print("Równość:", a == b)
print("Nierówność:", a != b)
print("Równość:", a == DualNumber(2, 3))
# Konwersja na int i float
print("Konwersja na int:", int(a))
print("Konwersja na float:", float(a))
# Opcjonalne funkcje na liczbach dualnych
if funcje_zaimplementowane:
print("Pierwiastek kwadratowy z a:", DualNumber.sqrt(a))
print("Funkcja wykładnicza z a:", DualNumber.exp(a))
print("Sinus z a:", DualNumber.sin(a))
print("Cosinus z a:", DualNumber.cos(a))
Zadanie
Implementujemy liczby dualne w Pythonie. Nasza implementacja powinna być napisana w pliku dual_numbers.py
.
Celem tego zadania jest zaimplementowanie klasy DualNumber
w Pythonie. Liczby dualne są rozszerzeniem liczb rzeczywistych o specjalny element ‘ε’ (epsilon), taki że ε² = 0. Są one używane w automatycznym różniczkowaniu.
Liczba dualna: Liczba postaci a + bε, gdzie ‘a’ i ‘b’ są liczbami rzeczywistymi, a ε jest jednostką nieskończenie małą.
Instrukcje:
Definicja klasy:
- Utwórz klasę o nazwie
DualNumber
. - Konstruktor
__init__
powinien przyjmować dwa argumenty,real
idual
, reprezentujące odpowiednio ‘a’ i ‘b’ w a + bε. Oba powinny domyślnie wynosić 0. - Zapisz te wartości jako atrybuty
self.real
iself.dual
.
- Utwórz klasę o nazwie
Reprezentacja jako napis:
- Zaimplementuj metody
__str__
i__repr__
, aby zapewnić przyjazną dla użytkownika reprezentację napisową liczby dualnej. Powinna ona wyglądać odpowiednio jak “a + bε” lub “a - bε” dla__str__
i “DualNumber(a,b)” dla__repr__
.
- Zaimplementuj metody
Operacje arytmetyczne:
- Zaimplementuj następujące operacje arytmetyczne, aby umożliwić obliczenia na liczbach dualnych:
__add__
(+): (a + bε) + (c + dε) = (a + c) + (b + d)ε__sub__
(-): (a + bε) - (c + dε) = (a - c) + (b - d)ε__mul__
(): (a + bε) (c + dε) = ac + (ad + bc)ε__truediv__
(/): (a + bε) / (c + dε) = (a/c) + ((bc - ad)/c²)ε (Załóż, że c != 0)__pow__
(**): (a + bε)^n = a^n + na^(n-1)bε__neg__
(-): -(a + bε) = -a - bε
- Zaimplementuj prawostronne wersje tych operacji, aby obsługiwać przypadki takie jak 2 + (a + bε):
__radd__
__rsub__
__rmul__
__rtruediv__
__rpow__
- Zaimplementuj następujące operacje arytmetyczne, aby umożliwić obliczenia na liczbach dualnych:
Operacje porównania:
- Zaimplementuj następujące operacje porównania:
__eq__
(==): (a + bε) == (c + dε) jeśli a == c i b == d__ne__
(!=): (a + bε) != (c + dε) jeśli a != c lub b != d
- Zaimplementuj następujące operacje porównania:
Konwersja na int i float:
- Zaimplementuj metody
__int__
i__float__
, aby umożliwić konwersję liczby dualnej na typyint
ifloat
. Konwersja powinna zwracać tylko część rzeczywistą liczby dualnej.
- Zaimplementuj metody
Dodatkowe funkcje matematyczne (opcjonalne):
- Zaimplementuj inne funkcje matematyczne jako metody klasy:
sqrt(z)
: Pierwiastek kwadratowy liczby dualnej.exp(z)
: Funkcja wykładnicza liczby dualnej.sin(z)
: Sinus liczby dualnej.cos(z)
: Cosinus liczby dualnej.
- Zaimplementuj inne funkcje matematyczne jako metody klasy:
Poniżej znajduje się przykład użycia klasy DualNumber
, (kod kliencki). Twoja implementacja powinna umożliwić poprawne działanie tego kodu.
Rozwiązanie
from __future__ import annotations
import math
class DualNumber:
"""Reprezentuje liczbę dualną postaci a + bε."""
def __init__(self, real: float = 0.0, dual: float = 0.0) -> None:
"""Inicjalizuje liczbę dualną.
Args:
real: Część rzeczywista (a). Domyślnie 0.0.
dual: Część dualna (b). Domyślnie 0.0.
"""
self.real = float(real)
self.dual = float(dual)
def __str__(self) -> str:
"""Zwraca reprezentację łańcuchową liczby dualnej."""
sign = "+" if self.dual >= 0 else "-"
return f"{self.real} {sign} {abs(self.dual)}ε"
def __repr__(self) -> str:
"""Zwraca oficjalną reprezentację łańcuchową liczby dualnej."""
return f"DualNumber({self.real}, {self.dual})"
def __add__(self, other: DualNumber | float) -> DualNumber:
"""Dodaje dwie liczby dualne lub liczbę dualną i liczbę rzeczywistą."""
if isinstance(other, DualNumber):
return DualNumber(self.real + other.real, self.dual + other.dual)
if isinstance(other, int | float):
return DualNumber(self.real + other, self.dual)
return NotImplemented
def __radd__(self, other: float) -> DualNumber:
"""Obsługuje dodawanie z liczbą rzeczywistą po lewej stronie.
other + self
"""
return self + other
def __sub__(self, other: DualNumber | float) -> DualNumber:
"""Odejmuje dwie liczby dualne lub liczbę dualną i liczbę rzeczywistą."""
if isinstance(other, DualNumber):
return DualNumber(self.real - other.real, self.dual - other.dual)
if isinstance(other, int | float):
return DualNumber(self.real - other, self.dual)
return NotImplemented
def __rsub__(self, other: float) -> DualNumber:
"""Obsługuje odejmowanie z liczbą rzeczywistą po lewej stronie."""
return DualNumber(other - self.real, -self.dual)
def __mul__(self, other: DualNumber | float) -> DualNumber:
"""Mnoży dwie liczby dualne lub liczbę dualną i liczbę rzeczywistą."""
if isinstance(other, DualNumber):
real_part = self.real * other.real
dual_part = (self.real * other.dual) + (self.dual * other.real)
return DualNumber(real_part, dual_part)
if isinstance(other, int | float):
return DualNumber(self.real * other, self.dual * other)
return NotImplemented
def __rmul__(self, other: float) -> DualNumber:
"""Obsługuje mnożenie z liczbą rzeczywistą po lewej stronie."""
return self * other
def __truediv__(self, other: DualNumber | float) -> DualNumber:
"""Dzieli dwie liczby dualne lub liczbę dualną i liczbę rzeczywistą."""
if isinstance(other, DualNumber):
if other.real == 0:
msg = "Dzielenie przez zero"
raise ZeroDivisionError(msg)
real_part = self.real / other.real
dual_part = (self.dual * other.real - self.real * other.dual) / (
other.real**2
)
return DualNumber(real_part, dual_part)
if isinstance(other, int | float):
if other == 0:
msg = "Dzielenie przez zero"
raise ZeroDivisionError(msg)
return DualNumber(self.real / other, self.dual / other)
return NotImplemented
def __rtruediv__(self, other: float) -> DualNumber:
"""Obsługuje dzielenie z liczbą rzeczywistą po lewej stronie."""
if self.real == 0:
msg = "Dzielenie przez zero"
raise ZeroDivisionError(msg)
real_part = other / self.real
dual_part = (0 - other * self.dual) / (self.real**2)
return DualNumber(real_part, dual_part)
def __pow__(self, n: float) -> DualNumber:
"""Podnosi liczbę dualną do potęgi n (n jest liczbą całkowitą lub zmiennoprzecinkową)."""
if isinstance(n, int | float):
real_part = self.real**n
dual_part = n * (self.real ** (n - 1)) * self.dual
return DualNumber(real_part, dual_part)
return NotImplemented
def __rpow__(self, other: float) -> DualNumber:
"""Obsługuje przypadek, gdy liczba rzeczywista jest podnoszona do potęgi liczby dualnej."""
if isinstance(other, int | float):
real_part = other**self.real
dual_part = real_part * math.log(other) * self.dual
return DualNumber(real_part, dual_part)
return NotImplemented
def __neg__(self) -> DualNumber:
"""Zwraca negację liczby dualnej."""
return DualNumber(-self.real, -self.dual)
def __eq__(self, other: DualNumber) -> bool:
"""Sprawdza, czy dwie liczby dualne są równe."""
if isinstance(other, DualNumber):
return self.real == other.real and self.dual == other.dual
return False
def __ne__(self, other: DualNumber) -> bool:
"""Sprawdza, czy dwie liczby dualne nie są równe."""
return not self == other
def __int__(self) -> int:
"""Konwertuje liczbę dualną na liczbę całkowitą (zwraca część rzeczywistą)."""
return int(self.real)
def __float__(self) -> float:
"""Konwertuje liczbę dualną na liczbę zmiennoprzecinkową (zwraca część rzeczywistą)."""
return float(self.real)
@staticmethod
def sqrt(z: DualNumber) -> DualNumber:
"""Oblicza pierwiastek kwadratowy z liczby dualnej."""
if z.real < 0:
msg = "Pierwiastek kwadratowy z ujemnej części rzeczywistej nie jest zdefiniowany dla liczb dualnych w tej implementacji"
raise ValueError(
msg,
)
real_part = math.sqrt(z.real)
dual_part = z.dual / (2 * real_part) if real_part != 0 else 0
return DualNumber(real_part, dual_part)
@staticmethod
def exp(z: DualNumber) -> DualNumber:
"""Oblicza funkcję wykładniczą liczby dualnej."""
real_part = math.exp(z.real)
dual_part = real_part * z.dual
return DualNumber(real_part, dual_part)
@staticmethod
def sin(z: DualNumber) -> DualNumber:
"""Oblicza sinus liczby dualnej."""
real_part = math.sin(z.real)
dual_part = math.cos(z.real) * z.dual
return DualNumber(real_part, dual_part)
@staticmethod
def cos(z: DualNumber) -> DualNumber:
"""Oblicza cosinus liczby dualnej."""
real_part = math.cos(z.real)
dual_part = -math.sin(z.real) * z.dual
return DualNumber(real_part, dual_part)
epsilon = DualNumber(0, 1) # Nieskończenie mała jednostka epsilon