Upgrade to Pro — share decks privately, control downloads, hide ads and more …

三分で読める高速フーリエ変換なんちゃって実装

 三分で読める高速フーリエ変換なんちゃって実装

Takatomo Torigoe

April 03, 2014
Tweet

More Decks by Takatomo Torigoe

Other Decks in Programming

Transcript

  1. 実装してみた import breeze.linalg.DenseVector import breeze.math.Complex def fft(f: DenseVector[Complex]): DenseVector[Complex] =

    { require((f.size == 0) || math.pow(2, (math.log(f.size) / math.log(2)).round).round == f.size, "size " + f.size + " must be a power of 2!") f.size match { case 0 => DenseVector() case 1 => f case n => { val even = fft(f(0 to -1 by 2)) val odd = fft(f(1 to -1 by 2)) val w = DenseVector((0 to n / 2 - 1).map(k => (-2 * math.Pi * Complex.i * k / n).exp).toArray) DenseVector.vertcat(even + (odd :* w), even - (odd :* w)) } } } 実質、十行ちょっと。 思ってたよりずっとシンプルでした。 今日は、高速フーリエ変換のアルゴリズム……ではなくて、実装の話をします。
  2. 言語:Scala? http://www.scala-lang.org/ Java - 冗長なおまじない + 関数型 簡潔に安全に汎用的にロジックをそ まま書ける、気がする。 個人的に

    わりと理想 言語。 Java8 ラムダ式とか 、Scalaに影響を受けているみたい。 Java仮想マシンの上で動くため、Javaの資産が使える。 JRuby、Jython、Groovy、Processingなど ファミリー。 由来はScalable language。 親しみ ある単語……。 開発元はTypesafe社。 素敵な名前! Twitterがヘビーユーザー。他にもTumblrとかFoursquareとか…… https://github.com/twitter/ http://twitter.github.io/
  3. ライブラリ:breeze? http://www.scalanlp.org/ MATLABや、PythonのNumPy、のScala版ライブラリ+αと思ってよさそう。 開発者曰く、Scala is "Usually within 10% of Java

    for ~1/2 the code." 開発者曰く、Scala is "Usually 20-30x faster than Python, for ± the same code." http://www.scalanlp.org/breeze-intro-banlp.pptx これを土台として自然言語処理とか機械学習をしたいっぽい。 今回は二つのクラスを利用するだけ。 import breeze.linalg.DenseVector import breeze.math.Complex
  4. データ構 :breeze.linalg.DenseVector? https://github.com/scalanlp/breeze/wiki/Breeze-Linear-Algebra ベクトルの要素ごとの演算がループ文なしで書ける。 とても便利。標準ライブラリに欲しいくらい。 ちなみにC++で 、std::valarrayというコンテナを使え 良いらしい。 Sparse Vector/Matrix(疎ベクトル/行列:成分のほとんどがゼロ)の対義語。

    sliceした時に、データをコピーせず生配列を共有するため、メモリ食わない。 高速フーリエ変換 ような分割統治法 アルゴリズム向き。 フィールド 以下 通り。dv(n) == dv.data(offset + stride * n)となる。 val data: Array[E], // 生配列 val offset: Int, val stride: Int, val length: Int
  5. コードふたたび import breeze.linalg.DenseVector import breeze.math.Complex def fft(f: DenseVector[Complex]): DenseVector[Complex] =

    { require((f.size == 0) || math.pow(2, (math.log(f.size) / math.log(2)).round).round == f.size, "size " + f.size + " must be a power of 2!") f.size match { case 0 => DenseVector() case 1 => f case n => { val even = fft(f(0 to -1 by 2)) val odd = fft(f(1 to -1 by 2)) val w = DenseVector((0 to n / 2 - 1).map(k => (-2 * math.Pi * Complex.i * k / n).exp).toArray) DenseVector.vertcat(even + (odd :* w), even - (odd :* w)) } } }
  6. コメント付けてみた def fft(f: DenseVector[Complex]): DenseVector[Complex] = { f.size match {

    case 0 => DenseVector() case 1 => f case n => { // 偶数番目の要素をsliceして再帰 val even = fft(f(0 to -1 by 2)) // 奇数番目の要素をsliceして再帰 val odd = fft(f(1 to -1 by 2)) // 1のn乗根ベクトル val w = DenseVector((0 to n / 2 - 1).map(k => (-2 * math.Pi * Complex.i * k / n).exp).toArray) // + 要素ごとに足し算 // - 要素ごとに引き算 // :* 要素ごとに掛け算(≠内積) // vercat ベクトルの連結 DenseVector.vertcat(even + (odd :* w), even - (odd :* w)) } } }
  7. 元ネタ:Rosetta Code? http://rosettacode.org/wiki/Fast_Fourier_transform 各種アルゴリズムの多言語サンプルコードWiki。 高速フーリエ変換のサンプルコードは、36言語。 Ada, ALGOL 68, BBC BASIC,

    C, C++, D, Factor, Fortran, GAP, Go, Haskell, J, Javascript, Julia, Liberty BASIC, Maple, Mathematica, MATLAB / Octave, Maxima, OCaml, PARI/GP, Perl, Perl 6, PicoLisp, PL/I, Prolog, Python, R, Racket, REXX, Ruby, Run BASIC, Scala, Scilab, Tcl, Ursala 高速フーリエ変換のScala版サンプルコードは、 可変コレクション、手続き型的なfor文、無駄な無限リスト、あたりが微妙……。 リファクタリングかけて、breeze使って、 アルゴリズムをそ まま読めるようにした が今回 コード。
  8. ふゅーちゃーこーど def fftPar(f: DenseVector[Complex], // 最初に計算した1のn乗根ベクトルを使いまわしたい w_ :Option[DenseVector[Complex]] = None

    ): DenseVector[Complex] = { f.size match { case 0 => DenseVector() case 1 => f // 一定サイズ以下は並列化しない方が速いかもしれない case n if n < 2 => fft(f) case _ => { // 1のn乗根ベクトルがまだ計算されていなかったら計算 val w = w_.getOrElse(DenseVector((0 to f.size / 2 - 1).map(k => (-2 * math.Pi * Complex.i * k / f.size).exp).toArray)) // ここでFutureを使ってみる // even_とodd_の型はFuture[DenseVector[Complex]] val even_ = Future{ fftPar(f(0 to -1 by 2), Some(w(0 to -1 by 2))) } val odd_ = Future{ fftPar(f(1 to -1 by 2), Some(w(0 to -1 by 2))) } // even_とodd_の計算結果が得られたらコールバックされる処理 val result_ = for (even <- even_; odd <- odd_) yield DenseVector.vertcat(even + (odd :* w), even - (odd :* w)) // result_の計算結果が得られるまでブロック // もっと良い方法があるかも Await.result(result_ , Duration.Inf) } } } とりあえず動いてる。 パフォーマンス測定しようとしたところで時間切れ。