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

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

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

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

Avatar for Takatomo Torigoe

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) } } } とりあえず動いてる。 パフォーマンス測定しようとしたところで時間切れ。