倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

spire で多項式 Polynomial を使ってみる (1) : ファクトリ・メソッド

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

文字は(おそらく)なんでもかまいません。 次数は上付き数字(² など)もしくは  { \TeX } のように ^ を使って書きます。 このメソッドの返り値は 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 クラスに定義されているメソッドを見ていきます。

Scalaスケーラブルプログラミング第3版

Scalaスケーラブルプログラミング第3版