Hierarchia klas: Wielomian i inne pojęcia alebraiczne

Klasa Polynomial

Stwórz klasę Polynomial, która będzie reprezentować funkcję \(W(x) = \sum_{i=0}^N c_i x^i\) gdzie \(N\) jest stopniem wielomianu a \(x\) dowolną liczbą rzeczywistą. Współczynniki \(c_i\) powinny być prywatnymi danymi klasy

Konstruktor klasy

Klasa powinna mieć konstruktor, umożliwiający łatwe tworzenie wielomianów. Zadbaj o wygodę użytkowników swojej klasy! Twój konstruktor powinien zatem być przeciążony (patrz przeciążenie). Utwórz warianty umożliwiające następujące sposoby konstruowania wielomianów:

w1 = Polynomial(1, 0, 5)      # Tworzy x^2 + 5
w2 = Polynomial([1, 0, 5])    # Tworzy x^2 + 5
w3 = Polynomial(w2)       # Konstruktor kopiujący
Dane prywatne

Zastanów się nad najwygodniejszą reprezentacją danych prywatnych klasy

Wypisywanie na ekran

Napisz metodę __str__(), która odpowiednio wydrukuje wielomian na ekranie

print(w1)      # Drukuje na ekran: x^2 + 1
Metody publiczne

Klasa Polynomial powinna mieć następujące metody: add(w), sub(w) i mul(w), które będą odpowiednio dodawać, odejmować oraz wymnażać wielomian w, modyfikując bieżący obiekt (self). Napisz także metody differentiate() i integrate(), które policzą pierwszą pochodną oraz całkę z wielomianu. Oczywiście klasa Polynomial powinna też mieć metodę eval() obliczającą wartość wielomianu w zadanym punkcie.

Na tym etapie Twoja klasa powinna umożliwić poprawne działanie następującego programu:

w1 = Polynomial(2, 0, 1)
print(w1)                    # Drukuje na ekran: x^2 + 1
w2 = Polynomial([3, 2, 1])
w3 = Polynomial(w2)
w2.add(w1)
print(w2.mul(w1))

Lepsza klasa Polynomial

Przeciążenie operatorów

Wykorzystaj Funkcje specjalne do zdefiniowania operatorów += -= *= , które zastąpią odpowiednio metody klasy: add(w), sub(w) i mul(w). W tym celu wystarczy odpowiednio zmienić nazwy metod, np add() na __iadd__(). Pamiętaj, aby metody te zwracały self. Zdefiniuj też operatory + - * a także operator wywołania (ang. call operator) jako metodę __call__(), który zastąpi metodę eval()

Zabezpieczanie danych prywatnych

Utwóż listę __slots__ w klasie Polynomial (Lista __slots__), aby nikt nie dodał nowych składowych do Twojej klasy

Dostęp do składowych

Utwóż getter coefficients udostępniający współczynniki wielomianu; dla zachowania integralności klasy metoda ta powinna zwracać głęboką kopię współczynników, ustawionych w kolejności wg. rosnących potęg.

Na tym etapie Twoja klasa powinna umożliwić poprawne działanie następującego programu:

w1 = Polynomial(2, 1)
print(w1)                     # Drukuje na ekran: x^2 + 1
w2 = Polynomial([3, 2, 1])
w3 = Polynomial(w2)
w2 += w1
w4 = w3 + w2
w5 = w3 - w2
w = w4 * w5

print(w2.coefficients)

Test jednostkowy dla klasy Polynomial

Jedną z głównych zalet obiektowego podejścia do programowania jest zamknięcie fragmentu kodu w obiekcie jako w autonomicznej całości (patrz enkapsulacja). Każdy obiekt należy jednak dokładnie przetestować. Przedstawiony poniżej plik TestPolynomial.py sprawdza działanie publicznych metod, które powinieneś zaimplementować w klasie Polynomial:

A unit test for Polynomial class
 1import unittest
 2
 3from Polynomial import Polynomial
 4
 5
 6class TestPolynomial(unittest.TestCase):
 7
 8    def setUp(self):
 9        self.w1 = Polynomial([1, 2, 3, 5])      # 1*x^3 + 2*x^2 + 3*x + 5
10        self.w2 = Polynomial(5, 2, 1)           #         5*x^2 + 2*x + 1
11        self.w3 = Polynomial(self.w1)
12
13    def test_coefficients(self):
14        self.assertEqual([5, 3, 2, 1], self.w1.coefficients)
15        self.assertEqual([1, 2, 5], self.w2.coefficients)
16        self.assertEqual([5, 3, 2, 1], self.w3.coefficients)
17        c = self.w3.coefficients
18        c.append(7)
19        self.assertEqual([5, 3, 2, 1], self.w3.coefficients)
20
21    def test_init(self):
22        args = [1, 2, 3, 5]
23        w = Polynomial(args)
24        args.append(7)
25        self.assertEqual([5, 3, 2, 1], w.coefficients)
26
27    def test_str(self):
28        self.assertEqual("1*x^3 + 2*x^2 + 3*x + 5", str(self.w1))
29        self.assertEqual("5*x^2 + 2*x + 1", str(self.w2))
30
31    def test_add(self):
32        w3 = self.w2 + self.w1
33        self.assertEqual([6, 5, 7, 1], w3.coefficients)
34        w3 = self.w1 + self.w2
35        self.assertEqual([6, 5, 7, 1], w3.coefficients)
36
37    def test_iadd(self):
38        w = Polynomial(self.w1)
39        c2 = self.w2.coefficients
40        w += self.w2
41        self.assertEqual([6, 5, 7, 1], w.coefficients)
42        w3 = self.w1 + self.w2
43        self.assertEqual(self.w2.coefficients, c2)
44
45    def test_sub(self):
46        w4 = Polynomial(self.w1)
47        w3 = self.w2 - self.w1
48        self.assertEqual(w4.coefficients, self.w1.coefficients)     # operation should not affect operands
49        self.assertEqual([-4, -1, 3, -1], w3.coefficients)
50        w4 = self.w1 - self.w2
51        self.assertEqual([4, 1, -3, 1], w4.coefficients)
52
53    def test_mul(self):
54        w4 = Polynomial(self.w1)
55        w3 = self.w2 * self.w1
56        self.assertEqual([5, 13, 33, 20, 12, 5], w3.coefficients)
57        self.assertEqual(w4.coefficients, self.w1.coefficients)     # operation should not affect operands
58
59    def test_int_math(self):
60        w3 = self.w2 * 2
61        self.assertEqual([2, 4, 10], w3.coefficients)
62
63if __name__ == '__main__':
64        unittest.main()

W szczególności test ten sprawdza integralność klasy. Na przykład metoda test_init(self) tworzy wielomian dla podanej listy współczynników, a następnie modyfikuje tą listę i sprawdza, czy wielomian nie uległ zmianie.

Wielomany Czebyszewa

Wieloman Czebyszewa k-tego rzędu \(T_k(x)\) zdefiniowany jest rekurencyjnie jak nastepuje:

\(\begin{matrix} T_0(x) & = & 1\\ T_1(x) & = & x\\ T_k(x) & = & 2x T_{k-1}(x) - T_{k-2}(x)\\ \end{matrix}\)

Wydrukuj na ekranie wielomian \(T_{128}(x)\). W tym celu:

funkcja chebyshev(k)

Napisz funkcję chebyshev(k), która zwraca listę współczynników wielomanu Czebyszewa k-tego rzędu.

Klasa Chebyshev

Zawrzyj funkcję chebyshev(k) w klasie Chebyshev jako jej statyczną składową. Klasa powinna dziedziczyć po klasie Polynomial. Ponieważ konstruktor klasy Polynomial wymaga podania współczynników tworzonego wielomianu, wykorzystaj tu napisaną uprzednio statyczną metodę.

Poniżej podaję test jednostkowy dla klasy Chebyshev:

(rozwiń kod testu jednostkowego dla klasy Chebyshev)
 1import unittest
 2
 3from Chebyshev import Chebyshev
 4
 5
 6class TestChebyshev(unittest.TestCase):
 7
 8    def test_Chebyshev(self):
 9        c = Chebyshev(6)
10        self.assertEqual([-1, 0, 18, 0, -48, 0, 32], c.coefficients)
11
12if __name__ == '__main__':
13        unittest.main()

Klasa Fractional

Stwórz klasę Fractional, która będzie reprezentować iloraz wyrażeń.

Konstruktor klasy

Klasa powinna mieć następujący konstruktor:

f = Fractional(1,5)      # Tworzy ulamek 1/5
Dane prywatne

Iloraz powinien przechowywać licznik oraz mianownik jako prywatne pola.

Publiczne pola

Zdefiniuj getter dla licznika i mianownika, nazwij je num i den

Wypisywanie na ekran

Napisz metodę __str__(), która odpowiednio wydrukuje ułamek na ekranie. Wykorzystaj w tym celu funkcję str() dla licznika i mianownika

Operatory

Klasa Fractional powinna udostępniać następujące operatory: +, - oraz *. Przy sprowadzaniu wyrażeń do wspólnego mianownika również korzystaj z operacji + oraz *, operujących odpowiednio na liczniku i mianowniku.

Na tym etapie Twoja klasa powinna umożliwić poprawne działanie następującego programu:

f1 = Fractional(2, 5)
f2 = Fractional(1,3)
print(f3)                     # Drukuje na ekran: 2/15
f3 = f1 * f2
f3 += f1
f4 = f3 - f2

Funkcje wymierne

Funkcja wymierna to nic więcej jak ułamek (Fractional) w którym zarówno licznik i mianownik to wielomiany (obiekty klasy Polynomial). Poniższy kod powinien działać poprawnie:

w1 = Polynomial(1, 2, 5)
w2 = Polynomial(1,3)
f1 = Fractional(w1, w2)
f2 = Fractional(w2, w1)
print(f1 + f2)