Swift で三次関数の実数解を求めるやつを書いていた。
概要
2018年の10月の頭には見られるようにしてたんだけど忙しさを言い訳にしてブログに書いてなかったので整理した。
まさか三次関数のソルバを自前で実装する必要に迫られるなんて思ってもみなかったよ…。今でもそう思ってる。
詳細
動作が分かりやすいようにサンプルアプリを書いた。
何に使うの。
よくあるアニメーションの EaseInEaseOut
と似たような機能を作る必要があったのだけど、イージング関数として一般的に使われる
- 始点が
(0.0, 0.0)
- 終点が
(1.0, 1.0)
- コントロールポイントが 2 つ
という三次ベジェ曲線で表現することにした。
コントロールポイントの座標 2 つだけを与えれば、そこそこ自由にイージング関数を表現出来るので、なるほどこれは確かに便利だ、という納得感があった。
あったのだが、しかしベジェ曲線の定義をよくよく見返すと媒介変数 t
によって (x, y)
を表示する形になっているので、 t
を消去して y = f(x)
の形に整理しなくてはならず、その過程で t
についての三次関数の解を求める必要があることに気付いてしまう。
一通りライブラリがないか探したもののちょうど良さそうな規模感のものが見当たらず、しょうがないから色々なものを参考にしつつ書いた。
シンプルに三次関数の実数解だけが Double
の配列で返ってきて欲しい。
"swift Polynomial"
などでググると一応見付かるは見付かる。
例えば madbat/SwiftMath に Polynomial.swift などがあって、見た感じ四次関数のソルバまで実装されていたが、残念なことに Swift 2 の時代で止まっていたりした。
また、数学方面の方々に Swift で代数学入門 などの記事を書いておられる人もいて、これはめちゃくちゃ参考になって読んでて楽しかったのだけど、僕が求めているものは (a: Double, b: Double, c: Double, d: Double)
という三次関数の係数 4 つの Double
を引数に渡したら [Double]
の配列が返ってくる func
であって、Swift の綺麗な世界で数学の綺麗さを表現しよう!みたいなものではなくて、もっとこう、泥臭く、現場で使うから必要なんや…みたいな、そういう気持ちが高まってしまって…。
用途的に虚数解は必要がないですし、そもそも浮動小数点での表現ですし。
ちなみに三次ベジェ曲線と三次関数について
A Primer on Bézier Curves に全てが纏まっていた。なんかすごかった。
その文書の 16 - Finding extremities: root finding の章のオマケで三次関数の根をカルダノの公式とビエタの解で求める JavaScript 実装がある。僕が書いたものはこれの Swift 移植です。
実際にカルダノの公式を用いて三次関数を代数的に解こうとすると実数解しか持たない関数だったとしても計算過程で虚数が出てくるが、ビエタの解による幾何学的な手法を用いると計算過程から虚数を排除し三角関数と冪乗だけで解くことが出来る。
そして現代のプログラミング言語で三次関数のソルバを実装する場合でも、虚数を排除して実数の三角関数と冪乗のみで書けた方が楽であることは間違いない。その辺りは標準ライブラリで使える関数として提供されている訳だし。
カルダノ から 450 年、 ビエタ から 400 年も後の時代に生きている我々からすれば、三角関数だろうが複素平面だろうがやってることは変わらないっぽいなと学の無い僕にでもなんとなく理解できる気はする。気がするだけかもしれない。
数学史を垣間見るのも面白いですね。僕も門外不出の解法を使って酒場の計算バトルで負け無しのやべーやつになってみたい人生だった。
締め
CAMediaTimingFunction
あたりに便利メソッドがきっと実装されてるやろ〜とか思ってたけど全然そんなことはなくて、まさか自分で三次関数のソルバを書くことになるとは思わなかったし、職業プログラマをやってるとたまにこういうことがあるんだなという話でした。