0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FFT で三角関数(sin)を1回しか呼ばない方法

Last updated at Posted at 2020-02-05

FFT では sin/cos 関数を何度もよびださないように、予めテーブルを作ったりしますが、テーブル生成時の三角関数を一回で済ませます。

sin テーブル生成の例

C++
void make_table1(double *table, unsigned int size)
{
  for (unsigned int i = 0; i < size; i++)
    table[i] = sin(M_PI*2 * i/size);
}

を代替します。座標$\left(x,y\right)$ を $\left(1,0\right)$ から $\left(cos45°,sin45°\right)$ まで回転するときの座標が $\left(cos,sin\right)$ なので、それを使います。計算量は減りますが、精度も落ちます。

C++
void make_table2(double *table, unsigned int size)
{
  assert((size >= 4) && (size == (size & -size)));

  unsigned int h_sz = size >> 1;
  unsigned int q_sz = h_sz >> 1;
  unsigned int o_sz = q_sz >> 1;
  unsigned int qhsz = h_sz + q_sz;

  double ds = sin(M_PI / h_sz); // sin 関数は1回だけ
  double dc = sqrt(1 - ds * ds);
  double px = 1, py = 0;

  table[0    + 0   ] =  0;
  table[0    + q_sz] = +1;
  table[h_sz + 0   ] =  0;
  table[h_sz + q_sz] = -1;
  for (unsigned int i = 1; i < o_sz; i++)
    {
      double nx = px * dc - py * ds;
      double ny = px * ds + py * dc;

      if (false)
        { // 少しだけ演算精度がよくなる
          double r = sqrt(nx * nx + ny * ny);
          nx /= r;
          ny /= r;
        }

      table[0    + i] = +ny;
      table[q_sz - i] = +nx;
      table[q_sz + i] = +nx;
      table[h_sz - i] = +ny;
      table[h_sz + i] = -ny;
      table[qhsz - i] = -nx;
      table[qhsz + i] = -nx;
      table[size - i] = -ny;

      px = nx;
      py = ny;
    }

  double ir2 = sqrt(2.0) / 2;

  table[0    + o_sz] = +ir2;
  table[q_sz + o_sz] = +ir2;
  table[h_sz + o_sz] = -ir2;
  table[qhsz + o_sz] = -ir2;
}
テーブル生成テスト プログラム
sample.cpp
# include <cassert>
# include <cstdio>
# include <cmath>

using namespace std;

// 全て sin 関数に頼る
void make_table1(double *table, unsigned int size)
{
  for (unsigned int i = 0; i < size; i++)
    table[i] = sin(M_PI*2 * i/size);
}

void make_table2(double *table, unsigned int size)
{
  assert((size >= 4) && (size == (size & -size)));

  unsigned int h_sz = size >> 1;
  unsigned int q_sz = h_sz >> 1;
  unsigned int o_sz = q_sz >> 1;
  unsigned int qhsz = h_sz + q_sz;

  double ds = sin(M_PI / h_sz); // sin 関数は1回だけ
  double dc = sqrt(1 - ds * ds);
  double px = 1, py = 0;

  table[0    + 0   ] =  0;
  table[0    + q_sz] = +1;
  table[h_sz + 0   ] =  0;
  table[h_sz + q_sz] = -1;
  for (unsigned int i = 1; i < o_sz; i++)
    {
      double nx = px * dc - py * ds;
      double ny = px * ds + py * dc;

      if (false)
        { // 少しだけ演算精度がよくなる
          double r = sqrt(nx * nx + ny * ny);
          nx /= r;
          ny /= r;
        }

      table[0    + i] = +ny;
      table[q_sz - i] = +nx;
      table[q_sz + i] = +nx;
      table[h_sz - i] = +ny;
      table[h_sz + i] = -ny;
      table[qhsz - i] = -nx;
      table[qhsz + i] = -nx;
      table[size - i] = -ny;

      px = nx;
      py = ny;
    }

  double ir2 = sqrt(2.0) / 2;

  table[0    + o_sz] = +ir2;
  table[q_sz + o_sz] = +ir2;
  table[h_sz + o_sz] = -ir2;
  table[qhsz + o_sz] = -ir2;
}

void compare(const double *l, const double *r, unsigned int n)
{
  double dmin = +1;
  double dmax = +0;

  printf("--------------------------------\n");
  for (unsigned int i = 0; i < n; i++)
    {
      double d = abs(l[i] - r[i]);

      printf("%4d/%4d: %+.8e, %+.8e, %+.8e\n", i, n, l[i], r[i], d);

      if (d > dmax) dmax = d;
      if (d < dmin) dmin = d;
    }
  printf("dmin=%e, dmax=%e\n", dmin, dmax);
}

int main(int argc, char **argv)
{
  unsigned int size = (1 << 10);
  double table1[size];
  double table2[size];
  make_table1(table1, size);
  make_table2(table2, size);
  compare(table1, table2, size);
  return 0;
}

誤差は
if(false) の場合: dmin=0.000000e+00, dmax=4.218847e-15
if(true) の場合: dmin=0.000000e+00, dmax=7.771561e-16


配列が $2^n$ ならば、最大の $n$ を決めて sin 値もテーブル化すると、sin 関数の呼び出しはゼロになりますが...

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?