Macierze: operacje elementarne¶
Tablice i pętle¶
Tablice w Pythonie noszą nazwę list. Używamy ich następująco:
Stworzenie lity (tablicy):
z określonymi elementami:
tab = [1, 2, 3, 4, 5, 6, 10]o określonej liczbie elementów:
tab = [0] * 100
Przypisanie albo odczytanie \(k\)-tego elementu listy (indeksy od 0):
tab[k] = 42lubtab[k]Sprawdzenie długości lity:
len(tab)
Python ma pętlę while, która powtarza podany zestaw instrukcji tak długo, jak długo podany warunek jest spełniony. Do warunków możemy równości (\(a = b\) to a == b, \(a \ne b\) to a != b), nierówności (\(a < b\) to a < b, \(a \le b\) to a <= b, podobnie nierówności w drugą stronę) i spójników logicznych and oraz or.

Ważne
Napisanie:
while warunek:
instrukcja1
instrukcja2
...
Powoduje powtarzanie podanych instrukcji (1, 2, …) tak długo, jak długo warunek jest prawdziwy.
Warunek musi być wyrażeniem logicznym — takim o wartości True albo False.
Na przykład:
pierwiastek = 0
while pierwiastek * pierwiastek < 1997:
print(f"{pierwiastek} to nadal za mało")
pierwiastek += 1
print("{pierwiastek} jest pierwszą liczbą, której kwadrat przekracza 1997")
Podstawowa tabelka liczb¶
Zaczniemy od napisania klasy, która pozwoli nam przechowywać dwuwymiarową tabelkę liczb:
m = Matrix(nr, nc)utworzy wypełnioną zerami macierz onrwierszach inckolumnach.m.nrows()zwróci liczbę wierszy.m.ncols()zwróci liczbę kolumn.set(r, c, v)ustawi wr-tym wierszuk-tej kolumny wartośćv.get(r, c)zwróci wartość w macierzy wr-tym wierszuk-tej kolumny.
Kod startowy, który możesz wkleić do nowego pliku (nazwij go matrix.py):
class Matrix:
def __init__(self, rows, cols):
... # TODO: zadanie 1
def nrows(self):
... # TODO: zadanie 3
def ncols(self):
... # TODO: zadanie 3
def get(self, row, col):
... # TODO: zadanie 3
def set(self, row, col, val):
... # TODO: zadanie 3
Zadanie 1: tworzenie pustej macierzy¶
Do pamiętania macierzy w naszym programie użyjemy listy list. Macierz
Będziemy pamiętać jako listę list:
lista = [[1, 2, 3], [4, 5, 6]]
Czyli lista zawiera kolejne wiersze macierzy:
lista[0]to pierwszy wiersz[1,2,3], jego pierwszy element (czyli \(m_{1\,1}\)) tolista[0][0].lista[1]to drugi wiersz[1, 2, 3], jego trzeci element (czyli \(m_{2\,3}\)) tolista[1][2].
W kodzie startowym uzupełnij funkcję Matrix.__init__, w której wewnątrz klasy w self._data
zapamiętasz listę list samych zer o odpowiednich rozmiarach.
Uwaga
Funkcje wewnątrz klasy wołamy (prawie) zawsze pisząc zmienna.funkcja().
Dlatego funkcje wewnątrz klasy MojaKlasa będziemy pisać jako MojaKlasa.moja_funkcja().
Funkcje spoza klasy będziemy pisać bez kropki - moja_funkcja().
Możesz potem przetestować swoje rozwiązanie, dopisując na końcu pliku stworzenie zmiennej zawierającej macierz \(\mathbb{R}^{4 \times 6}\):
m = Matrix(4, 6)
Zadanie 2: rozmiar macierzy¶
Przypomnienie: len(l) to liczba elementów bezpośrednio w liście l. Zagadka: ile wynosi
len([1,2,[3,4,5]]), dlaczego?
Teraz: jak możemy powiedzieć tylko na podstawia listy wierszy w self._data, jakie są wymiary
macierzy?
Uzupełnij kod funkcji Matrix.nrows() i Matrix.ncols().
Zadanie 3: dostęp do elementów macierzy¶
Uzupełnij teraz kod funkcji Matrix.get i Matrix.set.
Opcjonalnie: dodaj do każdej z nich sprawdzenie, czy r i c nie są za duże, lub za małe:
if not (0 <= row < self.nrows() and 0 <= col < self.ncols()):
raise IndexError(f"Niepoprawny index ({row}, {col}) w macierzy o wymiarach {self.nrows()}x{self.ncols()}")
Przetestuj, czy działa: zapisz do pliku macierz jednostkową o wymiarach 10 na 10:
Operacje elementarne na macierzach¶
Zaimplementujemy teraz kolejne funkcje do klasy macierzy, które wykorzystamy do rozwiązywania układów równań:
Matrix.swap_rows(row1, row2): zamienia miejscami wiersze o podanych indeksach.Matrix.multiply(row, scalar): mnoży wiersz o podanym indeksie przez podaną wartość.Matrix.add_row(dst_row, scalar, src_row): dodaje do wiersza o indeksiedst_rowwiersz o indeksiesrc_rowpomnożony przezscalar.
Zadanie 4: zamiana wierszy¶
Uzupełnij kod funkcji Matrix.swap_rows.
Pamiętaj, że w Pythonie indeksy zaczynają się od zera.
Przykładowo, jeżeli macierz m to:
to po wykonaniu m.swap_rows(0, 1) macierz m powinna być równa:
Zadanie 5: pomnożenie wiersza¶
Uzupełnij kod funkcji Matrix.multiply_row, która pomnoży wiersz o danym indeksie (pierwszy wiersz
ma index zero) przez podaną wartość.
Przykładowo, jeżeli macierz m to:
to po wykonaniu m.multiply_row(0, 2) macierz m powinna być równa:
Zadanie 6: dodawanie pomnożonego wiersza¶
Uzupełnij kod funkcji Matrix.add_row.
Pamiętaj, że po jej wykonaniu jedynym zmienionym wierszem powinien być ten o indeksie podanym
w pierwszym argumencie do funkcji; wszystkie pozostałe wiersze mają zostać, jak były.
Przykładowo, jeśli macierz m jest równa:
To po wykonaniu m.add_row(1, -6.0, 0) powinna wynosić:
Wczytywanie i zapisywanie macierzy¶
Możesz teraz dokleić to zaklęcie do swojego pliku matrix.py, żeby odblokować swoim macierzom
skilla zapisywania do pliku i wczytywania z niego:
def read_matrix_file(filename):
number_of_columns = None
number_of_rows = 0
elements = []
with open(filename) as opened_file:
for lineno, line in enumerate(opened_file):
line_elements = line.split()
if not line_elements:
continue
number_of_rows += 1
if number_of_columns is None:
number_of_columns = len(line_elements)
else:
if len(line_elements) != number_of_columns:
raise ValueError(f"{filename}:{lineno}: zła liczba elementów"
f" - {len(line_elements)} zamiast {number_of_columns}")
for element in line_elements:
try:
elements.append(float(element))
except ValueError:
raise ValueError(f"{filename}:{lineno}: niepoprawna liczba '{element}'")
result = Matrix(number_of_rows, number_of_columns)
elements.reverse()
for r in range(number_of_rows):
for c in range(number_of_columns):
result.set(r, c, elements.pop())
return result
def write_matrix_file(matrix, filename=None):
sizes = [0] * matrix.ncols()
for r in range(matrix.nrows()):
for c in range(matrix.ncols()):
sizes[c] = max(sizes[c], len(str(matrix.get(r, c))))
if filename:
opened_file = open(filename, "w")
else:
from sys import stdout
opened_file = stdout
for r in range(matrix.nrows()):
for c in range(matrix.ncols()):
if c != 0:
opened_file.write(" ")
opened_file.write(str(matrix.get(r, c)).rjust(sizes[c]))
opened_file.write("\n")
if filename:
opened_file.close()
Instrukcje warunkowe¶
Do rozwiązania tego zadania będziesz potrzebować instrukcji warunkowych. W Pythonie instrukcja warunkowa składa się z:
Głównej części
if warunek:wraz z instrukcjami wykonywanymi, gdy warunek jest spełniony (czyli gdy wartość logiczna wyrażeniawarunektoTrue);Opcjonalnie: części
elif warunek:wraz z instrukcjami wykonywanymi: gdy:żadna z poprzednich części nie była wykonana oraz…
…
warunekjest prawdziwy.
Opcjonalnie: części
else:wraz z instrukcjami wykonywanymi, gdy żadna z wcześniejszych części nie została wykonana.
Przykładowo:
if ile == 0:
print("Janek nie ma jabłek")
elif ile == 1:
print(f"Janek ma {ile} jabłko")
elif 2 <= ile <= 4:
print(f"Janek ma {ile} jabłka")
else:
print(f"Janek ma {ile} jabłek")
W jaki sposób możemy uzyskać ten sam efekt, co dodając część else, ale używając elif?
Wskazówka
Napisać:
else:
...
to to samo, co:
elif True:
...
Dlaczego potrzebujemy elif, skoro moglibyśmy (jak w C++) użyć else wraz z kolejnym if?
Wskazówka
Problemem jest to, że Python wymusza zwiększenie wcięcia dla każdej kolejnej grupy instrukcji, więc pisząc jak w C++ musielibyśmy pisać:
if ile == 0:
print("Janek nie ma jabłek")
else:
if ile == 1:
print(f"Janek ma {ile} jabłko")
else:
if 2 <= ile <= 4:
print(f"Janek ma {ile} jabłka")
else:
print(f"Janek ma {ile} jabłek")
Ważne
Instrukcja warunkowa w Pythonie umożliwia wykonywanie różnego kodu, zależnie od prawdziwości warunków, które odwołują się do wartości zmiennych:
if jablka > pomarancze:
print(f"Mam więcej jabłek niż pomarańczy")
elif jablka < pomarancze:
print(f"Mam więcej pomarańczy niż jabłek")
else:
print(f"Mam tyle samo jabłek, co pomarańczy")

Zadanie 7: wypisywanie macierzy jako układu równań¶
Napisz funkcję Matrix.eqs_print(), która wypisze macierz w postaci układu równań.
Na przykład dla macierzy:
Powinna wypisać:
x1 + 2*x2 + 3*x3 = 4
x1 - 2*x2 + 3*x3 = -2
4*x1 + 6*x3 = 4
Zwróć uwagę, że:
Kiedy współczynik przy jakiejś zmiennej wynosi 0, to w ogóle nic tam nie wypisujemy (\(x_2\) w trzecim równaniu).
Kiedy współczynnik przy jakiejś zmiennej wynosi 1, to pomijamy jego wartość (\(x_1\) w pierwszych dwóch równaniach).
Kiedy współczynnik jest ujemny, to piszemy \(-\) zamiast \(+\) (\(x_2\) w drugim równaniu).