{ 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)) } } } 実質、十行ちょっと。 思ってたよりずっとシンプルでした。 今日は、高速フーリエ変換のアルゴリズム……ではなくて、実装の話をします。
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
{ 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)) } } }
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)) } } }
): 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) } } } とりあえず動いてる。 パフォーマンス測定しようとしたところで時間切れ。