## Kartezični produkti

V čisto prvi "lekciji" sem se rekel, da morata biti tabeli, ki ju želimo seštevati, množiti ali karkoli že, enakih dimenzij.

Zlagal sem se.

K tabeli z n stolpci in poljubnim številom vrstic lahko prištejemo (primnožimo, odštejemo ...) enodimenzionalno tabelo z n elementi. Prištel jih bo k vsaki vrstici. Vendar nam to pri tej nalogi ne bo pomagalo. :)

Bolj zanimivo je tole:

    import numpy as np

    f = np.array([[1], [2], [3], [4]])
    g = np.array([1, 10, 100])

    f

    array([[1],
           [2],
           [3],
           [4]])

    g

    array([  1,  10, 100])

Pomnožimo ju: `f` bo prispeval vrstice in `g` stolpce. Če se dimenzije ne ujemajo, ne bo nič hudega.

    f * g

    array([[  1,  10, 100],
           [  2,  20, 200],
           [  3,  30, 300],
           [  4,  40, 400]])

Vsak element je produkt ustreznih elementov. Ker je `f` prispeval vrstico, `g` stolpec, je

    (f * g)[2, 1]

    30

enak

    f[2][0] * g[1]

    30

(K `f[2]` smo morali dodati `[0]`, saj gre za dvodimenzionalno tabelo, mi pa bi radi prišli do števila.)

To se da nadaljevati v tretjo dimenzijo.

    e = np.array([[[1]], [[5]], [[8]]])

    e * f * g

    array([[[   1,   10,  100],
            [   2,   20,  200],
            [   3,   30,  300],
            [   4,   40,  400]],

           [[   5,   50,  500],
            [  10,  100, 1000],
            [  15,  150, 1500],
            [  20,  200, 2000]],

           [[   8,   80,  800],
            [  16,  160, 1600],
            [  24,  240, 2400],
            [  32,  320, 3200]]])

Zdaj `e` prispeva prvi indeks, `f` drugega, `g` tretjega.

    (e * f * g)[1, 2, 0]

    15

    e[1][0][0] * f[2][0] * g[0]

    15

Vrstni red množenj ni pomemben. Pomembno je, koliko dimenzij ima kdo.

    g * e * f

    array([[[   1,   10,  100],
            [   2,   20,  200],
            [   3,   30,  300],
            [   4,   40,  400]],

           [[   5,   50,  500],
            [  10,  100, 1000],
            [  15,  150, 1500],
            [  20,  200, 2000]],

           [[   8,   80,  800],
            [  16,  160, 1600],
            [  24,  240, 2400],
            [  32,  320, 3200]]])

## Razpihovanje dimenzij

Kako si pripraviti matriko, kot je `e`? Se moramo res izgubljati v teh oglatih oklepajih? Tu nam pomaga še en trik pri indeksiranju: kot enega od indeksov lahko podamo `None`. Ta bo v tabelo, ki jo dobimo kot rezultat, dodal novo dimenzijo velikosti 1.

Tabela `e` je oblike

    e.shape

    (3, 1, 1)

Lahko bi začeli z

    e = np.array([1, 5, 0])

    e.shape

    (3,)

in mu dodali manjkajoči dimenziji z dvema `None`.

    e[:, None, None]

    array([[[1]],

           [[5]],

           [[0]]])

    e[:, None, None].shape

    (3, 1, 1)

Kar smo počeli zgoraj, je torej ekvivalentno temu:

    f = np.array([1, 2, 3, 4])
    g = np.array([1, 10, 100])
    e = np.array([1, 2, 3])

    e[:, None, None] * f[:, None] * g

    array([[[   1,   10,  100],
            [   2,   20,  200],
            [   3,   30,  300],
            [   4,   40,  400]],

           [[   2,   20,  200],
            [   4,   40,  400],
            [   6,   60,  600],
            [   8,   80,  800]],

           [[   3,   30,  300],
            [   6,   60,  600],
            [   9,   90,  900],
            [  12,  120, 1200]]])

## Sploščitev matrike

Obratna operacija je `flatten`. Ta splošči tabelo v eno dimenzijo.

    a = np.array([[3, 6, 1], [2, 5, 7], [1, 2, 8]])

    a

    array([[3, 6, 1],
           [2, 5, 7],
           [1, 2, 8]])

    a.flatten()

    array([3, 6, 1, 2, 5, 7, 1, 2, 8])

## Naloga (drugi del)

Tale naloga ni nadležna, zahteva pa nekaj razmišljanja, nekaj znanja Pythona in spretno uporabo `numpy`-ja. Ni preprosta, ni pa ena tistih, s katerimi se zafrkavaš v nedogled in si slabe volje celo potem, ko ti uspe. Vsaj meni se ni zdela takšne.

Rešite jo, pa četudi najprej v čistem Pythonu, brez `numpy`-ja.

Pazite, v drugem delu primer iz opisa naloge uporablja druge podatke.

Spoilerji.

Trik je v tem, da bo elementov več, kot jih gre v pomnilnik. Mej med njimi pa ne. Predstavljajmo si, da vsaka koordinata, na katero naletimo pri prižiganju in ugašanju, prereže reaktor. Šestkrat - dvakrat po `x`, dvakrat po `y`, dvakrat po `z`. Pri reševanju prvega dela je vsak element matrike ustrezal eni kocki reaktorja. Zdaj bo vsak element ustrezal celemu bloku. Bloke bo potrebno oštevilčiti, po vrsti. Slovar je vaš prijatelj, gre pa tudi z običajnimi indeksi.

Na koncu nas ne bo zanimalo število prižganih blokov temveč kock. Za to bo potrebno izračunati prostornine blokov. Tu in predvsem tu pa bo prišlo prav, kar smo se pravkar naučili.