{%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 クイズ ~配列編~"}