コードを書けば複素数がわかる

55
コードを書けば複素数がわかる @taketo1024 情報科学若手の会冬の陣 2015

Upload: taketo-sano

Post on 17-Jul-2015

1.381 views

Category:

Science


0 download

TRANSCRIPT

Page 1: コードを書けば複素数がわかる

コードを書けば複素数がわかる

@taketo1024

情報科学若手の会冬の陣 2015

Page 2: コードを書けば複素数がわかる

i2 = �1

Page 3: コードを書けば複素数がわかる

学校で習うとき1. 虚数単位 i = √-1 がいきなり出てくる

2. 複素数 z = a + bi の四則演算が定義される

3. 「数」と思っていた z がベクトルになってる

4. 「i をかけるのは90度回転です」などと教わる

5. 以後当たり前のように電流や波の方程式に出てくる

i =p�1

z = a+ bi

z

i

Page 4: コードを書けば複素数がわかる

ちょっと待ってくれ… i は「想像上の数」なんだろ…?i

Page 5: コードを書けば複素数がわかる

簡単なコードで i は 作れる!

i

Page 6: コードを書けば複素数がわかる

方針

1. 複素数を最初から2次元ベクトルとして定義する。

2. i^2 = -1 となるように 掛け算を入れる。

3. これが実数を拡大した「数」になることを確認。

i2 = �1

Page 7: コードを書けば複素数がわかる

1. 複素数を作ろう

Page 8: コードを書けば複素数がわかる

まず2次元ベクトルから出発

struct Complex { let x: Double let y: Double init(_ x: Double, _ y: Double) { self.x = x self.y = y } }

言語:Swift

Page 9: コードを書けば複素数がわかる

let z = Complex(2, 3)

x

y

2

z =

✓23

◆3

Page 10: コードを書けば複素数がわかる

足し算・引き算・実数倍を定義func + (z: Complex, w: Complex) -> Complex { return Complex(z.x + w.x, z.y + w.y) }

prefix func -(z: Complex) -> Complex { return Complex(-z.x, -z.y); }

func - (z: Complex, z: Complex) -> Complex { return Complex(z.x - w.x, z.y - w.y) }

func * (a: Double, z: Complex) -> Complex { return Complex(a * z.x, a * z.y) }

演算子のオーバーライド

Page 11: コードを書けば複素数がわかる

イコールを定義

struct Complex: Equatable { … }

func == (a: Complex, b: Complex) -> Bool { return (a.x == b.x) && (a.y == b.y) }

プロトコル(Java のインターフェース)

Page 12: コードを書けば複素数がわかる

Complex(2, 3) + Complex(1, -1) == Complex(3, 2)

x

y

Page 13: コードを書けば複素数がわかる

次に1 = (1, 0), i = (0, 1) として、

z = x + yi の形で書けるようにする。

1 =

✓10

◆i =

✓01

z = a+ bi

Page 14: コードを書けば複素数がわかる

Int, Double から Complex を生成

struct Complex: …, IntegerLiteralConvertible, FloatLiteralConvertible { … init(integerLiteral x: IntegerLiteralType) { self.x = Double(x) self.y = 0 } init(floatLiteral x: FloatLiteralType) { self.x = x self.y = 0 } }

リテラル限定の静的キャスト(Swift特有?)

Page 15: コードを書けば複素数がわかる

これで実数を複素数に「埋め込んだ」ことになる。

x

y

1 =

✓10

1 == Complex(1, 0) // true

Page 16: コードを書けば複素数がわかる

あとは 定数 i を定義すれば…

let i = Complex(0, 1)

i

x

y

i =

✓01

Page 17: コードを書けば複素数がわかる

let z = 2 + 3 * i

x

y

求めていた表現を得る!

2 =

✓20

3i =

✓03

◆z =

✓23

◆= 2 + 3i

Page 18: コードを書けば複素数がわかる

さて、いよいよ掛け算の定義。

Page 19: コードを書けば複素数がわかる

複素数の掛け算は、 実数と同じ計算規則を満たし、かつ、

となるように定義したい。

i2 = �1

Page 20: コードを書けば複素数がわかる

z = a + bi, w = c + di として、積 zw は、z = a+ bi, w = c+ di

となる。 特に a = c = 0, b = d = 1 の場合が i^2 = -1 の式。

zw

zw = (a+ bi)(c+ di)

= a(c+ di) + bi(c+ di)分配法則

= ac+ adi+ bci+ bdi2

= ac+ adi+ bci� bd

分配法則

= ac� bd+ adi+ bci交換法則

= (ac� bd) + (ad+ bc)i分配法則

i2 = �1

i2 = �1

Page 21: コードを書けば複素数がわかる

先の式で掛け算を定義してみると…

struct Complex { … }

func * (z: Complex, w: Complex) -> Complex { return Complex(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x) }

Page 22: コードを書けば複素数がわかる

ちゃんと「掛け算の要件」を満たしている!

let α = 3 + 5 * i let β = -1 + 4 * i let γ = 4 - 7 * i

α * 1 == α // 1は単位元 α * β == β * α // 交換法則 (α * β) * γ == α * (β * γ) // 結合法則 α * (β + γ) == α * β + α * γ // 分配法則

当たり前に見えるが、テキトーに定義したのではこうならない。 これで安心して「数」として扱えるようになる。

Page 23: コードを書けば複素数がわかる

i * i == -1 // true

できました!

Page 24: コードを書けば複素数がわかる

割り算は?

Page 25: コードを書けば複素数がわかる

zw = (ac� bd) + (ad+ bc)i = 1 + 0i

割り算は「逆数との積」なので、z の逆数 w は、

これを解けば、

w =1

a2 + b2(a� bi)

,(ac� bd = 1

ad+ bc = 0

z w

Page 26: コードを書けば複素数がわかる

「逆数との積」で割り算を定義

struct Complex { … }

func / (z: Complex, w: Complex) -> Complex { let w_inv = (1 / (w.x * w.x + w.y * w.y) ) * Complex(w.x, -w.y) return z * w_inv }

Page 27: コードを書けば複素数がわかる

複素数、できました!struct Complex: Equatable, IntegerLiteralConvertible, FloatLiteralConvertible { let x: Double let y: Double init(_ x: Double, _ y: Double) { self.x = x self.y = y } init(_ x: Double) { self.x = x self.y = 0 } init(integerLiteral x: IntegerLiteralType) { self.x = Double(x) self.y = 0 } init(floatLiteral x: FloatLiteralType) { self.x = x self.y = 0 } }

func == (z: Complex, w: Complex) -> Bool { return z.x == w.x && z.y == w.y }

func + (z: Complex, w: Complex) -> Complex { return Complex(z.x + w.x, z.y + w.y) }

func - (z: Complex, w: Complex) -> Complex { return Complex(z.x - w.x, z.y - w.y) }

prefix func -(z: Complex) -> Complex { return Complex(-z.x, -z.y); }

func * (a: Double, z: Complex) -> Complex { return Complex(a * z.x, a * z.y) }

func * (z: Complex, w: Complex) -> Complex { return Complex(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x) }

func / (z: Complex, w: Complex) -> Complex { let w_inv = Complex(w.x / (w.x * w.x + w.y * w.y), -w.y / (w.x * w.x + w.y * w.y)) return z * w_inv }

Page 28: コードを書けば複素数がわかる

この掛け算こそが複素数の本質struct Complex: Equatable, IntegerLiteralConvertible, FloatLiteralConvertible { let x: Double let y: Double init(_ x: Double, _ y: Double) { self.x = x self.y = y } init(_ x: Double) { self.x = x self.y = 0 } init(integerLiteral x: IntegerLiteralType) { self.x = Double(x) self.y = 0 } init(floatLiteral x: FloatLiteralType) { self.x = x self.y = 0 } }

func == (z: Complex, w: Complex) -> Bool { return z.x == w.x && z.y == w.y }

func + (z: Complex, w: Complex) -> Complex { return Complex(z.x + w.x, z.y + w.y) }

func - (z: Complex, w: Complex) -> Complex { return Complex(z.x - w.x, z.y - w.y) }

prefix func -(z: Complex) -> Complex { return Complex(-z.x, -z.y); }

func * (a: Double, z: Complex) -> Complex { return Complex(a * z.x, a * z.y) }

func * (z: Complex, w: Complex) -> Complex { return Complex(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x) }

func / (z: Complex, w: Complex) -> Complex { let w_inv = Complex(w.x / (w.x * w.x + w.y * w.y), -w.y / (w.x * w.x + w.y * w.y)) return z * w_inv }

func * (z: Complex, w: Complex) -> Complex { return Complex(z.x * w.x - z.y * w.y, z.x * w.y + z.y * w.x) }

Page 29: コードを書けば複素数がわかる

2. 複素数を動かしてみよう

Page 30: コードを書けば複素数がわかる

class ComplexPlane : UIView {

var unit: CGFloat = 50.0 var scale: CGFloat = 1.0 var points: [String: Complex] = [:] var colors: [String: UIColor] = [:] override func drawRect(rect: CGRect) { let ctx = UIGraphicsGetCurrentContext() let centerX = self.bounds.width / 2 let centerY = self.bounds.height / 2 // fill background CGContextSetFillColorWithColor(ctx, UIColor.whiteColor().CGColor) CGContextFillRect(ctx, self.bounds) // draw axises … } }

複素平面のViewクラス

Page 31: コードを書けば複素数がわかる

let cplane = ComplexPlane(frame: …)

cplane["1"] = 1 cplane["i"] = i

let z = Complex(r: 2, θ: M_PI / 3) cplane["z"] = z

let w = z * z cplane["w"] = w

Page 32: コードを書けば複素数がわかる

DEMO : 動かしてみよう!

Page 33: コードを書けば複素数がわかる

z と w の関係は?z w

Page 34: コードを書けば複素数がわかる

Re

Im

複素数 z は絶対値 r = |z|, 偏角 θ= arg(z) を用いて、 z = r(cosθ + i sin θ) と書ける(極表示)

r

r = |z| ✓ = arg(z)z

z = r(cos✓ + isin✓)

z = r(cos✓ + isin✓)

rcos✓

irsin✓

Page 35: コードを書けば複素数がわかる

複素数の掛け算を極表示で書き直してみると…

Page 36: コードを書けば複素数がわかる

z = r(cos✓ + isin✓), w = s(cos�+ isin�)

zw = rs{(cos✓cos�� sin✓sin�) + i(sin✓cos�+ cos✓sin�)}

= rs(cos(✓ + �) + isin(✓ + �))

に対して、

つまり…、

加法定理

Page 37: コードを書けば複素数がわかる

Re

Im

r

sz

w

zw

rs

= rs(cos(✓ + �) + isin(✓ + �))zw

複素数の掛け算は「絶対値の積」×「偏角の和」だった!

Page 38: コードを書けば複素数がわかる

Re

Im

特に i^2 = -1 は、 「90°回転を2回すれば180°回転」

i2 = �1

i90°

i2 = �1

Page 39: コードを書けば複素数がわかる

3. なぜ複素数?

Page 40: コードを書けば複素数がわかる

そもそもこれはどこから出て来た?

i2 = �1

Page 41: コードを書けば複素数がわかる

(例) 方程式: x^2 + x + 1 = 0

-10 -7.5 -5 -2.5 0 2.5 5 7.5 10

-5

-2.5

2.5

5

x

2 + x+ 1 = 0

この方程式は実数の範囲では解を持たない。

y = x

2 + x+ 1

Page 42: コードを書けば複素数がわかる

形式的に2方程式の解の公式を使うと、

ここで √-3 を √3 i と置き換えて:

は、x^2 + x + 1 = 0 の解になっている。

p�3

p3i

x

2 + x+ 1 = 0

x =�1±

p12 � 4 · 1 · 12

=�1±

p�3

2

↵ =�1 +

p3i

2,� =

�1�p3i

2

Page 43: コードを書けば複素数がわかる

-10 -7.5 -5 -2.5 0 2.5 5 7.5 10

-5

-2.5

2.5

5

y = x

2 + x+ 1

y = x^2 + x + 1 は x = α, β で x 軸と交わっている…?y = x

2 + x+ 1 x = ↵,�

↵? �?

x, y を複素数と見てグラフを描くには、 残念ながら我々の世界では次元が1つ足りない。

Page 44: コードを書けば複素数がわかる

Re

Im

Re

Im

zw

w = f(z) = z^2 + z + 1 を平面から平面への写像と見て、 z の動きにあわせて w がどう動くか見てみる。z w

w = f(z) = z2 + z + 1

代わりに、

f

Page 45: コードを書けば複素数がわかる

DEMO

w = f(z) = z2 + z + 1

Page 46: コードを書けば複素数がわかる

Re

Im

Re

Imz w

z を半径を大きくしながら円上で動かす。z

Page 47: コードを書けば複素数がわかる

Re

Im

Re

Imz w

半径 1 のときに w = 0 となる点が2つある。w = 0

Page 48: コードを書けば複素数がわかる

Re

Im

Re

Im

z w

この2点が α, β で、f によって 0 に写されていた!↵,� f

↵ =�1 +

p3i

2,� =

�1�p3i

2

Page 49: コードを書けば複素数がわかる

Re

Im

Re

Imz w

したがって…

一般の n 次式の場合も同様に、 z を 0 から大きくしていけば、w は必ず 0 を通る

Page 50: コードを書けば複素数がわかる

代数学の基本定理

複素数の範囲では必ず解を持つ!

anzn + ...+ a1z + a0 = 0n 次方程式 は、

Page 51: コードを書けば複素数がわかる

コードを書いて自分で動かしてみれば、 数学はグッと身近になる!

Page 52: コードを書けば複素数がわかる
Page 53: コードを書けば複素数がわかる

「SwiftComplex」github で公開してます:

Page 54: コードを書けば複素数がわかる

1/30(金) 「第1回 プログラマのための数学勉強会」開催!

もう満席ですが、次も近いうちやります!発表者募集中!

Page 55: コードを書けば複素数がわかる

Thanks!@taketo1024