LINUX SB 快照站

不依赖任何数学知识的“慢傅里叶变换”算法

原帖: linux.sb/topic/10530 · 共 6 楼 · 标题快照 2026-08-12 10:40:27

楼主
发帖 2026-08-10 12:16:57 · 快照 2026-08-12 10:40:27

slowFT

第一次实现了“不用任何数学知识,编程初学者都可以写的傅里叶变换”。

基于相减能量最低法和三分法的慢速傅里叶变换。

这是一个“暴力搜索”傅里叶变换算法,它的核心思想极其简单:对每一个可能的频率,通过反复尝试找到最匹配的正弦波。

判断匹配程度的方法是,从原信号中减掉,剩余信号总能量越低,越匹配。

算法的工作方式:
1. 计算平均值,找到直流分量
2. 对每个频率,先搜索最佳相位:在0到1之间使用三分法不断缩小区间,找到使减去后能量最小的相位
3. 再用同样的方法搜索最佳幅度:在0到一个很大的数之间使用三分法找最优值
4. 最终每个频率都得到一对值(幅度,相位)

与传统傅里叶变换的区别:
- 不需要任何数学知识:不理解复数、欧拉公式、正交性也能看懂
- 直观易懂:就是在做“波形减去尝试”——调整正弦波的位置和高度,看它能减掉多少能量
- 极其缓慢:对每个频率都要进行几十次搜索,每次搜索都要遍历全部数据点

适用场景:
- 教学演示:让学生理解傅里叶变换的物理意义
- 自定义基函数:可以搜索方波、三角波等其他波形
- 验证傅里叶变换算法的正确性和可行性

局限性:
- 计算复杂度极高:O(N² × 迭代次数),比标准FFT慢到不可理喻

这不是一个实用的算法,但它是理解傅里叶变换“到底在干什么”的最佳入门代码。

代码如下

<?php

function slowFT($array){
    $FT=[];
    $zero=array_sum($array)/count($array);
    $FT[0]=[$zero,null];
    for($freq=1;$freq<floor(count($array)/2)-1;$freq++){
        $phase=get_phase($array,$freq);
        $size=get_size($array,$freq,$phase);
        $FT[$freq]=[$size,$phase];
    }
    return $FT;
}
function customsin($count,$freq,$phase,$size,$x){
    return $size*sin(2*pi()*($freq*$x/$count-$phase));
}
function getenergy($freq, $phase, $size, $array) {
    $energy = 0;
    for ($x = 0; $x < count($array); $x++) {
        $energy += ($array[$x] - customsin(count($array), $freq, $phase, $size, $x)) ** 2;
    }
    return $energy;
}
function get_phase($array,$freq){
    $min=0;
    $max=1;
    for($i=0;$i<15;$i++){
        $mid1=$min+($max-$min)*1/3;
        $mid2=$min+($max-$min)*2/3;
        $energy1=getenergy($freq,$min,1,$array);
        $energy2=getenergy($freq,$mid1,1,$array);
        $energy3=getenergy($freq,$mid2,1,$array);
        $energy4=getenergy($freq,$max,1,$array);
        $sum1=$energy1+$energy2;
        $sum2=$energy2+$energy3;
        $sum3=$energy3+$energy4;
        if($sum1<=$sum2 && $sum1<=$sum3){
            $max=$mid1;
        }elseif($sum2<=$sum1 && $sum2<=$sum3){
            $min=$mid1;
            $max=$mid2;
        }else{
            $min=$mid2;
        }
    }
    return ($min+$max)/2;
}
function get_size($array, $freq, $phase) {
    $min = 0;
    $max = 200000000;
    for ($i = 0; $i < 80; $i++) {
        $m1 = $min + ($max - $min) / 3;
        $m2 = $min + ($max - $min) * 2 / 3;
        $energy1 = getenergy($freq,$phase,$m1,$array);
        $energy2 = getenergy($freq,$phase,$m2,$array);
        if ($energy1 < $energy2) {
            $max = $m2;
        } else {
            $min = $m1;
        }
    }
    return ($min + $max) / 2;
}
#1
发帖 2026-08-10 12:19:25 · 快照 2026-08-12 10:40:27

@altzin #楼主 默默给你点了个赞,并投币1积分。

#2
发帖 2026-08-10 12:21:28 · 快照 2026-08-12 10:40:27

这脑洞挺有意思,虽然慢到没法用,但用来当教学例子确实直观。

#3
发帖 2026-08-10 12:24:58 · 快照 2026-08-12 10:40:27

这帖子可以。。少见

#4
发帖 2026-08-10 12:25:40 · 快照 2026-08-12 10:40:27

@altzin #楼主 默默给你点了个赞,并投币5积分。