05: Vícerozměrná pole a maticové typy
Tento notebook je výukovým materiálem v předmětu BI-JUL.21 vyučovaném v zimním semestru akademického roku 2026/2027 Tomášem Kalvodou. Tvorba těchto materiálů byla podpořena NVS FIT.
Hlavní stránkou předmětu, kde jsou i další notebooky a zajímavé informace, je jeho Course Pages stránka.
versioninfo()Julia Version 1.13.0 Commit d1c37793dd2 (2026-09-09 19:00 UTC) Build Info: Official https://julialang.org release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz WORD_SIZE: 64 LLVM: libLLVM-20.1.8 (ORCJIT, skylake) GC: Built with stock GC Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)
1. Vícerozměrná pole (Array)
Vícerozměrné pole si lze představovat jako "kolekci" objektů uložených ve vícerozměrné mřížce (matice/tabulka, kvádr,...).
I pole mají svůj parametrický typ, který určuje jaké objekty dokáží uchovávat a jaký mají rozměr.
Nejobecnějším případem je přirozeně Array{Any}, resp. Array{Any, n}, kde označuje dimenzionalitu (rozměry) pole.
Pole těchto typů pojmou libovolné objekty, tj. objekty typu Any.
Obecně objekty typu Array{T} představují vícerozměrná pole s prvky typu T.
Array{Any} <: Arraytrue
Array{Any, 2} <: Array{Any}true
Jak už bylo zmíněno v třetí lekci, k prvkům, nebo částem, pole přistupujeme pomocí hranatých závorek. Nejrpve se ale pusťme do konstrukce prvních polí, aby pak bylo co indexovat.
1.1 Vytváření a indexování polí
Neinicializované pole s rozměry dims a prvky typu T vytvoříme pomocí příkazu Array{T}(undef, dims...).
dims může být tuple, nebo popořadě velikosti jednotlivých "dimenzí".
Ukažme si to na příkladech:
# 1d array of Int64s with 5 elements
Array{Int64}(undef, 5)5-element Vector{Int64}:
140487656736016
140487656736496
140487656736976
140487656737456
0Ihned poznamenejme, že Vector{T} jen šikovná zkratka pro jednodimenzionální pole s prvky typu T.
Vector{Any} == Array{Any, 1}true
Nyní se přesuňme k dvojrozměrným polím.
# 2d 10x5 array of Float64s
Array{Float64}(undef, 10, 5)10×5 Matrix{Float64}:
6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310
6.94101e-310 6.94101e-310 6.94101e-310 5.0e-324 6.94101e-310
6.94101e-310 6.94101e-310 0.0 6.94101e-310 6.94101e-310
6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310
6.94102e-310 6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310
6.94101e-310 5.0e-324 6.94101e-310 6.94101e-310 6.94101e-310
6.94101e-310 NaN 6.94101e-310 6.94101e-310 1.0e-323
6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310 1.5e-323
6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310 1.0e-323
6.94101e-310 6.94101e-310 6.94101e-310 6.94101e-310 1.0e-323Podobně jako výše, Matrix{T} je pouze konvenční krycí název pro dvourozměrné pole.
Matrix{Any} == Array{Any, 2}true
Od tří dimenzí dále už výpis pole samozřejmě není úplně přehledný, je ale možný.
# 3d 4x4x4 array of strings
Array{String}(undef, (4, 4, 4))4×4×4 Array{String, 3}:
[:, :, 1] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 2] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 3] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 4] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undefArray{Any}(undef, (4, 4, 4))4×4×4 Array{Any, 3}:
[:, :, 1] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 2] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 3] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
[:, :, 4] =
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undef
#undef #undef #undef #undefTato pole nejsou inicializována, jejich hodnoty jsou "náhodné", podle toho co zbylo na místě původní paměti.
Vzhledem k explicitnímu zmínění slovíčka undef v předchozích příkazech by se mohlo zdát, že můžeme prvky polí tímto způsobem předvyplnit požadovanou hodnotou, ale není tomu tak:
Array{Int64}(42, 5)MethodError: no method matching (Array{Int64})(::Int64, ::Int64)
The type `Array{Int64}` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
(Array{T})(::UndefInitializer, ::Int64, ::Int64, ::Int64) where T
@ Core boot.jl:668
(Array{T})(::UndefInitializer, ::Int64, ::Int64) where T
@ Core boot.jl:667
(Array{T})(::UndefInitializer, ::Int64) where T
@ Core boot.jl:666
...
Stacktrace:
[1] top-level scope
@ In[12]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489Viz metodu fill níže.
Vytváření polí s požadovanými prvky (Maticové literály)
Jednorozměrná (vektory) a dvourozměrná (matice) pole můžeme zadat explicitně pomocí hranatých závorek, před kterými můžeme případně uvést požadovaný typ prvků.
K oddělení prvků máme bohaté možnosti: používáme čárky (,), mezery, konce řádků, nebo středníky (;).
Tímto způsobem můžeme kontrolovat rozměry pole.
[1, 2, 3][1, 2, 3.]transpose([1,2,3])[1.0; 2.0; 3.0]Float64[1, 2, 3]Ale pozor, následující pole je už dvourozměrné:
[1 2 3]Dvourozměrná pole zadáváme po řádcích oddělených středníky, nebo koncemi řádků:
[1 0; 0 1]Float64[
1 2 3
4 5 6
]Pozor, čárky neprojdou.
[1 0, 0 1]Tento způsob se samozřejmě hodí jen pro malá pole. Častěji potřebujeme pracovat například s maticemi s velkým počtem prvků a takovéto explicitní vypisování by bylo komplikované. Typicky třeba známe předpis pro -tý prvek. Pak můžeme použít analog Pythonovského list comprehension.
Například Float64 5x5 verzi Hilbertovy matice, které má na -tém políčku hodnotu můžeme definovat takto:
H = [ 1 / (i + j - 1) for i = 1:5, j = 1:5 ]Zde první index je řádkový a druhý sloupcový, jak bude patrné z následujícího příkladu
[ (i, j) for i = 1:4, j = 1:5 ][ (i, j) for i = 1:4, j = 1:4, k = 1:2][ (i, j) for i = 1:4, j = 1:4, k = 1:2, ℓ = 1:2]Před hranatou závorkou můžeme opět vynutit typ prvků:
[ j for j = 1:10 ]10-element Vector{Int64}:
1
2
3
4
5
6
7
8
9
10Dejte pozor na ne/přítomnost hranatých závorek, například následující buňka nevyústí v matici:
vv = [ [ j+k for j=1:3 ] for k=1:3 ]hcat(vv...)vcat(vv...)Lze přidávat i podmínky jako v Pythonu:
[ j for j = 1:10 if iseven(j) ]Indexování polí
Prvky pole jsou standardně indexovány od .
K prvkům pole přistupujeme pomocí hranatých závorek.
K indexování můžeme použít i rozsah (range) a získat tak část pole (slice, řez).
Stejným způsobem můžeme prvky, nebo celé bloky matice, modifikovat pomocí přiřazení.
Ukažme si to na následující Töplitzově matici (konstantní hodnoty na diagonále i všech vedlejších diagonálách):
T = [ 5 - i + j for i = 1:4, j = 1:4 ]4×4 Matrix{Int64}:
5 6 7 8
4 5 6 7
3 4 5 6
2 3 4 5Hodnoty v rozích matice:
println("Levý horní: ", T[1, 1])
println("Pravý horní: ", T[1, 4])
println("Levý dolní: ", T[4, 1])
println("Pravý dolní: ", T[4, 4])Levý horní: 5 Pravý horní: 8 Levý dolní: 2 Pravý dolní: 5
T[1, 5]BoundsError: attempt to access 4×4 Matrix{Int64} at index [1, 5]
Stacktrace:
[1] throw_boundserror(A::Matrix{Int64}, I::Tuple{Int64, Int64})
@ Base ./essentials.jl:14
[2] checkbounds
@ ./abstractarray.jl:702 [inlined]
[3] getindex(::Matrix{Int64}, ::Int64, ::Int64)
@ Base ./array.jl:962
[4] top-level scope
@ In[20]:1
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489T[-1, 1]BoundsError: attempt to access 4×4 Matrix{Int64} at index [-1, 1]
Stacktrace:
[1] throw_boundserror(A::Matrix{Int64}, I::Tuple{Int64, Int64})
@ Base ./essentials.jl:14
[2] checkbounds
@ ./abstractarray.jl:702 [inlined]
[3] getindex(::Matrix{Int64}, ::Int64, ::Int64)
@ Base ./array.jl:962
[4] top-level scope
@ In[21]:1
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489Změníme hodnotu v pravo nahoře:
T[1, 4] = -2
T4×4 Matrix{Int64}:
5 6 7 -2
4 5 6 7
3 4 5 6
2 3 4 52x3 podmatice v levém horním rohu:
1:2typeof(1:2)T4×4 Matrix{Int64}:
5 6 7 -2
4 5 6 7
3 4 5 6
2 3 4 5T[1:2, 1:3]2×3 Matrix{Int64}:
5 6 7
4 5 6Změna 2x2 bloku vpravo dole na jednotkovou matici 2x2:
T[3:4, 3:4] = [1 0; 0 1]
T4×4 Matrix{Int64}:
5 6 7 -2
4 5 6 7
3 4 1 0
2 3 0 1Pokud chceme celý řádek/sloupec, pak pro tyto účely můžeme použít pouze :. Rozsah "až do konce" můžeme vyjádřit pomocí end:
T[:, 2:end]4×3 Matrix{Int64}:
6 7 -2
5 6 7
4 1 0
3 0 1T[:, 2:-1]4×0 Matrix{Int64}T[:,1:2:end]4×2 Matrix{Int64}:
5 7
4 6
3 1
2 0TT[:, 2:-1:1]4×2 Matrix{Int64}:
6 5
5 4
4 3
3 2T[:, 2:-1:-1]BoundsError: attempt to access 4×4 Matrix{Int64} at index [1:4, 2:-1:-1]
Stacktrace:
[1] throw_boundserror(A::Matrix{Int64}, I::Tuple{Base.Slice{Base.OneTo{Int64}}, StepRange{Int64, Int64}})
@ Base ./essentials.jl:14
[2] checkbounds
@ ./abstractarray.jl:702 [inlined]
[3] _getindex
@ ./multidimensional.jl:983 [inlined]
[4] getindex(::Matrix{Int64}, ::Function, ::StepRange{Int64, Int64})
@ Base ./abstractarray.jl:1363
[5] top-level scope
@ In[30]:1
[6] eval(m::Module, e::Any)
@ Core ./boot.jl:489T4×4 Matrix{Int64}:
5 6 7 -2
4 5 6 7
3 4 1 0
2 3 0 1T[:, [3, 1, 2]]4×3 Matrix{Int64}:
7 5 6
6 4 5
1 3 4
0 2 3Nyní probereme metody, které se hodí při vytváření nových "obecných" polí. V případě matic máme více možností, kterým se budeme věnovat v příslušné lekci.
Konkatenace polí (vcat a hcat)
V metamatickém zápisu často používáme blokový zápis matic. Jeho analogem je konkatenace.
Mějme tři pole:
A = [1, 1, 1]B = [2 2]C = [3 3; 3 3; 3 3]Pokud má operace rozměrově smysl, můžeme pole spojovat horizontálně:
[A C A]hcat(A, C, A)Nebo vertikálně:
[B; C; B]vcat(B, C, B)A oba přístupy lze dohromady i kombinovat právě v něčem, co připomíná blokový zápis:
[ B B; C C][
B B
C C
]push! a pop!
Do jednorozměrného pole lze přidávat prvky pomocí push! a ubírat je pomocí pop! (odzadu):
a = [1, 2, 3]
push!(a, 4)
a4-element Vector{Int64}:
1
2
3
4x = pop!(a)
println(x)
aDále máme i metodu append!, která ale spojuje více jednorzměrných polí dohromady.
append!(a, [5, 6])
aappend!(a, 42)append!(a, [42])@which append!(a, 42)Existují i další související metody jako pushfirst!, popfirst!, popat!, insert! a další.
fill a fill!
K uniformní inicializaci prvků pole slouží dvě metody:
fill(x, dims...): vytvoří nové pole s prvkyxa rozměrydims.fill!(A, x): zaplní již existující poleAprvkyx
# 1d array of 5 elemens equal to 42
fill(42, 5)5-element Vector{Int64}:
42
42
42
42
42# 2d 3x4 array of 0.5s
fill(0.5, 3, 4)3×4 Matrix{Float64}:
0.5 0.5 0.5 0.5
0.5 0.5 0.5 0.5
0.5 0.5 0.5 0.5fill(1.23, (2, 2))2×2 Matrix{Float64}:
1.23 1.23
1.23 1.23fill("Ahoj!", 2, 2)2×2 Matrix{String}:
"Ahoj!" "Ahoj!"
"Ahoj!" "Ahoj!"B = fill(undef, 2, 2)2×2 Matrix{UndefInitializer}:
UndefInitializer() UndefInitializer()
UndefInitializer() UndefInitializer()fill!(B, "Hello!")MethodError: Cannot `convert` an object of type String to an object of type UndefInitializer
The function `convert` exists, but no method is defined for this combination of argument types.
Closest candidates are:
convert(::Type{T}, ::T) where T
@ Base Base_compiler.jl:136
Stacktrace:
[1] fill!(A::Matrix{UndefInitializer}, x::String)
@ Base ./array.jl:358
[2] top-level scope
@ In[7]:1
[3] eval(m::Module, e::Any)
@ Core ./boot.jl:489A = Array{String}(undef, 2, 2)
println(A)
fill!(A, "Hello!")
println(A)[#undef #undef; #undef #undef] ["Hello!" "Hello!"; "Hello!" "Hello!"]
zeros a ones
Často chceme předvyplnit matice jedničkami, nebo nulami.
Přesně k tomuto účelu slouží metody zeros(T, dims...) a ones(T, dims...).
První argument, tedy typ, je nepovinný, výchozí hodnota je Float.
zeros(2, 2)2×2 Matrix{Float64}:
0.0 0.0
0.0 0.0ones(3)3-element Vector{Float64}:
1.0
1.0
1.0ones(2, 2)2×2 Matrix{Float64}:
1.0 1.0
1.0 1.0ones(2, 2, 2)2×2×2 Array{Float64, 3}:
[:, :, 1] =
1.0 1.0
1.0 1.0
[:, :, 2] =
1.0 1.0
1.0 1.0Kdybychom chtěli stejné matice, ovšem s Int64 prvky, stačí tento požadavek explicitně zmínit:
zeros(Int64, 2, 2)2×2 Matrix{Int64}:
0 0
0 0ones(Int64, 3)3-element Vector{Int64}:
1
1
1Pro vytvoření pouze nuly nebo jedničky v daném typu slouží analogické metody zero a one.
zero(Float64)0.0
zero(0.1 + 2.1im)0.0 + 0.0im
one(Int64)1
one(38465)1
ones(4, 4)4×4 Matrix{Float64}:
1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0one(Matrix{Int64}(undef, 2, 2))2×2 Matrix{Int64}:
1 0
0 1Ale viz I v další lekci.
copy a deepcopy
Tyto metody asi nepotřebují vysvětlení. Vytvoří kopii, resp. hlubokou kopii, daného pole. Názorná ukázka:
A = Array{Any}(undef, 2)
a = 42
b = [1, 2]
A[1] = a
A[2] = b
A2-element Vector{Any}:
42
[1, 2]C = copy(A)
D = deepcopy(A)
a = 11
b[1] = 9999
println(C)
println(D)Any[42, [9999, 2]] Any[42, [1, 2]]
b2-element Vector{Int64}:
9999
2rand a randn
Náhodně generovaným hodnotám, resp. pravděpodobnosti a statistice, se budeme podrobně věnovat později během semestru. Zde si zmíníme jenom dvě metody pro vytváření "náhodných" matic.
První z nich, rand(T, dims...) vytvoří pole dimenzí dims s prvky typu T rovnoměrně rozloženými (u podtypů AbstractFloat v intervalu ), první typový argument je opět nepovinný a jeho výchozí hodnota je Float64.
rand(Int64, 4)4-element Vector{Int64}:
3180059457554898787
6494474076006121240
3284801994282569457
-152020939516039289rand(5, 5)5×5 Matrix{Float64}:
0.234773 0.864187 0.918251 0.875007 0.0505554
0.957278 0.0263307 0.346336 0.443664 0.483722
0.644632 0.114567 0.138979 0.948895 0.145756
0.332579 0.989134 0.879961 0.269407 0.172091
0.255715 0.765739 0.5823 0.467442 0.585604rand([1, 2, "Ahoj!"])"Ahoj!"
rand(1:10)4
rand([1 2 "Ahoj!"; π 7 Any], 2, 2)2×2 Matrix{Any}:
Any 2
Any 1Příkaz randn(T, dims...) se od předchozího liší tím, že generuje prvky podle normálního rozdělení (se střední hodnotou a standardní odchylkou , má dobrý smysl jen pro podtypy AbstractFloat).
randn(10)10-element Vector{Float64}:
-1.0816936645870152
1.7825259691910118
0.06440564871489422
0.3121925452932865
-0.12798526207072625
2.0184665390531453
-1.0459915260474693
0.6118705533420232
0.17248473676548942
-1.1576328667834044Následující pokus přirozeně skončí nezdarem:
randn(Int64, 2, 2)MethodError: no method matching randn(::Random.TaskLocalRNG, ::Type{Int64})
The function `randn` exists, but no method is defined for this combination of argument types.
Closest candidates are:
randn(::Random.AbstractRNG, ::Type{T}, ::NTuple{N, Int64} where N) where T
@ Random /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:269
randn(::Random.AbstractRNG, ::Type{T}, ::Integer, ::Integer...) where T
@ Random /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:272
randn(::Random.AbstractRNG, ::Union{Type{Float16}, Type{Float32}, Type{Float64}})
@ Random /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:224
...
Stacktrace:
[1] randn!
@ /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:230 [inlined]
[2] randn
@ /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:272 [inlined]
[3] randn(::Type{Int64}, ::Int64, ::Int64)
@ Random /usr/share/julia/stdlib/v1.13/Random/src/normal.jl:274
[4] top-level scope
@ In[38]:1
[5] eval(m::Module, e::Any)
@ Core ./boot.jl:489Pokud rand předáme pole, pak náhodně vybere jeden jeho prvek:
for j = 1:10
println(rand([1, 2, 3, 4]))
end4 2 2 4 4 1 2 4 1 4
for j = 1:10
println(rand(1:4))
end3 1 2 4 3 2 1 1 4 2
for j = 1:10
println(rand([-1 0; 1 2]))
end-1 2 0 0 2 0 -1 1 2 0
To by jako exkurze do světa náhody pro tento okamžik stačilo. Podrobněji se do něj vrátíme hned příště.
range
Pro vytváření jednorozměrných polí se často hodí metoda range(start; stop, length), pomocí níž lze vytvořit pole s rovnoměrně rozmístěnými prvky:
range(1, stop=5)1:5
range(1, stop=6, length=10)Další užitečné metody
Pro práci s poli se mohou hodit následující metody:
eltype(A): typ prvků polelength(A): počet prvků polendims(A): počet rozměrů ("dimenzí") polesize(A): tuple s velikostí jednotlivých rozměrů polesize(A, n): velikost tého rozměru poleaxes(A): tuple s hodnotami indexů v jednotlivých rozměrech (o přeindexování polí později)
Ukázka:
A = rand(4, 3, 2)4×3×2 Array{Float64, 3}:
[:, :, 1] =
0.712 0.533139 0.362059
0.563498 0.811968 0.543035
0.683902 0.766389 0.17583
0.932175 0.514862 0.9299
[:, :, 2] =
0.642265 0.13877 0.388891
0.864212 0.911015 0.682836
0.163881 0.288806 0.352623
0.286188 0.617202 0.245521eltype(A)Float64
length(A)24
Vlastní implementace:
typ_prvku(matice::Matrix{N}) where {N} = Ntyp_prvku (generic function with 1 method)
typ_prvku(ones(2, 2))Float64
typ_prvku([1 1; "String" 1.0])Any
typeof(A)Array{Float64, 3}length(A)24
4 * 3 * 224
length([[1, 2], [3, 4]])2
ndims(A)3
ndims([1, 2])1
ndims([1 2; 3 4])2
size(A)(4, 3, 2)
println(size(A, 1))
println(size(A, 2))
println(size(A, 3))4 3 2
Tj. nemusíme dělat ošklivé:
size(A)[2]3
axes(A)(Base.OneTo(4), Base.OneTo(3), Base.OneTo(2))
axes(A, 2)N.B.:
Base.OneTo(4) == 1:4for j = 1:size(A, 2)
println(A[1, j, 1])
endfor j = axes(A, 2)
println(A[1, j, 1])
end1.2 Cvičení
- Vytvořte matici
Arozměru mající na antidiagonálách postupně konstanty , , ,..., . - Vytvořte matici
Brozměru , která má všechny prvky nulové, pouze na "okraji" (obdélník) má jedničky. - Vytvořte čtvercovou matici
Crozměru , která je složena ze čtyř stejně velkých bloků: vlevo nahoře je blok náhodně vygenerovaných strojových čísel, vpravo nahoře matice naplněná jedničkami, vlevo dole nulová matice a vpravo dole diagonální matice s počátečními ciframi čísla na diagonále. - Vytvořte čtvercovou matici
Drozměru , která bude v levém horním trojúhelníku mít Pascalův trojúhelník. Prvky vypočtete Pascalovou "konstrukcí", ne počítáním kombinačních čísel.
A = [ i + j + 1 for i = 0:9, j = 0:9 ]
AB = [ i == 1 || j == 1 || i == 5 || j == 10 ? 1 : 0 for i = 1:5, j = 1:10]
BB = ones(Int64, 5, 10)
B[2:4, 2:9] = zeros(Int64, 3, 8)
Bπvo = [3, 1, 4, 1, 5]
Pi = [ j == k ? πvo[j] : 0 for j = 1:5, k = 1:5 ]
C = [ rand(5, 5) ones(5, 5); zeros(5, 5) Pi ]
CD = [
1 1 1 1
1 2 3 0
1 3 0 0
1 0 0 0
]# Toto jistě není nejjednodušší řešení!
D = zeros(Int64, 10, 10)
D[1, :] = ones(10)
D[:, 1] = ones(10)
for j = 2:10 # cisluje antidiagonalu
for k = 2:(j-1) # iterace po antidiagonale
D[1 + j - k, k] = D[1 + j - k - 1, k] + D[1 + j - k, k - 1]
end
end
D2. Operace s poli/maticemi a jejich prvky
S poli a jejich prvky lze provádět celou řadu nejen matematických operací. Většinou jsme motivování operacemi s vektory a maticemi (na rozdíl od jiných programovacích jazyků).
2.1 Sčítání + a odčítání -
Pole lze přirozeně po složkách sčítat (+), odčítat (-).
Tyto operátory fungují jak unárně, tak binárně.
Pole musí být stejného rozměru.
A = [1 2; 3 4]2×2 Matrix{Int64}:
1 2
3 4B = [5 6 7; 8 9 10]2×3 Matrix{Int64}:
5 6 7
8 9 10C = [1 1; -1 -1]2×2 Matrix{Int64}:
1 1
-1 -1Součet a rozdíl po složkách, pokud si odpovídají rozměry:
A + A2×2 Matrix{Int64}:
2 4
6 8A - C2×2 Matrix{Int64}:
0 1
4 5"Opačná matice":
-A2×2 Matrix{Int64}:
-1 -2
-3 -4A + BDimensionMismatch: a has size (2, 2), b has size (2, 3), mismatch at dim 2
Stacktrace:
[1] throw_promote_shape_mismatch(a::Tuple{Base.OneTo{…}, Base.OneTo{…}}, b::Tuple{Base.OneTo{…}, Base.OneTo{…}}, i::Int64)
@ Base ./indices.jl:135
[2] promote_shape
@ ./indices.jl:196 [inlined]
[3] promote_shape
@ ./indices.jl:188 [inlined]
[4] +(A::Matrix{Int64}, Bs::Matrix{Int64})
@ Base ./arraymath.jl:14
[5] top-level scope
@ In[66]:1
[6] eval(m::Module, e::Any)
@ Core ./boot.jl:489
Some type information was truncated. Use `show(err)` to see complete types.[1, 2, 3] + [4, 5, 6]3-element Vector{Int64}:
5
7
9[1, 2, 3] - [4, 5, 6]3-element Vector{Int64}:
-3
-3
-3[1, 2, 3] + [1 2 3]DimensionMismatch: a has size (1, 3), b has size (3,), mismatch at dim 1
Stacktrace:
[1] throw_promote_shape_mismatch(a::Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}, b::Tuple{Base.OneTo{Int64}}, i::Int64)
@ Base ./indices.jl:135
[2] promote_shape
@ ./indices.jl:196 [inlined]
[3] promote_shape
@ ./indices.jl:193 [inlined]
[4] promote_shape
@ ./indices.jl:188 [inlined]
[5] +(A::Vector{Int64}, Bs::Matrix{Int64})
@ Base ./arraymath.jl:14
[6] top-level scope
@ In[69]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489[1, 2, 3] + [1 2 3]'3×1 Matrix{Int64}:
2
4
6[1, 2, 3]' + [1 2 3]1×3 Matrix{Int64}:
2 4 6Chování vůči typům:
1 + 2.43.4
C + rand(2, 2)2×2 Matrix{Float64}:
1.12243 1.91474
-0.79342 -0.106897Násobení *
Hvězdička má význam "násobení" a to má více významů. Pole můžeme násobit číslem (skalárem), hvězdičku v tomto případě lze i vynechat:
A2×2 Matrix{Int64}:
1 2
3 44 * A2×2 Matrix{Int64}:
4 8
12 16-2A2×2 Matrix{Int64}:
-2 -4
-6 -8A * 22×2 Matrix{Int64}:
2 4
6 8Dále má hvězdička význam maticového násobení v pravém slova smyslu, tj. matice musí být správného rozměru:
A * A2×2 Matrix{Int64}:
7 10
15 22A * B2×3 Matrix{Int64}:
21 24 27
47 54 61B * ADimensionMismatch: incompatible dimensions for matrix multiplication: tried to multiply a matrix of size (2, 3) with a matrix of size (2, 2). The second dimension of the first matrix: 3, does not match the first dimension of the second matrix: 2.
Stacktrace:
[1] matmul_size_check_error(sizeA::Tuple{Int64, Int64}, sizeB::Tuple{Int64, Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:433
[2] matmul_size_check
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:420 [inlined]
[3] _mul
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:121 [inlined]
[4] mul
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:119 [inlined]
[5] *(A::Matrix{Int64}, B::Matrix{Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:116
[6] top-level scope
@ In[80]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489Násobení po složkách je něco jiného!
A .* A2×2 Matrix{Int64}:
1 4
9 16Inverze inv
Maticovou inverzi lze počítat pouze pro regulární matice.
inv([1 2; 3 4])2×2 Matrix{Float64}:
-2.0 1.0
1.5 -0.5[1 2; 3 4] * inv([1 2; 3 4])2×2 Matrix{Float64}:
1.0 0.0
8.88178e-16 1.0inv([1 2; 3 4]) * [1 2; 3 4] 2×2 Matrix{Float64}:
1.0 0.0
1.11022e-16 1.0inv([1 1; 1 1])SingularException: matrix is singular; factorization failed. Zero pivot found at index 2
Stacktrace:
[1] checknonsingular
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/factorization.jl:69 [inlined]
[2] _check_lu_success
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:84 [inlined]
[3] #lu!#180
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:92 [inlined]
[4] lu!
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:90 [inlined]
[5] lu!
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:89 [inlined]
[6] _lu
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:405 [inlined]
[7] lu(::Matrix{Int64}; kwargs::@Kwargs{})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:399
[8] lu
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/lu.jl:399 [inlined]
[9] inv(A::Matrix{Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/dense.jl:1148
[10] top-level scope
@ In[85]:1
[11] eval(m::Module, e::Any)
@ Core ./boot.jl:489inv([1 2 3; 4 5 6])DimensionMismatch: matrix is not square: dimensions are (2, 3)
Stacktrace:
[1] checksquare
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/LinearAlgebra.jl:344 [inlined]
[2] inv(A::Matrix{Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/dense.jl:1140
[3] top-level scope
@ In[86]:1
[4] eval(m::Module, e::Any)
@ Core ./boot.jl:489Vidíte, že je-li typem prvků matice integer, výsledkem bude matice strojových čísel. Podobné chování jsme už viděli i u obyčejného dělení. I pro jednotkovou matici, kde k popsání inverze strojová čísla potřeba nejsou, k tomuto efektu dojde:
inv([1 0; 0 1])2×2 Matrix{Float64}:
1.0 0.0
0.0 1.0Invertovat ale můžeme i přímo v aritmetice racionálních čísel.
ratA = Rational{Int64}[1 1; 1 -1]
ratA2×2 Matrix{Rational{Int64}}:
1 1
1 -1ratAinv = inv(ratA)2×2 Matrix{Rational{Int64}}:
1//2 1//2
1//2 -1//2ratA * ratAinv2×2 Matrix{Rational{Int64}}:
1 0
0 1ratAinv * ratA2×2 Matrix{Rational{Int64}}:
1 0
0 1Zpět k příklad výše.
m = [ 1//1 2//1; 3//1 4//1 ]
invm = inv(m)m * invmH = [ 1 // (i + j - 1) for i = 1:8, j = 1:8 ]inv(H)H * inv(H)H = [ 1 / (i + j - 1) for i = 1:15, j = 1:15 ]inv(H) * Hbig"1" // 2 |> typeofH = [ big"1" // (i + j - 1) for i = 1:15, j = 1:15 ]inv(H)inv(H) * HDělení /, resp. \
Přesněji, mezi maticovými operandy jde o násobení maticovou inverzí zprava, resp. zleva.
A / A2×2 Matrix{Float64}:
1.0 0.0
0.0 1.0A \ B2×3 Matrix{Float64}:
-2.0 -3.0 -4.0
3.5 4.5 5.5inv(A) * B2×3 Matrix{Float64}:
-2.0 -3.0 -4.0
3.5 4.5 5.5Umocňování ^
Matice lze umocňovat na celočíselné exponenty, se standardními omezeními.
A ^ 22×2 Matrix{Int64}:
7 10
15 22A * A2×2 Matrix{Int64}:
7 10
15 22A ^ 62×2 Matrix{Int64}:
5743 8370
12555 18298A * A * A * A * A * A2×2 Matrix{Int64}:
5743 8370
12555 18298A ^ -12×2 Matrix{Float64}:
-2.0 1.0
1.5 -0.5inv(A)2×2 Matrix{Float64}:
-2.0 1.0
1.5 -0.5A ^ -32×2 Matrix{Float64}:
-14.75 6.75
10.125 -4.625inv(A) * inv(A) * inv(A)2×2 Matrix{Float64}:
-14.75 6.75
10.125 -4.625Provádění operací po složkách pomocí . (broadcasting)
Binární operace i metody lze provádět a aplikovat po složkách, resp. na prvcích matice, pomocí symbolu ..
Například můžeme vynásobit odpovídající si prvky obdélníkové matice (které by v pravém slova smyslu maticově násobit nešlo):
B2×3 Matrix{Int64}:
5 6 7
8 9 10[ iseven(B[j, k]) for j in axes(B, 1), k in axes(B, 2) ]2×3 Matrix{Bool}:
0 1 0
1 0 1iseven.(B)2×3 BitMatrix: 0 1 0 1 0 1
map(iseven, B)2×3 Matrix{Bool}:
0 1 0
1 0 1iseven(B)MethodError: no method matching iseven(::Matrix{Int64})
The function `iseven` exists, but no method is defined for this combination of argument types.
Closest candidates are:
iseven(::BigInt)
@ Base gmp.jl:367
iseven(::Missing)
@ Base missing.jl:103
iseven(::AbstractFloat)
@ Base float.jl:957
...
Stacktrace:
[1] top-level scope
@ In[107]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489B2×3 Matrix{Int64}:
5 6 7
8 9 10B .* B2×3 Matrix{Int64}:
25 36 49
64 81 100B * BDimensionMismatch: incompatible dimensions for matrix multiplication: tried to multiply a matrix of size (2, 3) with a matrix of size (2, 3). The second dimension of the first matrix: 3, does not match the first dimension of the second matrix: 2.
Stacktrace:
[1] matmul_size_check_error(sizeA::Tuple{Int64, Int64}, sizeB::Tuple{Int64, Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:433
[2] matmul_size_check
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:420 [inlined]
[3] _mul
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:121 [inlined]
[4] mul
@ /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:119 [inlined]
[5] *(A::Matrix{Int64}, B::Matrix{Int64})
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/matmul.jl:116
[6] top-level scope
@ In[110]:1
[7] eval(m::Module, e::Any)
@ Core ./boot.jl:489Operand ani nemusí být maticový:
4 .* B2×3 Matrix{Int64}:
20 24 28
32 36 404 * B2×3 Matrix{Int64}:
20 24 28
32 36 404 .+ B2×3 Matrix{Int64}:
9 10 11
12 13 144 + BMethodError: no method matching +(::Int64, ::Matrix{Int64})
For element-wise addition, use broadcasting with dot syntax: scalar .+ array
The function `+` exists, but no method is defined for this combination of argument types.
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...)
@ Base operators.jl:653
+(::Array, ::Array...)
@ Base arraymath.jl:12
+(::LinearAlgebra.UniformScaling, ::AbstractMatrix)
@ LinearAlgebra /usr/share/julia/stdlib/v1.13/LinearAlgebra/src/uniformscaling.jl:156
...
Stacktrace:
[1] top-level scope
@ In[114]:1
[2] eval(m::Module, e::Any)
@ Core ./boot.jl:489B2×3 Matrix{Int64}:
5 6 7
8 9 10B[:, 2:3] .= 0
B2×3 Matrix{Int64}:
5 0 0
8 0 0B .<< 12×3 Matrix{Int64}:
10 0 0
16 0 0Můžeme například aplikovat sinus na všechny prvky matice:
sin.(A)2×2 Matrix{Float64}:
0.841471 0.909297
0.14112 -0.756802Pozor! Následující kód je naprosto v pořádku, většinu elementárních funkcí lze zavést i pro operátory (čtvercové matice):
sin(A)2×2 Matrix{Float64}:
-0.465581 -0.148424
-0.222637 -0.688218A - A^3 / 6 + A^5 / factorial(5) - A^7 / factorial(7) + A ^ 9 / factorial(9) - A ^ 11 / factorial(11) +
A ^ 13 / factorial(13) - A ^ 15 / factorial(15)2×2 Matrix{Float64}:
-0.467181 -0.150756
-0.226135 -0.693316Tento přístup (broadcasting) funguje i pro funkce mající více či méně argumentů, nebo pro další binární operátory!
f = (x) -> x^3#5 (generic function with 1 method)
A2×2 Matrix{Int64}:
1 2
3 4f.(A)2×2 Matrix{Int64}:
1 8
27 64map(f, A)2×2 Matrix{Int64}:
1 8
27 64A2×2 Matrix{Int64}:
1 2
3 4C2×2 Matrix{Int64}:
1 1
-1 -1min.(A, C)2×2 Matrix{Int64}:
1 1
-1 -1min.(A, 3)2×2 Matrix{Int64}:
1 2
3 3min(A, 3)MethodError: no method matching isless(::Int64, ::Matrix{Int64})
The function `isless` exists, but no method is defined for this combination of argument types.
Closest candidates are:
isless(::Missing, ::Any)
@ Base missing.jl:87
isless(::Integer, ::Base.CoreLogging.LogLevel)
@ Base logging/logging.jl:133
isless(::Real, ::AbstractFloat)
@ Base operators.jl:223
...
Stacktrace:
[1] min(x::Matrix{Int64}, y::Int64)
@ Base ./operators.jl:555
[2] top-level scope
@ In[129]:1
[3] eval(m::Module, e::Any)
@ Core ./boot.jl:489min.(A, C, ones(Int64, 2, 2))2×2 Matrix{Int64}:
1 1
-1 -1Maskování
Porovnáváním (například pomocí <) matic prvek po prvku můžeme vytvořit masku, pomocí které lze poté z matice extrahovat požadované prvky.
A2×2 Matrix{Int64}:
1 2
3 4C = [0 3; 4 -1]2×2 Matrix{Int64}:
0 3
4 -1A .< C2×2 BitMatrix: 0 1 1 0
mask = A .< C2×2 BitMatrix: 0 1 1 0
V zápisu výše jde v podstatě o infixovou notaci, explicitněji:
isless.(A, C)2×2 BitMatrix: 0 1 1 0
Indexováním pomocí takovéto matice Dostaneme pouze prvky se zkoumanou vlastností:
A[mask]2-element Vector{Int64}:
3
2A[A .< C]2-element Vector{Int64}:
3
2Jeden z operandů opět nutně nemusí být maticový:
C2×2 Matrix{Int64}:
0 3
4 -1C .<= 22×2 BitMatrix: 1 0 0 1
C[C .<= 2]2-element Vector{Int64}:
0
-1Nebo (všimněte si, že nedostaneme matici s Boolean prvky, jak bychom možná očekávali):
mask = iseven.(A)2×2 BitMatrix: 0 1 0 1
A[mask]2-element Vector{Int64}:
2
4Alternativně bychom k podobnému účelu mohli použít metodu filter:
filter(iseven, A)2-element Vector{Int64}:
2
4Porovnání rychlosti
Explicitní for loop vs. broadcasting.
using BenchmarkTools
function f1(A::Matrix{T}) where { T <: Number }
A[1, :] .*= A[1, :]
end
function f2(A::Matrix{T}) where { T <: Number }
for j in axes(A, 2)
A[1, j] *= A[1, j]
end
end
function f3(A::Matrix{T}) where { T <: Number }
A[1, :] = A[1, :] .* A[1, :]
end
mat = rand(2, 1_000_000)2×1000000 Matrix{Float64}:
0.811779 0.602917 0.732652 0.0773776 … 0.719825 0.118598 0.999523
0.170057 0.0402556 0.375776 0.997364 0.136964 0.619693 0.477143@benchmark f1($mat)BenchmarkTools.Trial: 426 samples with 1 evaluation per sample. Range (min … max): 6.459 ms … 57.138 ms ┊ GC (min … max): 0.00% … 75.52% Time (median): 9.581 ms ┊ GC (median): 0.00% Time (mean ± σ): 11.743 ms ± 9.487 ms ┊ GC (mean ± σ): 22.03% ± 20.35% █▅▅▅▆▅▄▅ ▁ ███████████▇█▅▆▄▄▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▇▄▄▁▄▇▇▁▆ ▇ 6.46 ms Histogram: log(frequency) by time 53 ms < Memory estimate: 15.26 MiB, allocs estimate: 5.
@benchmark f2($mat)BenchmarkTools.Trial: 2893 samples with 1 evaluation per sample. Range (min … max): 1.109 ms … 5.194 ms ┊ GC (min … max): 0.00% … 0.00% Time (median): 1.239 ms ┊ GC (median): 0.00% Time (mean ± σ): 1.727 ms ± 789.552 μs ┊ GC (mean ± σ): 0.00% ± 0.00% ▇█▆▅▄▃▂▁▁▁ ▁▁▁ ▁▁▁▁▁▁▁▁▁ ▁▁ ▁ ▁ ▁ ██████████▇█▇▆▇▆▇▆▇▇▇▇▇▇████████████████████████▇███▇▇▇▇▆▅▅ █ 1.11 ms Histogram: log(frequency) by time 3.7 ms < Memory estimate: 0 bytes, allocs estimate: 0.
@benchmark f3($mat)BenchmarkTools.Trial: 326 samples with 1 evaluation per sample. Range (min … max): 7.446 ms … 77.541 ms ┊ GC (min … max): 0.00% … 79.38% Time (median): 11.708 ms ┊ GC (median): 0.00% Time (mean ± σ): 15.339 ms ± 12.558 ms ┊ GC (mean ± σ): 26.61% ± 22.58% █▁▄ ▁ ▂▂ ███▇█▇▇██▄▅▄▃▃▃▃▂▃▁▂▁▁▁▁▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂▂▃▂▃▃▃▃ ▃ 7.45 ms Histogram: frequency by time 56.9 ms < Memory estimate: 22.89 MiB, allocs estimate: 9.
Zde vidíme, že Julia má ráda rychlé výpočetní for smyčky, není nutná snaha o vektorizaci, jak je tomu třeba u Pythonu nebo Matlabu (kde pak díky tomu dané operace probíhají rychle mimo Python/Matlab díky C/FORTRAN).
Maskování vs. filter.
function my_filter(A::Matrix{T}, number::T) where { T <: Number }
A[A .< number]
endmy_filter (generic function with 1 method)
@benchmark my_filter($mat, 0.5)BenchmarkTools.Trial: 459 samples with 1 evaluation per sample.
Range (min … max): 7.530 ms … 54.230 ms ┊ GC (min … max): 0.00% … 82.56%
Time (median): 10.197 ms ┊ GC (median): 2.80%
Time (mean ± σ): 10.900 ms ± 4.746 ms ┊ GC (mean ± σ): 7.89% ± 9.50%
█
█▄▂▄▆▇█▆▅▅▄▅▄▅▅▅▅▅▇▅▆▅▅▅▃▃▄▄▃▄▃▄▃▃▃▂▂▂▂▂▁▂▁▂▁▂▁▁▁▁▁▁▂▁▁▁▂▁▂ ▃
7.53 ms Histogram: frequency by time 19.1 ms <
Memory estimate: 11.68 MiB, allocs estimate: 7.@benchmark filter((x) -> x < 0.5, $mat)BenchmarkTools.Trial: 606 samples with 1 evaluation per sample. Range (min … max): 2.688 ms … 75.674 ms ┊ GC (min … max): 0.00% … 90.44% Time (median): 5.076 ms ┊ GC (median): 0.00% Time (mean ± σ): 8.250 ms ± 11.448 ms ┊ GC (mean ± σ): 44.71% ± 27.28% █▄▃▅▅▃▃▁ ███████████▆▇▅▅▄▄▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▇▆▆▅▆▇▆▆▇ ▇ 2.69 ms Histogram: log(frequency) by time 49.6 ms < Memory estimate: 26.70 MiB, allocs estimate: 5.
Cvičení: Monte Carlo výpočet operátorové normy symetrické matice
Pro reálnou čtvercovou matici definujeme její operátorovou normu předpisem Na pravé straně mají normy význam Euklidovké normy na .
Jinak řečeno, každá hodnota pro jednotkový vektor představuje spodní odhad .
Implementujte metodu, která se náhodnou volbou různých vektorů snaží dostat co nejlepší odhad této normy.
Zatím nepoužívejme nástroje z LinearAlgebra, vystačíme si se základními nástroji pro práci s poli. Připomeňme, že Euklidovská norma vektoru je .
using ProgressMeter
function rands2(n::Int64)
x = rand(n) .- 0.5
return x / sqrt(sum(x .* x))
end
"""
Náhodný vektor na jednotkové `n`-dimenzionální sféře.
"""
function rands(n::Int64)
v = ones(n)
f = () -> 2 * (rand() - 0.5)
v[1] = f()
v[2] = sqrt(1 - v[1]^2)
for j = 2:n-2
r = f()
v[j], v[j+1] = v[j] * r, v[j] * sqrt(1 - r^2)
end
if n > 2
r = f()
v[n-1], v[n] = v[n-1] * r, v[n-1] * rand([-1, 1]) * sqrt(1 - r^2)
end
return v
end
"""
Spodní odhad operátorové normy pomocí Monte Carlo přístupu.
"""
function mc_op_norm(A::Matrix{Float64}, trials::Int64)
size(A, 1) == size(A, 2) || error("`A` is not a square matrix!")
max_norm = 0.0
@showprogress 1 "Tossing coins furiously..." for j = 1:trials
x = A * rands(size(A, 1))
val = sum(x .* x)
val > max_norm && (max_norm = val)
end
return sqrt(max_norm)
endTest náhodného generátoru.
rands2(2)x = rands(2)
println(x)
sum(x .* x)x = rands(10)
println(x)
sum(x .* x)Normy některých matic. Začněme nulovou maticí.
mc_op_norm(zeros(3, 3), 10)Diagonální matice.
mc_op_norm([-4. 0; 0 3], 1_000_000)A ještě jedna zajímavá matice:
mc_op_norm([1. 4; 5 6], 10^6)sqrt(39 + 5sqrt(53))Pro rostoucí do nekonečna se norma následujících matic blíží .
J(n) = Float64[ abs(j-k) == 1 ? 1 : 0 for j=1:n, k=1:n ]J(5)mc_op_norm(J(2), 10^6)mc_op_norm(J(20), 10^6)mc_op_norm(J(20), 10^7)mc_op_norm(J(30), 10^9)Pro rostoucí rozměry by tato hodnota měla konvergovat k .
H = [ 1 / (j + k - 1) for j = 1:50, k = 1:50 ]mc_op_norm(H, 10_000_000)Cvičení 2.3: Erastothenovo síto
Pomocí maskování (indexování pomocí BitArray) lze elegantně využít k implementaci jednoduchého Erastothenova síta.
Erastothenovo síto nám umožňuje nalézt všechna prvočísla menší nebo rovna zadané mezi (n).
Základní verze bez optimalizací je následující
- Jednička není prvočíslo.
- Dalším číslem je dvojka, tedy prvočíslo, zahodíme všechny další násobky dvou.
- Dalším nevyškrtnutým číslem je trojka, tedy prvočíslo, zahodíme všechny další násobky tří.
- Čtyřku jsme už vyškrtli, takže se dostaneme k pětce, prvočíslu, a opět zahodíme všechny další násobky pěti.
- atd.
I. Implementujte metodu erastothenes(n) počítající všechna prvnočísla menší nebo rovna n.
# N.B. BitArray:
m = BitArray([1 0; 1 0])
println(eltype(m))
println(m[1,1] == true)# Range se chová jako vektor
(1:5)[[true, true, false, false, true]]3-element Vector{Int64}:
1
2
5function erastothenes(n::Integer)
mask = ones(Bool, n)
mask[1] = false
for j = 2:ceil(Int64,sqrt(n))
if mask[j]
for k = 2j:j:n
mask[k] = 0
end
end
end
return (1:n)[mask]
enderastothenes (generic function with 1 method)
erastothenes(50)15-element Vector{Int64}:
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47II. Spočtěte všechna prvnočísla menší nebo rovna milion (OEIS:A000040).
ps = erastothenes(100_000_000)5761455-element Vector{Int64}:
2
3
5
7
11
13
17
19
23
29
31
37
41
⋮
99999721
99999773
99999787
99999821
99999827
99999839
99999847
99999931
99999941
99999959
99999971
99999989III. S pomocí napočtených dat nalezněte všechna prvočíselná dvojčata v uvedeném rozsahu (tj. prvočísla lišící se o ; OEIS:A077800). Implementujte metodu, která nalezne všechna dvojčata lišící se o zadané číslo. Tato metoda bude vracet pole tuplů (dvojčat).
function twins(primes::Vector{Int64}, gap::Int64)
# ...
endtwins(ps, 2)twins(ps, 4)Řešení některých příkladů
Možné řešení Erastothenova síta:
function erastothenes(n::Integer)
mask = ones(Bool, n)
mask[1] = 0
for k = 2:n
if mask[k]
for j = 2k:k:n
mask[j] = 0
end
end
end
(1:n)[mask]
endMožné řešení twins metody.
function twins(primes::Vector{Int64}, gap::Int64)
twins = []
for j in 2:length(primes)
if primes[j] - primes[j-1] == gap
push!(twins, (ps[j-1], ps[j]))
end
end
return twins
endMonte Carlo.
using ProgressMeter
"""
Náhodný vektor na jednotkové `n`-dimenzionální sféře.
"""
function rands(n::Int64)
v = ones(n)
f = () -> 2 * (rand() - 0.5)
v[1] = f()
v[2] = sqrt(1 - v[1]^2)
for j = 2:n-2
r = f()
v[j], v[j+1] = v[j] * r, v[j] * sqrt(1 - r^2)
end
if n > 2
r = f()
v[n-1], v[n] = v[n-1] * r, v[n-1] * rand([-1, 1]) * sqrt(1 - r^2)
end
return v
end
"""
Spodní odhad operátorové normy pomocí Monte Carlo přístupu.
"""
function mc_op_norm(A::Matrix{Float64}, trials::Int64)
size(A, 1) == size(A, 2) || error("`A` is not a square matrix!")
max_val = 0.0
@showprogress 1 "Tossing coins furiously..." for j = 1:trials
v = A * rands(size(A, 1))
val = sum(v .* v)
val > max_val && (max_val = val)
end
sqrt(max_val)
end