{%hackmd SK1vjm9IQLOH_XRYTfAfkA %} Julia クイズ <br>~配列編~ === <!-- .element: style="font-size: 300%" --> 2024/04/26 KCIバータイム 後藤 俊介 Note: … --- ## はじめに + LT なのでいちいち回答を求めずどんどん進みます。 + 純粋にクイズとして楽しみたい人は、LTを聞かずに後でこのスライドを個人で見てくださいw + クイズとして楽しみたい人向けの補足(<kbd>↓</kbd>キー押してね) ---- ### 補足 + **問 ◯. ~** というタイトルのスライドで <kbd>↓</kbd> キーを押すと、答えが表示されます(サブスライド機能)。 + <kbd>→</kbd> キーを押すと答えをスキップして次の問題に移ります。 + <kbd>←</kbd> キーで前の問題に戻ります。 --- ## 概要 + Julia の配列の入門的な内容をクイズにしました。 + 特に Python のリストや NumPy の Array との差異に注目。 --- ## 動作環境 + Julia v1.10.2 + すべて REPL で動作確認済 --- # インデクシングの基本 --- ## 問 1. インデックス(1) ```julia julia> a = [1, 2, 3] 3-element Vector{Int64}: 1 2 3 julia> a[0] #> ??? ``` 1. 0番目の要素 `1` が返る 2. `BoundsError` が発生する (答えは <kbd>↓</kbd> キーで(以下同)) ---- ### 答え. **2. `BoundsError` が発生する** ```julia julia> a[0] ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [0] Stacktrace: [1] getindex(A::Vector{Int64}, i1::Int64) @ Base ./essentials.jl:13 [2] top-level scope @ REPL[XXX]:1 ``` ---- ### 解説. + Julia は 1-origin であり、インデックスは 1 から始まります。標準の配列は、最初の要素を取得するには `a[1]` と書く必要があります。 ```julia julia> a[1] 1 ``` --- ## 問 2. インデックス(2) ```julia julia> a = [1, 2, 3] 3-element Vector{Int64}: 1 2 3 julia> a[-1] #> ??? ``` 1. 最後の要素 `3` が返る 2. `BoundsError` が発生する ---- ### 答え. **2. `BoundsError` が発生する** ```julia julia> a[-1] ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [-1] Stacktrace: [1] getindex(A::Vector{Int64}, i1::Int64) @ Base ./essentials.jl:13 [2] top-level scope @ REPL[XXX]:1 ``` ---- ### 解説. + Julia の配列は、有効なインデックス値は `1`~(要素数)です。負のインデックスはサポートされていません。 + 最後の要素を取得したい場合は、`a[end]` と書くことができます。また後ろから2番目なら `a[end-1]` と書けます。 ```julia julia> a[end] 3 julia> a[end-1] 2 ``` --- # 行列の基本 --- ## 前提知識 + Julia では2次元配列を簡単に書けます。 + Julia の2次元配列は `Matrix` というエイリアス(つまり **行列** という別名)が付いています。 ```juia julia> A = [1 2 3 4] 2×2 Matrix{Int64}: 1 2 3 4 julia> A = [1 2; 3 4] # このようにインラインで書くことも可能(結果は同じ) 2×2 Matrix{Int64}: 1 2 3 4 ``` --- ## 問 3. インデックス(3) ```julia julia> A = [1 2 3 4] 2×2 Matrix{Int64}: 1 2 3 4 julia> A[1, 2] #> ??? ``` 1. 1行目・2列目の要素 `2` が返る 2. 1列目・2行目の要素 `3` が返る 3. `BoundsError` が発生する ---- ### 答え. **1. 1行目・2列目の要素 `2` が返る** ```julia julia> A[1, 2] 2 ``` ---- ### 解説. + Julia の2次元配列のインデックスは、カンマ区切りで `A[《行》,《列》]` の指定となります。 + (NumPy の配列も順番だけ見ると同じかも?) ```julia julia> A[2, 1] 3 ``` --- ## 問 4. インデックス(4) ```julia julia> A = [1 2 3 4] 2×2 Matrix{Int64}: 1 2 3 4 julia> A[2] #> ??? ``` 1. 2行目 `[3, 4]` が返る 2. 2行目の最初の要素 `3` が返る 3. `DimensionMismatch` が発生する ---- ### 答え. **2. 2行目の最初の要素 `3` が返る** 正確には「列指向で2番目の要素が返る」 ```julia julia> A[2] 3 ``` ---- ### 解説. + Julia の2次元配列(多次元配列)は、インデックスに整数値1つ(`N`)だけを指定すると「左上から数えた`N`番目の要素が返ります。 + その数え方は、「上から下へ→左から右へ」の順です(列指向)。つまり `A[N]==A[(N-1)%《行数》+1, (N-1)÷《行数》+1]` です。 + NumPy の配列と違い(その記法では)行や列を抽出することはできません。 ```julia julia> A[1], A[2], A[3], A[4] (1, 3, 2, 4) julia> A[end] 4 ``` --- ## 問 5. インデックス(5) ```julia julia> A = [1 2 3 4] 2×2 Matrix{Int64}: 1 2 3 4 julia> # ??? #> `[1, 2]` すなわち1行目を返す記法は? ``` 1. `A[1,]` 2. `A[1, :]` 3. `A[1, -]` ---- ### 答え. **2. `A[1, :]`** ```julia julia> A[1, :] 2-element Vector{Int64}: 1 2 ``` ---- ### 解説. + 二次元配列で行(`N`行目)を取得したい場合は、`A[N, :]` と書けばOKです。 + 列の場合は `A[:, N]` でOK(この辺も NumPy の配列と同様) ```julia julia> A[:, end] # 最後の列を取得 2-element Vector{Int64}: 2 4 ``` --- # スライス --- ## 問 6. スライス(1) ```julia julia> a = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> a[1:3] #> ??? ``` 1. 1~2要素 `[1, 2]` が返る 2. 1~3要素 `[1, 2, 3]` が返る 3. `BoundsError` が発生する ---- ### 答え. **2. 1~3要素 `[1, 2, 3]` が返る** ```julia julia> a[1:3] 3-element Vector{Int64}: 1 2 3 ``` ---- ### 解説. + Julia のスライス表記(正確には **範囲リテラル表記**)は、閉区間すなわち開始も終了も含まれます。 + 行列の場合も同様です。 ```julia julia> A = [1 2 3 4 5 6] 2×3 Matrix{Int64}: 1 2 3 4 5 6 julia> A[1:2, 1:2] 2×2 Matrix{Int64}: 1 2 4 5 ``` --- ## 問 7. スライス(2) ```julia julia> a = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> a[2:] #> ??? ``` 1. 2要素目以降 `[2, 3, 4]` が返る 2. 2番目の要素 `2` が返る 3. 文法エラー ---- ### 答え. **3. 文法エラー** 正確には `ParseError` ```julia julia> a[2:] ERROR: ParseError: # Error @ REPL[165]:1:5 a[2:] # └ ── missing last argument in range expression Stacktrace: [1] top-level scope @ none:1 ``` ---- ### 解説. + エラーメッセージの通り、Julia の範囲リテラル表記は終了を省略できません。 + 「`N`個目から最後まで」を指定したい場合は `a[N:end]` と書けばOK。 ```julia julia> a[2:end] 3-element Vector{Int64}: 2 3 4 ``` --- ## 問 8. スライス(3) ```julia julia> a = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> a[:3] #> ??? ``` 1. 3要素目まで `[1, 2, 3]` が返る 2. 3番目の要素 `3` が返る 3. 文法エラー ---- ### 答え. **2. 3番目の要素 `3` が返る** ```julia julia> a[:3] 3 ``` ---- ### 解説. + Julia の範囲リテラル表記は、開始も省略できません。 + ただし `:3` は文法的には正しく(**シンボルリテラル表記**)、結果的に `3` という数値リテラルと同じ意味になります。なので `a[:3] == a[3] == 3` となります。 + 「最初から`N`個目まで」を指定したい場合は `a[begin:N]` と書けばOK。 ```julia julia> a[begin:3] # `a[1:3]` でもOK 3-element Vector{Int64}: 1 2 3 ``` --- ## 問 9. スライス(4) ```julia julia> a = [1, 2, 3, 4, 5] 5-element Vector{Int64}: 《略》 julia> # ??? #> [1, 3, 5] つまり1つおきに要素を取得するには? ``` 1. `a[1:end:2]` 2. `a[1:2:end]` 3. そのようなスライス表記はない ---- ### 答え. **2. `a[1:2:end]`** ```julia julia> a[1:2:end] 3-element Vector{Int64}: 1 3 5 ``` ---- ### 解説. + Julia の範囲リテラル表記は、ステップ値も指定できます。 + その場合、`start:step:stop` の順です。Python のスライス表記と順番が違うので注意。 + なお Python の `1::2`(終了値を省略)とか `::-1`(ステップ値だけを指定)のような表記はありません。 ```julia julia> a[end:-1:begin] # Python の `a[::-1]` 相当 5-element Vector{Int64}: 5 4 3 2 1 ``` --- ## 問 10. スライス(5) ```julia julia> a = [1, 2, 3, 4, 5] 5-element Vector{Int64}: 《略》 julia> a[1:6] #> ??? ``` 1. 全要素からなる `[1, 2, 3, 4, 5]` が返る 2. `BoundsError` が発生する ---- ### 答え. **2. `BoundsError` が発生する** ```julia julia> a[1:6] ERROR: BoundsError: attempt to access 5-element Vector{Int64} at index [1:6] Stacktrace: [1] throw_boundserror(A::Vector{Int64}, I::Tuple{UnitRange{Int64}}) @ Base ./abstractarray.jl:737 [2] checkbounds @ ./abstractarray.jl:702 [inlined] [3] getindex(A::Vector{Int64}, I::UnitRange{Int64}) @ Base ./array.jl:973 [4] top-level scope @ REPL[XXX]:1 ``` ---- ### 解説. + Julia のスライスは、インデックスの範囲外を指定すると `BoundsError` が発生してしまいます。 + ただし例外として、要素数が0件となる場合(=`a[1:0]`)はエラーになりません。 ```julia julia> a[1:0] Int64[] julia> a[6:5] Int64[] ``` --- # 演算 --- ## 問 11. ベクトル同士の演算(1) ```julia julia> x = [1, 2, 3]; # 行の最後に `;` をつけると式の値がREPLに表示されない julia> y = [4, 5, 6]; julia> x + y #> ??? ``` 1. `[1, 2, 3, 4, 5, 6]`(つまり連結) 2. `[5, 7, 9]`(つまり要素ごとの足し算) 3. 何らかのエラー ---- ### 答え. **2. `[5, 7, 9]`(つまり要素ごとの足し算)** ```julia julia> x + y 3-element Vector{Int64}: 5 7 9 ``` ---- ### 解説. + Julia では配列同士の加算は要素ごとの加算になります(減算も同様です)。 + なので要素数が異なれば `DimensionMissmatch` というエラーになります。 ```julia julia> y - x 3-element Vector{Int64}: 3 3 3 julia> x + [1, 2, 3, 4, 5] ERROR: DimensionMismatch: dimensions must match: a has dims (Base.OneTo(3),), b has dims (Base.OneTo(5),), mismatch at 1 Stacktrace:《長いので略》 ``` --- ## 問 12. ベクトル同士の演算(2) ```julia julia> x = [1, 2, 3]; julia> y = [4, 5, 6]; julia> x * y #> ??? ``` 1. `[4, 10, 18]`(つまり要素ごとの掛け算) 2. `32`(つまりベクトルの内積) 3. `[-3, 6, -3]`(つまりベクトルの外積) 4. 何らかのエラー ---- ### 答え. **4. 何らかのエラー** 正確には `MethodError`。 ```julia julia> x * y ERROR: MethodError: no method matching *(::Vector{Int64}, ::Vector{Int64}) 《以下略》 ``` ---- ### 解説. + Julia では1次元配列同士の乗算は定義されていません! + 要素ごとの掛け算(アダマール積)は、`a .* b` と書く必要があります。 ```julia julia> x .* y 3-element Vector{Int64}: 4 10 18 ``` ---- ### 補足. + 内積・外積は、`LinearAlgebra` 標準パッケージでサポートされる特別な演算子があります。 ```julia julia> using LinearAlgebra julia> x ⋅ y # 内積。`⋅` は `\cdot` + Tab で入力できます。 # なお `dot(x, y)` と書いてもOK 32 julia> x × y # 外積。`×` は `\times` + Tab で入力できます。 # なお `cross(x, y)` と書いてもOK 3-element Vector{Int64}: -3 6 -3 ``` --- ## 問 13. 行列同士の演算(1) ```julia julia> A = [1 2; 3 4]; julia> B = [5 6; 7 8]; julia> A + B #> ??? ``` 1. `[6 8; 10 12]`(つまり要素ごとの足し算) 2. `[1 2; 3 4; 5 6; 7 8]`(つまり列方向の結合) 3. 何らかのエラー ---- ### 答え. **1. `[6 8; 10 12]`(つまり要素ごとの足し算)** ```julia julia> A + B 2×2 Matrix{Int64}: 6 8 10 12 ``` ---- ### 解説. + Julia では配列同士の加算は要素ごとの加算になります(減算も同様です)。 + なので要素数や次元数が異なれば `DimensionMissmatch` というエラーになります。 ```julia julia> B - A 2×2 Matrix{Int64}: 4 4 4 4 julia> A + [1, 2] # 行列とベクトルの足し算はエラーになる ERROR: DimensionMismatch: dimensions must match: a has dims (Base.OneTo(2), Base.OneTo(2)), must have singleton at dim 2 Stacktrace:《略》 ``` --- ## 問 14. 行列同士の演算(2) ```julia julia> A = [1 2; 3 4]; julia> B = [5 6; 7 8]; julia> A * B #> ??? ``` 1. `[5 12; 21 32]`(つまり要素ごとの掛け算) 2. `[19 22; 43 50]`(つまり行列積) 3. 何らかのエラー ---- ### 答え. **2. `[19 22; 43 50]`(つまり行列積)** ```julia julia> A * B 2×2 Matrix{Int64}: 19 22 43 50 ``` ---- ### 解説. + Julia では2次元配列同士の乗算は定義されており、**行列積** になります(NumPy の `A.dot(B)` もしくは `A @ B` 相当)。 + `A .* B` と書けば要素ごとの積(アダマール積)になります。 + もちろん要素数が合わなければエラーになります。 ```julia julia> A .* B 2×2 Matrix{Int64}: 5 12 21 32 julia> A * [1 2; 3 4; 5 6] ERROR: DimensionMismatch: matrix A has dimensions (2,2), matrix B has dimensions (3,2) Stacktrace:《長いので略》 ``` --- ## 問 15. 行列とベクトルの演算(1) ```julia julia> A = [1 2; 3 4]; julia> x = [10, 20]; julia> A + x #> ??? ``` 1. `[11 22; 13 24]`(つまり行ごとのブロードキャスティング) 2. `[11 12; 23 24]`(つまり列ごとのブロードキャスティング) 3. 何らかのエラー ---- ### 答え. **3. 何らかのエラー** 正確には `DimensionMissmatch` ```julia julia> A + x ERROR: DimensionMismatch: dimensions must match: a has dims (Base.OneTo(2), Base.OneTo(2)), must have singleton at dim 2 Stacktrace:《長いので略》 ``` ---- ### 解説. + Julia では2次元配列と1次元配列(つまり行列とベクトル)の足し算は通常エラーになります(次元が合わないのはほぼ必至なので)。 + 要素ごとの足し算(ブロードキャスティング)を意図する場合、`A .+ x` もしくは `A .+ x'` と書く必要があります。前者は列ごとの、後者は行ごとの足し算になります。 + ↑要素数が合わなければエラーになります。 ```julia julia> A .+ x # A の各列に x を加算(行数が合わなければエラー) 2×2 Matrix{Int64}: 11 12 23 24 julia> A .+ x' # A の各行に x を加算(列数側なければエラー) 2×2 Matrix{Int64}: 11 22 13 24 ``` --- ## 問 16. 行列とベクトルの演算(2) ```julia julia> A = [1 2; 3 4]; julia> x = [10, 20]; julia> A * x #> ??? ``` 1. `[10 20; 60 80]`(つまり行列の各列とベクトルの積からなる行列) 2. `[50, 110]`(つまり行列と縦ベクトルの積) 3. 何らかのエラー ---- ### 答え. **2. `[50, 110]`(つまり行列と縦ベクトルの積)** つまりこれも **行列積**。 ```julia julia> A * x 2-element Vector{Int64}: 50 110 ``` ---- ### 解説. + Julia の1次元配列(ベクトル)は **縦ベクトル**(列ベクトル)扱いとなります。 + 2次元配列(行列)との積は数学的な $Ax$ という行列とベクトルの積と同じ意味になります。 + ↑要素数が合わなければエラーになります。 ```julia julia> A .* x # こう書くとA の各列に a を要素ごとに乗算(行数が合わなければエラー) 2×2 Matrix{Int64}: 10 20 60 80 julia> A * [1, 2, 3] ERROR: DimensionMismatch: matrix A has dimensions (2,2), vector B has length 3 Stacktrace:《長いので略》 ``` --- ## 問 17(おまけ). 行列とベクトルの演算(3) ```julia julia> A = [1 1; 2 4]; julia> y = [5, 14]; julia> A \ y #> ??? # `\` はバックスラッシュ ``` 1. $y = Ax$ を満たす `x` を計算してくれる 2. 何らかのエラー ---- ### 答え. **1. $y = Ax$ を満たす `x` を計算してくれる** ```julia julia> A \ y 2-element Vector{Float64}: 3.0 2.0 ``` ---- ### 解説. + `\`(バックスラッシュ)は「右除算演算子」と言い、スカラーどうしならば `a \ b == b / a` となります。 + 行列演算の場合、`A \ y` は `A^-1 * y` と同じ意味になります、つまり `y == A * x` を満たす `x` の計算(連立一次方程式の求解)になります。 + 裏で BLAS や LAPACK が動くので `A^-1 * y` よりも高速です。 --- # まとめ + Julia の 1次元配列は **ベクトル**(列ベクトル)、2次元配列は **行列** + Julia には行列やベクトルを数式に近い形で定義・演算できる工夫がたくさん + Julia 楽しいよ! --- ## ご清聴ありがとうございました。
{"lang":"ja-JP","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"theme\":\"league\"}","description":"2024/04/26 KCIバータイム\n後藤 俊介","contributors":"[{\"id\":\"80062a4b-8dad-49ac-95bf-848ce0686e9e\",\"add\":14366,\"del\":18740}]","title":"Julia クイズ ~配列編~"}
    499 views