JavaScript DFT 离散傅里叶变换

来源:互联网 发布:淘宝可以买限定皮肤吗 编辑:程序博客网 时间:2024/05/22 21:24

关于 离散傅里叶变换 DFT 以及 快速傅里叶变换 FFT 的算法可以在 算法导论第30章 或者 其他人的博客 里看到。这里不做赘述。

我们来看一种函数式的实现:

依赖

复数类

首先,建立一个复数方法类:

// complex methods// 构造函数,默认为 0complex = (x, y) => { return { x: x || 0, y: y || 0 }; }; // 从弧度构造单位复数complex.fromAngle = (angle) => complex(Math.cos(angle), Math.sin(angle));// 复数加复数complex.add = (a, b) => complex(a.x + b.x, a.y + b.y);// 复数乘复数complex.mul = (a, b) => complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);// 数乘复数complex.numMul = (n, c) => complex(n * c.x, n * c.y);// 复数减复数complex.minus = (a, b) => complex(a.x - b.x, a.y - b.y);

注意,这个复数乘复数与复数减复数是冗余的:

complex.numMul = (n, c) => complex.mul(complex(n), c);complex.minus = (a, b) => complex.add(a, complex.numMul(-1, b));

列表重构

关于列表重构,可以参考我的另一篇博文:JavaScript 列表重构

// array restructuringflatten = (matrix) => matrix.reduce((pre, cur) => pre.concat(cur), []);partition = (array, n) => array.reduce((pre, cur, i) => (pre[i % n] = pre[i % n] || []).push(cur) && pre, []);transpose = (matrix) => partition(flatten(matrix), matrix[0].length);

DFT & DFT的逆

这里先很不要脸地给出一行形式的代码

recursiveDFT = (a, inverse) => a.length == 1 ? a : flatten(transpose(transpose(a.reduce((pre, cur, i) => pre[i & 1].push(cur) && pre, [[], []]).map(v => recursiveDFT(v))).map((v, i) => [complex.add(v[0], complex.mul(complex.fromAngle(i * (inverse ? -2 * Math.PI / a.length : 2 * Math.PI / a.length)), v[1])), complex.minus(v[0], complex.mul(complex.fromAngle(i * (inverse ? -2 * Math.PI / a.length : 2 * Math.PI / a.length)), v[1]))]))).map(v => inverse ? complex.numMul(1 / a.length, v) : v);

传参

目前这个函数尚不类型安全,必须要有严格的传参方式才能保证不出错。

第一个参数是一个数组:

  • 其中每个元素都要有 x 与 y 这两个属性。
  • 数组的长度是2的幂(1, 2, 4, 8, 16, …)

第二个参数是可判定真假的值,由于存在强制转换因此不会有问题。
当第二个参数为true时,执行DFT的逆,默认为false。

测试

由于使用了ES6的语法,浏览器的支持度查询:Arrow Functions Supported

如果你的浏览器支持,你可以尽情测试:

var x = [complex(1), complex(2), complex(0), complex(0)]; // 1 + 2xvar y = [complex(3), complex(4), complex(0), complex(0)]; // 3 + 4xvar rx = recursiveDFT(x);recursiveDFT(rx, true); // almost same as xvar ry = recursiveDFT(y); recursiveDFT(ry, true); // almost same as yvar rc = rx.map((v, i) => complex.mul(v, ry[i]));recursiveDFT(rc, true); // almost same as [complex(3), complex(10), complex(8), complex(0)]

感兴趣的读者可以自行将那一行代码婊开看逻辑。

虽然我定义了几个函数(complex方法类、restructuring方法类)来处理这个问题,但是不难发现这些函数依然可以inline化组合到一起,写出一个不带分号的 One Line DFT。

0 0
原创粉丝点击