![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
K vyjádření matic se často používají vnořené seznamy. Například, matice o rozměru 3x3 (3 řádky x 3 sloupce):
může být vyjádřena jako seznam s vnořenými seznamy stejné délky. Jednotlivé vnořené seznamy prezentují příslušné řádky matice:
>>> matrix = [[1, 2, 3], ... [4, 5, 6], ... [7, 8, 9]]Tentýž zápis v úspornějším provedení: >>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
V maticovém počtu lze tyto vnořené seznamy označit jako řádkové matice, neboli vektory.
Řádek matice vybereme jako prvek vnějšího seznamu (indexy počínají nulou):
>>> matrix[1] [4, 5, 6]
Položku určitého řádku označíme indexem řádku a indexem položky:
>>> matrix[1][1] 5
Práce s maticemi vyžaduje specifické znalosti maticového počtu. Na rozdíl od algebry v něm například obecně neplatí komutativní zákon: A*B != B*A; platí ale C = A+B == B+A, kde
Násobení matice A o velikosti m (řádků) *
Pokud je tato podmínka splněna, lze prvek
|1 2 | * |5 6 |1*5 + 2*7 | 1*6 + 2*8 |19 22 | | | | | = ----------|---------- = | | | 3 4 | |7 8 | 3*5 + 4*7 | 5*6 + 4*8 | 43 50 |
Rozměr výsledné matice ab je tedy dán počtem řádků matice a (
a = [[1, 2, 3], [4, 5, 6]]# 2 řádky, 3 sloupce: a(2,3) b = [[7, 8], [9, 2], [3,1]]# 3 řádky, 2 sloupce: b(3,2) ab = a * b = [[34, 15], [91], [48]]# 2 řádky, 2 sloupce: ab(2,2) # ab(0,0)= 1*7 + 2*9 + 3*3 = 34 # ab(1,1)= 4*8 + 5*2 + 6*1 = 48
Výpočet součinu matic lze provést trojím použitím idiomu
def matrix_mult (m1, m2): result = [[0 for row in range(len(m1))]# maketa matice for col in range(len(m2[0]))] for i in range(len(m1)):# rows in m1 for j in range(len(m2[0])):# cols in m2 for k in range(len(m2)):# rows in m2 result[i][j] += m1[i][k] * m2[k][j] return result
========= RESTART: F:\Codetest\HowTo\matice\Test.py ======== >>> a = [[1,2,3], [4,5,6]] >>> b = [[7,8], [9,1], [2,3]] >>> matrix_mult(a,b) [[31, 19], [85, 55]]
Sevřenější tvar má toto řešení s komprehencí seznamu a s vestavěnými funkcemi
def matrix_mult (m1, m2):# interní pojmenování: m1_r, m2_c # *m2 je sběrný parametr (sloupec druhé matice) return [[sum(x * y for x, y in zip(m1_r, m2_c)) for m2_c in zip(*m2)] for m1_r in m1]
Nejsnazší práci s maticemi poskytuje modul
Testem podnícený rozvoj (Test-driven development - TDD) je způsob vyvíjení programu, při kterém se postupuje po malých, automaticky ověřovaných krocích.
Tento postup si ukážeme na sestavení funkce, která vytvoří matici
Nejprve pro tuto funkci sestavime doctestest bez programového kódu a uložíme do souboru
ch09_matrices.py def make_matrix (rows, columns):""" >>> make_matrix(3,5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] """ if __name__ == '__main__':import doctest doctest.testmod()Poznámka: záhlaví a závěr skriptu nejsou v dalších úkázkách
z úsporných důvodů uváděny.
Provedení skriptu skončí neúspěchem:
******************************************************** File "matrices.py", line 3, in __main__.make_matrixFailed example: make_matrix(3, 5) Expected: [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] Got nothing ********************************************************1 items had failures: 1 of 1 in __main__.make_matrix ***Test Failed*** 1 failures.
Test nebyl úspěšný proto, že tělo funkce obsahuje pouze dokumentační řetězec a žádný příkaz
Při použití TDD zpravidla píšeme ten nejjednodušší kód, který projde testem, v našem případě to je příkaz return:
def make_matrix (rows, columns):""" >>> make_matrix(3,5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] """ return [[0, 0, 0, 0, 0], [0, 0,0, 0, 0], [0, 0, 0, 0, 0]]
Při provedení tohoto skriptu rovněž neobstojíme i když dostáváme semanticky stejný ale formálně různý výsledek, lišící se v tom, že citované seznamy jsou různě dlouhé, ten druhý je delší o mezery mezi jednotlivými položkami - doctesty se někdy chovají podivně.
Doplníme tedy mezery do seznamu v dokumentačním řetězci a spuštěním skriptu si ověříme, že tentokrát je všechno v pořádku (
Rozteče argumentů příkazu return upravovat nemusíme, příkaz si je upraví sám.
Uvědomíme si ale, že funkce
def make_matrix (rows, columns):""" >>> make_matrix(3,5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] >>> make_matrix(4,2) [[0, 0], [0, 0], [0, 0], [0, 0]] """ return [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Vida:
********************************************************* File "matrices.py", line 5, in __main__.make_matrix Failed example: make_matrix(4, 2) Expected: [[0, 0], [0, 0], [0, 0], [0, 0]] Got: [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] ********************************************************* 1 items had failures: 2 of 2 in __main__.make_matrix ***Test Failed*** 1 failures.
Tato technika se nazývá
def make_matrix (rows, columns):""" >>> make_matrix(3,5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] >>> make_matrix(4,2) [[0, 0], [0, 0], [0, 0], [0, 0]] """ return [[0]* columns]* rows
Řešení formálně neuspokojí proceduru doctestu. Jevově jsou oba řádky stejné ale interně se budou zřejmě lišit počtem připojených (neviditelných) mezer. Řekněmež, že nám to nevadí.
Alternativní řešení téže úlohy má tuto skladbu:
def make_matrix (rows, columns):""" >>> make_matrix(3,5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]] >>> make_matrix(4,2) [[0, 0], [0, 0], [0, 0], [0, 0]] """ matrix = []for rowin range(rows): matrix += [[0]* columns]return matrix
Použití TDD nám při našem programátorském snažení přináší několik výhod:
Funkci
def add_matrices (m1, m2): mx = make_matrix(len(m1), len(m1[0]))
Většina počítačových programů provádí při každém spuštění totéž, takže říkáme, že jsou deterministické.
Determinismus je obvykle dobrá věc, protože můžeme předpokládat, že stejný výpočet povede ke stejnému výsledku. U některých aplikacích si však přejeme, aby počítač byl nepředvídatelný. Hry jsou zřejmým příkladem, ale těch příkladů může být více.
Ukazuje se, že napsat program se skutečně nepředvídatelným chováním není snadné, ale jsou způsoby, kterak jej učinit alespoň zdánlivě nedeterministickým. Jedním ze způsobů je použití náhodných čísel. Python poskytuje vestavěnou funkci, která generuje pseudonáhodná čísla, která nejsou náhodná v přísně matematickém smyslu, ale pro naše účely postačí.
Modul
import randomdef rand (high):# high je celé číslo for iin range(high): x = random.random()# náhodné číslo v rozsahu [ 0.0, 1.0)
Náhodná čísla mezi nulou a zadanou mezí
Podobným problémem jako vytvoření náhodného čísla je provedení náhodného výběru. Ten lze provést pomocí funkce
>>> from random import choice >>> fokus = [("a"*3), "verpánek", {12, sum, True}] >>> choice(fokus), choice(fokus), choice(fokus) ('verpánek', 'aaa', {<built-in function sum>, 12, True}) >>> choice(fokus) 'aaa'# 'aaa' je rozbalené ("a"*3)
Jak vidno, prvky výběrové kolekce (list, tuple, string nebo set) mohou být různého typu. Samotný set však argumentem funkce choice přímo být nemůže, protože objekt
K vytvoření seznamu náhodných čísel, musíme od první chvíle vědět, v jakém rozsahu se generovaná náhodná čísla budou pohybovat [0 až 1) a kolik těch čísel má seznam obsahovat (zřejmě n). Jméno
Tělo funkce začíná vytvořením seznamu s
import randomdef randomList (n): s = [0] * n# počáteční seznam s nulami for iin range(n): s[i] = random.random()# náhrada nuly za náhodné číslo return s# seznam 'n' náhodných čísel
Voláním (invokací) této funkce pro
>>> randomList(6) [0.5199466739572948, 0.452751456638563, 0.41454566096564627, 0.7953941766158702, 0.3872848065377861, 0.9080198286105817]
Předpokládá se, že náhodná čísla, generovaná systémovou funkcí
Tento předpoklad si zkusíme ověřit napsáním programu, který rozdělí hodnoty do úseků a určí počet výskytů v jednotlivých úsecích, neboli jejich četnost.
Rozdělíme-li celý interval možných náhodných hodnot do stejně velikých úseků, a spočítáme-li výskyt náhodných hodnot v jednotlivých úsecích, měl by tento výskyt být všude přibližně stejný.
Označíme-li počet úseků (buckets) jménem
K výpočtu mezí jednotlivých úseků použijeme smyčku. Pro každý úsek určíme jeho spodní (low) a horní (high) hodnotu. Proměnná
bucketWidth = 1.0 / numBucketsfor iin range(numBuckets): low = i * bucketWidth high = low + bucketWidth
Při výpočtu dolní meze jednotlivého úseku (kyblíku) násobíme proměnnou smyčky podílem na jeden kyblík.
Horní mez je o
Pro
0.0 to 0.125 0.125 to 0.25 0.25 to 0.375 0.375 to 0.5 0.5 to 0.625 0.625 to 0.75 0.75 to 0.875 0.875 to 1.0
Chceme procházet vygenerovaným seznamem náhodných čísel [0.0 - 1.0) a určovat, kolik náhodných čísel zapadne do některého ze stanovených úseků (kyblíků, buckets). Řešení této úlohy si ukážeme ve dvou variantách. Pro první variantu si sestavíme funkci sami, pro druhou variantu je funkce již pro nás připravená.
Pro tuto variantu si sestavíme funkci
Vyjdeme z upravené sekvence příkazů ve cvičení 6.17.2, kde jména
V dalším kroku změníme předmět prověřování. Nezajímá nás výskyt písmen v řetězci ale chceme vědět, zda se hodnota proměnné
Za tím účelem doplníme smyčku
count = 0for numin list:# list vytvoříme funkcí randomList(n) if low < num < high: count = count + 1
Následně zapouzdříme upravený kód do funkce zvané
def inBucket (list, low, high): count = 0for numin list:if low < num < high: count = count + 1return count
Právě odvozenou funkci možná použijeme jako pomocnou funkci v dalším výpočtu. Musíme ale dořešit, kterak operativně dosazovat meze low a high pro jednotlivé úseky.
Pro záznam výskytů náhodných čísel v jednotlivých úsecích vytvoříme seznam
list =randomList (n)# seznam náhodných čísel numBuckets# alias b: počet úseků výskyty = [0] * numBuckets# počáteční seznam s nulami bucketWidth = 1.0 / numBuckets# číselný rozsah jednoho intervalu for iin range(numBuckets): low = i * bucketWidth# dolní mez intervalu high = low + bucketWidth# horní mez intervalu výskyty[i] = inBucket(list, low, high)
K hladkému provedení výše uvedeného skriptu musí mít interpret Pythonu k disposici importovaný modul
Nejlépe to zajistíme tak, že založíme soubor
Z výše uvedeného skriptu vytvoříme funkci
Když v interaktivní konzole IDLE provedeme volání
[138, 124, 128, 118, 130, 117, 114, 131]# SUMA = 1000
Tato čísla jsou docela blízká číslu
I když nám náš program chodí, mohl by chodit ještě lépe. Pokaždé, když volá funkci
Lepší by bylo projít seznamem jen jednou a pro každou hodnotu náhodného čísla určit rovnou index úseku (neboli index položky seznamu
Idea výhodnějšího řešení spočívá v tom, že náhodné číslo
Vytvořenou funkci
def buckets_b (n,b): výskyty = [0] * b# akumulátor list = randomList(n)# volání funkce for iin list: index = int(i * b)# zaokrouhlení dolů výskyty[index] = výskyty[index] + 1
Výstupem z funkce
[111, 132, 144, 127, 114, 123, 130, 119]# SUMA = 1000
def add_row (matrix): """ >>> m = [[0,0], [0,0]] >>> add_row(m) [[0, 0], [0, 0], [0, 0]] >>> n = [[3, 2, 5], [1, 4, 7]] >>> add_row(n) [[3,2,5], [1,4,7], [0,0,0]] >>> n [[3,2,5], [1,4,7]] """def add_column (matrix): """ >>> m = [[0,0], [0,0]] >>> add_column(m) [[0,0,0], [0,0,0]] >>> n = [[3,2], [5,1], [4,7]] >>> add_column(n) [[3,2,0], [5,1,0], [4,7,0]] >>> n [[3,2], [5,1], [4,7]] """
Všimněte si, že poslední doctesty v každé funkci ověřují, že
Poslední dva doctesty opět potvrzují, žedef add_matrices (m1, m2): """ >>> a = [[1,2], [3,4]] >>> b = [[2,2], [2,2]] >>> add_matrices(a, b) [[3,4], [5,6]] >>> c = [[8,2], [3,4], [5,7]] >>> d = [[3,2], [9,2], [10,12]] >>> add_matrices(c, d) [[11,4], [12,6], [15,19]] >>> c [[8,2], [3,4], [5,7]] >>> d [[3,2], [9,2], [10,12]] """
def scalar_mult (n, m): """ >>> a = [[1,2], [3,4]] >>> scalar_mult(3, a) [[3, 6], [9, 12]] >>> b = [[3,5,7], [1,1,1], [0,2,0], [2,2,3]] >>> scalar_mult(10, b) [[30, 50, 70], [10, 10, 10], [0, 20, 0], [20, 20, 30]] >>> b [[3, 5, 7], [1, 1, 1], [0, 2, 0], [2, 2, 3]] """
def matrix_mult (m1, m2): """ >>> matrix_mult([[1,2], [3,4]], [[5,6], [7,8]]) [[19, 22], [43, 50]] >>> matrix_mult([[1,2,3], [4,5,6]], [[7,8], [9,1], [2,3]]) [[31, 19], [85, 55]] >>> matrix_mult([[7,8], [9,1], [2,3]], [[1,2,3], [4,5,6]]) [[39, 54, 69], [13, 23, 33], [14, 19, 24]] """
expression = input("Zadej booleovský výraz \ pro 'p' a 'q' : " ) print(" p q %s" % expression) delka = len(" p q %s" % expression) print(delka* "=") for p in True, False: for q in True, False: print("%-7s %-7s %-7s" % (p, q, eval(expression)))Tento skript použijeme pro osvojení booleovských výrazů. Uložte program do souboru
p q p or q ------------------------ True True True True False True False True True False False False
Funkci zavoláme z konzoly IDLE:def truth_table (expression): print(" p q %s" % expression) delka = len(" p q %s" % expression) print(delka* "=") for p in True, False: for q in True, False: print("%-7s %-7s %-7s" % (p, q, eval(expression)))
>>> truth_table ("p or q") p q p or q ------------------------ True True True True False True False True True False False FalseVyzkoušejte si fci
Které výrazy jsou logicky eqvivalentní?
Napište fci
Uložte ji do souboru
>>> is_divisible(20,4) Toto číslo je dělitelné číslem 4 >>> is_divisible(21,8) Toto číslo neni dělitelné číslem 8
Při řešení použijete podmínky
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |