spire を使ってみるシリーズ(目次)。 Scala の高速・高精度な数学ライブラリ spire に定義されている、多項式を表す型 Polynomial の基本的な使い方を見ていきます。 今回は Polynomial オブジェクトを生成する、Polynomial コンパニオン・オブジェクトに定義されているファクトリ・メソッドを扱います。
この記事のサンプルコードでは、以下の import 文が書かれているものとします:
import spire.math._ import spire.implicits._
【この記事の内容】
apply ファクトリ・メソッド
まずは apply() メソッドによるファクトリ・メソッドを見ていきます。単項式を生成したい場合は、係数と次数を指定します:
// 単項式 val p0: Polynomial[Double] = Polynomial(2.0, 3) assert( p0.toString == "(2.0x³)" )
1つ目の引数が係数、2つ目の引数が次数(Int 値)です。
多項式を生成したい場合、次数とその係数の組からなる Map を引数として渡します:
// 次数と係数の Map から多項式を生成する val p1: Polynomial[Double] = Polynomial(Map(3 -> 3.0, 1 -> 4.0, 0 -> 5.0)) assert( p1.toString == "(3.0x³ + 4.0x + 5.0)" )
こちらはキーが次数(Int 値)です。 エントリにない次数の項の係数は0となります。
文字列から Polynomial オブジェクトを生成することもできます:
// 文字列から多項式を生成する(係数は有理数 Rational 型) val p2: Polynomial[Rational] = Polynomial("3/2x² + 4x + 5/2") assert( p2.toString == "(3/2x² + 4x + 5/2)" ) // 次数は ^ で書くことも可 val p3: Polynomial[Rational] = Polynomial("5x^3 + 4x^2") assert( p3.toString == "(5x³ + 4x²)" ) // 文字列中の文字は x でなくともよい val p4: Polynomial[Rational] = Polynomial("5t^3 + 4t^2") assert( p4.toString == "(5x³ + 4x²)" ) // toString で呼び出すと x
文字は(おそらく)なんでもかまいません。 次数は上付き数字(² など)もしくは のように ^ を使って書きます。 このメソッドの返り値は Polynomial[Rational] となっていて、係数の型が有理数 spire.math.Rational 型に固定されています。
もう少しテクニカルな(?)方法として、項を表す spire.math.poly Term オブジェクトのコレクションを使って Polynomial オブジェクトを生成することもできます。 Term オブジェクトは Term コンパニオン・オブジェクトに定義されている apply() ファクトリ・メソッドで簡単に生成できます:
import spire.math.poly.Term val terms = (0 to 4).map(i => Term((-1)**i, i)) // 係数と次数を与えて Term オブジェクトを生成 assert( Polynomial(terms).toString == "(x⁴ - x³ + x² - x + 1)" )
コレクションの中に同類項(同じ次数の項)が含まれていれば、それらの項をまとめた多項式が返されます:
val terms = (0 to 4).map(i => Term((-1)**i, i)) val terms2 = Term(3, 2) +: terms assert( Polynomial(terms2).toString == "(x⁴ - x³ + 4x² - x + 1)" ) // 同類項はまとめられる
Scala のコレクションと合わせて上手く使えば便利そうなファクトリ・メソッドですね。
spire の User's Guide によると
import spire.syntax.literals._ poly"3x^2 - 5x + 1" poly"5/4x^6 - 7x - 2" poly"1.2x^3 - 6.1x^2 + 9x - 3.33"
といったコードでリテラルとして Polynomial オブジェクトを生成できるみたいですが、自分の環境(Scala 2.11, 2.12, spire 0.13.0)では上手く動きませんでした。
次数が決まった多項式の生成
次は(Scala コードとしての)定数と、次数の決まった多項式を生成するファクトリ・メソッドを見ていきます。Polynomial コンパニオン・オブジェクトに定義されている定数は以下の4つです(実際にはメソッドですが):
assert( Polynomial.zero[Int].toString == "(0)" ) // 0 assert( Polynomial.one[Int].toString == "(1)" ) // 1 assert( Polynomial.x[Int].toString == "(x)" ) // x assert( Polynomial.twox[Int].toString == "(2x)" ) // 2x
当然ですが、型推論が利く位置で使えば型指定は必要ありません。
数式としての定数(項)、つまり0次式は constant() メソッドで生成できます:
// 定数(項) val c = Polynomial.constant(3) assert( c.toString == "(3)" )
1次式は linear() メソッドで生成します:
// 1次式 val line1 = Polynomial.linear(2) assert( line1.toString == "(2x)" ) val line2 = Polynomial.linear(2, 3) assert( line2.toString == "(2x + 3)" )
引数が1つのものは定数項が0となります。
2次式は quadratic() メソッドで生成します:
// 2次式 val quad0 = Polynomial.quadratic(3) assert( quad0.toString == "(3x²)") val quad1 = Polynomial.quadratic(3, 4) assert( quad1.toString == "(3x + 4)") // ? val quad2 = Polynomial.quadratic(3, 4, 5) assert( quad2.toString == "(3x² + 4x + 5)")
引数が1つと3つのものはいいんですが、2つのものは1次式が返されるようです。 バグの素になりそうなのであまり使わない方がいいかと思います。
3次式は cubic() メソッドで生成します:
// 3次式 val cube0 = Polynomial.cubic(4) assert( cube0.toString == "(4x³)" ) val cube1 = Polynomial.cubic(4, 5, 6, 7) assert( cube1.toString == "(4x³ + 5x² + 6x + 7)" )
まぁ、これは特に問題ないかと思います。
その他のファクトリ・メソッド
dense() メソッド(dense は「密集した、中身が詰まった」の意)は配列によって係数をすべて指定して多項式を生成します:// dense() メソッド val p5 = Polynomial.dense(Array(1, 2, 3, 4)) assert( p5.toString == "(4x³ + 3x² + 2x + 1)" ) // 最後の0は無視される val p5 = Polynomial.dense(Array(1, 2, 3, 4, 0)) assert( p5.toString == "(4x³ + 3x² + 2x + 1)" )
引数として渡した配列の n 番目の要素が n 次の係数になります。 最初(0番目)の要素は定数項ですね。
sparse() メソッド(sparse は「希薄な」の意)は apply ファクトリ・メソッドに Map を渡す場合と同じです:
// sparse() メソッド val p7 = Polynomial.sparse(Map(100 -> 1, 50 -> 2, 0 -> 1)) assert( p7.toString == "(x¹⁰⁰ + 2x⁵⁰ + 1)" )
多くの係数が0の場合に使うと効率が良いのだと思います。
interpolate() メソッド(interpolate は「内挿する」の意)は、グラフ上の点を与えて、グラフがそれらの点を通る多項式を生成します。 n 個の点を与えると n-1 次式が返されます。
// interpolate() メソッド // 2個の点を与えて1次式(直線の式)を生成する val p8 = Polynomial.interpolate((0.0, 1.0), (1.0, 3.0)) assert( p8.toString == "(2.0x + 1.0)" ) // 3個の点を与えて2次式(放物線の式)を生成する val p9 = Polynomial.interpolate((0.0, -1.0), (1.0, -2.0), (3.0, 2.0)) assert( p9.toString == "(x² - 2.0x - 1.0)" )
引数に同じ点を与えると例外が投げられます。
結構いろいろなファクトリ・メソッドが定義されていて使い勝手が良さそうですね。 次回からは Polynomial クラスに定義されているメソッドを見ていきます。
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る