前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >经典傅里叶算法小集合 附完整c代码

经典傅里叶算法小集合 附完整c代码

原创
作者头像
cpuimage
发布2018-08-09 23:45:40
9050
发布2018-08-09 23:45:40
举报
文章被收录于专栏:算法+算法+

前面写过关于傅里叶算法的应用例子。

基于傅里叶变换的音频重采样算法 (附完整c代码)

当然也就是举个例子,主要是学习傅里叶变换。

这个重采样思路还有点瑕疵,

稍微改一下,就可以支持多通道,以及提升性能。

当然思路很简单,就是切分,合并。

留个作业哈。

本文不讲过多的算法思路,傅里叶变换的各种变种,

绝大多数是为提升性能,支持任意长度而作。

当然各有所长,

当时提到参阅整理的算法:

https://github.com/cpuimage/StockhamFFT

https://github.com/cpuimage/uFFT

https://github.com/cpuimage/BluesteinCrz

https://github.com/cpuimage/fftw3

例如 : 

Stockham 是优化速度, 

BluesteinCrz 是支持任意长度,

uFFT是经典实现。

当然,各有利弊,精度也不一。

最近一直对傅里叶算法没放手。

还是想要抽点时间,不依赖第三方库,实现一份不差于fftw的算法,

既要保证精度,又要保证性能,同时还要支持任意长度。

目前还在进行中,目前项目完成了45%左右。

越是学习,看的资料林林总总,越觉得傅里叶变换的应用面很广。

花点时间,采用纯c ,实现了经典的傅里叶算法,

调整代码逻辑,慢慢开始有点清晰了。

前人栽树后人乘凉,为了学习方便,

把本人用纯c实现的经典傅里叶算法开源出来给大家学习。

算法逻辑写得简洁明了,我也是尽力了。

当然,可能还有更好的实现思路,更多的改进算法。

不过,我的目的更多是便于学习和理解算法。

希望能帮助到一些也在学习傅里叶变换算法的同学。

贴上完整算法代码:

代码语言:javascript
复制
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif

typedef struct {
    float real, imag;
} cmplx;

cmplx cmplx_mul_add(const cmplx c, const cmplx a, const cmplx b) {
    const cmplx ret = {
            (a.real * b.real) + c.real - (a.imag * b.imag),
            (a.imag * b.real) + (a.real * b.imag) + c.imag
    };
    return ret;
}

void fft_Stockham(cmplx *input, cmplx *output, size_t n, int flag) {
    size_t half = n >> 1;
    cmplx *tmp = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *y = (cmplx *) calloc(sizeof(cmplx), n);
    memcpy(y, input, sizeof(cmplx) * n);
    for (size_t r = half, l = 1; r >= 1; r >>= 1) {
        cmplx *tp = y;
        y = tmp;
        tmp = tp;
        float factor_w = -flag * M_PI / l;
        cmplx w = {cosf(factor_w), sinf(factor_w)};
        cmplx wj = {1, 0};
        for (size_t j = 0; j < l; j++) {
            size_t jrs = j * (r << 1);
            for (size_t k = jrs, m = jrs >> 1; k < jrs + r; k++) {
                const cmplx t = {(wj.real * tmp[k + r].real) - (wj.imag * tmp[k + r].imag),
                                 (wj.imag * tmp[k + r].real) + (wj.real * tmp[k + r].imag)};
                y[m].real = tmp[k].real + t.real;
                y[m].imag = tmp[k].imag + t.imag;
                y[m + half].real = tmp[k].real - t.real;
                y[m + half].imag = tmp[k].imag - t.imag;
                m++;
            }
            const float t = wj.real;
            wj.real = (t * w.real) - (wj.imag * w.imag);
            wj.imag = (wj.imag * w.real) + (t * w.imag);
        }
        l <<= 1;
    }
    memcpy(output, y, sizeof(cmplx) * n);
    free(tmp);
    free(y);
}

void fft_radix3(cmplx *in, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, in, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 3;
    size_t np = n / radix;
    cmplx *res = (cmplx *) malloc(sizeof(cmplx) * n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = in[radix * i + j];
        }
    }
    fft_radix3(f0, f0, np, flag);
    fft_radix3(f1, f1, np, flag);
    fft_radix3(f2, f2, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], f2[k], wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix5(cmplx *x, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, x, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 5;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = x[radix * i + j];
        }
    }
    fft_radix5(f0, f0, np, flag);
    fft_radix5(f1, f1, np, flag);
    fft_radix5(f2, f2, np, flag);
    fft_radix5(f3, f3, np, flag);
    fft_radix5(f4, f4, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k], f4[k],
                                                                                                       wj), wj), wj),
                                               wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix6(cmplx *input, cmplx *output, size_t n, int flag) {
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 6;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    cmplx *f5 = f4 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = input[radix * i + j];
        }
    }
    fft_radix6(f0, f0, np, flag);
    fft_radix6(f1, f1, np, flag);
    fft_radix6(f2, f2, np, flag);
    fft_radix6(f3, f3, np, flag);
    fft_radix6(f4, f4, np, flag);
    fft_radix6(f5, f5, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            output[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k],
                                                                                                       cmplx_mul_add(
                                                                                                               f4[k],
                                                                                                               f5[k],
                                                                                                               wj), wj),
                                                                                         wj), wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_radix7(cmplx *x, cmplx *result, size_t n, int flag) {
    if (n < 2) {
        memcpy(result, x, sizeof(cmplx) * n);
        return;
    }
    size_t radix = 7;
    size_t np = n / radix;
    cmplx *res = (cmplx *) calloc(sizeof(cmplx), n);
    cmplx *f0 = res;
    cmplx *f1 = f0 + np;
    cmplx *f2 = f1 + np;
    cmplx *f3 = f2 + np;
    cmplx *f4 = f3 + np;
    cmplx *f5 = f4 + np;
    cmplx *f6 = f5 + np;
    for (size_t i = 0; i < np; i++) {
        for (size_t j = 0; j < radix; j++) {
            res[i + j * np] = x[radix * i + j];
        }
    }
    fft_radix7(f0, f0, np, flag);
    fft_radix7(f1, f1, np, flag);
    fft_radix7(f2, f2, np, flag);
    fft_radix7(f3, f3, np, flag);
    fft_radix7(f4, f4, np, flag);
    fft_radix7(f5, f5, np, flag);
    fft_radix7(f6, f6, np, flag);
    float wexp0 = -2 * (float) M_PI * (flag) / (float) (n);
    cmplx wt = {cosf(wexp0), sinf(wexp0)};
    cmplx w0 = {1, 0};
    for (size_t i = 0; i < np; i++) {
        const float w0r = w0.real;
        w0.real = (w0r * wt.real) - (w0.imag * wt.imag);
        w0.imag = (w0.imag * wt.real) + (w0r * wt.imag);
    }
    cmplx w = {1, 0};
    for (size_t j = 0; j < radix; j++) {
        cmplx wj = w;
        for (size_t k = 0; k < np; k++) {
            result[k + j * np] = cmplx_mul_add(f0[k], cmplx_mul_add(f1[k], cmplx_mul_add(f2[k],
                                                                                         cmplx_mul_add(f3[k],
                                                                                                       cmplx_mul_add(
                                                                                                               f4[k],
                                                                                                               cmplx_mul_add(
                                                                                                                       f5[k],
                                                                                                                       f6[k],
                                                                                                                       wj),
                                                                                                               wj), wj),
                                                                                         wj), wj), wj);
            const float wjr = wj.real;
            wj.real = (wjr * wt.real) - (wj.imag * wt.imag);
            wj.imag = (wj.imag * wt.real) + (wjr * wt.imag);
        }
        const float wr = w.real;
        w.real = (wr * w0.real) - (w.imag * w0.imag);
        w.imag = (w.imag * w0.real) + (wr * w0.imag);
    }
    free(res);
}

void fft_Bluestein(cmplx *input, cmplx *output, size_t n, int flag) {
    size_t m = 1 << ((unsigned int) (ilogbf((float) (2 * n - 1))));
    if (m < 2 * n - 1) {
        m <<= 1;
    }
    cmplx *y = (cmplx *) calloc(sizeof(cmplx), 3 * m);
    cmplx *w = y + m;
    cmplx *ww = w + m;
    float a0 = (float) M_PI / n;
    w[0].real = 1;
    if (flag == -1) {
        y[0].real = input[0].real;
        y[0].imag = -input[0].imag;
        for (size_t i = 1; i < n; i++) {
            const float wexp = a0 * i * i;
            w[i].real = cosf(wexp);
            w[i].imag = sinf(wexp);
            w[m - i] = w[i];
            y[i].real = (input[i].real * w[i].real) - (input[i].imag * w[i].imag);
            y[i].imag = (-input[i].imag * w[i].real) - (input[i].real * w[i].imag);
        }
    } else {
        y[0].real = input[0].real;
        y[0].imag = input[0].imag;
        for (size_t i = 1; i < n; i++) {
            const float wexp = a0 * i * i;
            w[i].real = cosf(wexp);
            w[i].imag = sinf(wexp);
            w[m - i] = w[i];
            y[i].real = (input[i].real * w[i].real) + (input[i].imag * w[i].imag);
            y[i].imag = (input[i].imag * w[i].real) - (input[i].real * w[i].imag);
        }
    }
    fft_Stockham(y, y, m, 1);
    fft_Stockham(w, ww, m, 1);
    for (size_t i = 0; i < m; i++) {
        const float r = y[i].real;
        y[i].real = (r * ww[i].real) - (y[i].imag * ww[i].imag);
        y[i].imag = (y[i].imag * ww[i].real) + (r * ww[i].imag);
    }
    fft_Stockham(y, y, m, -1);
    float scale = 1.0f / m;
    if (flag == -1) {
        for (size_t i = 0; i < n; i++) {
            output[i].real = ((y[i].real * w[i].real) + (y[i].imag * w[i].imag)) * scale;
            output[i].imag = -((y[i].imag * w[i].real) - (y[i].real * w[i].imag)) * scale;
        }
    } else {
        for (size_t i = 0; i < n; i++) {
            output[i].real = ((y[i].real * w[i].real) + (y[i].imag * w[i].imag)) * scale;
            output[i].imag = ((y[i].imag * w[i].real) - (y[i].real * w[i].imag)) * scale;
        }
    }
    free(y);
}

size_t base(size_t n) {
    size_t t = n & (n - 1);
    if (t == 0) {
        return 2;
    }
    for (size_t i = 3; i <= 7; i++) {
        size_t n2 = n;
        while (n2 % i == 0) {
            n2 /= i;
        }
        if (n2 == 1) {
            return i;
        }
    }
    return n;
}

void FFT(cmplx *input, cmplx *output, size_t n) {
    memset(output, 0, sizeof(cmplx) * n);
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t p = base(n);
    switch (p) {
        case 2:
            fft_Stockham(input, output, n, 1);
            break;
        case 3:
            fft_radix3(input, output, n, 1);
            break;
        case 5:
            fft_radix5(input, output, n, 1);
            break;
        case 6:
            fft_radix6(input, output, n, 1);
            break;
        case 7:
            fft_radix7(input, output, n, 1);
            break;
        default:
            fft_Bluestein(input, output, n, 1);
            break;
    }
}

void IFFT(cmplx *input, cmplx *output, size_t n) {
    memset(output, 0, sizeof(cmplx) * n);
    if (n < 2) {
        memcpy(output, input, sizeof(cmplx) * n);
        return;
    }
    size_t p = base(n);
    switch (p) {
        case 2:
            fft_Stockham(input, output, n, -1);
            break;
        case 3:
            fft_radix3(input, output, n, -1);
            break;
        case 5:
            fft_radix5(input, output, n, -1);
            break;
        case 6:
            fft_radix6(input, output, n, -1);
            break;
        case 7:
            fft_radix7(input, output, n, -1);
            break;
        default: {
            fft_Bluestein(input, output, n, -1);
            break;
        }
    }
    float scale = 1.0f / n;
    for (size_t i = 0; i < n; i++) {
        output[i].real = output[i].real * scale;
        output[i].imag = output[i].imag * scale;
    }
}

int main() {
    printf("Fast Fourier Transform\n");
    printf("blog: http://cpuimage.cnblogs.com/\n");
    printf("A Simple and Efficient FFT Implementation in C");
    size_t N = 513;
    cmplx *input = (cmplx *) calloc(sizeof(cmplx), N);
    cmplx *output = (cmplx *) calloc(sizeof(cmplx), N);
    for (size_t i = 0; i < N; ++i) {
        input[i].real = i;
        input[i].imag = 0;
    }
    for (size_t i = 0; i < N; ++i) {
        printf("(%f %f) \t", input[i].real, input[i].imag);
    }
    for (int i = 0; i < 100; i++) {
        FFT(input, output, N);
    }
    printf("\n");
    IFFT(output, input, N);
    for (size_t i = 0; i < N; ++i) {
        printf("(%f %f) \t", input[i].real, input[i].imag);
    }
    free(input);
    free(output);
    getchar();
    return 0;
}

项目地址:

https://github.com/cpuimage/cpuFFT

想了好久都没想到取啥名字好,最后还是选择了cpu这个前缀。

以上,权当抛砖引玉。

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是:  gaozhihan@vip.qq.com

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档