以尽可能少的字符实现快速傅立叶变换。
规则:
- 最短的解决方案获胜
- 可以假定输入是一维数组,其长度是2的幂。
- 您可以使用您选择的算法,但是解决方案实际上必须是快速傅立叶变换,而不仅仅是天真的离散傅立叶变换(也就是说,它的渐近计算成本为)
编辑:
该代码应实现标准的前向快速傅立叶变换,其形式可以在Wolfram文章的公式(3)中看到,
- 不允许使用现有标准库或统计信息包中的FFT函数。这里的挑战是简洁地实现 FFT算法本身。
以尽可能少的字符实现快速傅立叶变换。
规则:
编辑:
该代码应实现标准的前向快速傅立叶变换,其形式可以在Wolfram文章的公式(3)中看到,
Answers:
在@ chyaong的帮助下,Cooley–Tukey FFT的另一个实现。
{n=Length@#}~With~If[n>1,Join[+##,#-#2]&[#0@#[[;;;;2]],#0@#[[2;;;;2]]I^Array[-4#/n&,n/2,0]],#]&
FFT[x_] := With[{N = Length[x]},
If[N > 1,
With[{a = FFT[ x[[1 ;; N ;; 2]] ],
b = FFT[ x[[2 ;; N ;; 2]] ] * Table[E^(-2*I*Pi*k/N), {k, 0, N/2 - 1}]},
Join[a + b, a - b]],
x]]
#[[;;;;2]]==#[[1;;N;;2]]
和[[2;;;;2]]==[[2;;N;;2]]
。
With[{L=Length@#},If[L>1,Join[+##,#-#2]&[#0@#[[;;;;2]],#0@#[[2;;;;2]]E^(-2I*Pi(Range[L/2]-1)/L)],#]]&
_2&(0((+,-)]%_1^i.@#%#)&$:/@|:]\)~1<#
几年后有所改善。仍然使用Cooley-Tukey FFT算法。
使用保存的4个字节ë πi = -1,由于@ 漏嫩。
f =: _2&(0((+,-)]%_1^i.@#%#)&$:/@|:]\)~1<#
f 1 1 1 1
4 0 0 0
f 1 2 3 4
10 _2j2 _2 _2j_2
f 5.24626 3.90746 3.72335 5.74429 4.7983 8.34171 4.46785 0.760139
36.9894 _6.21186j0.355661 1.85336j_5.74474 7.10778j_1.13334 _0.517839 7.10778j1.13334 1.85336j5.74474 _6.21186j_0.355661
_2&(0((+,-)]%_1^i.@#%#)&$:/@|:]\)~1<# Input: array A
# Length
1< Greater than one?
_2&( )~ Execute this if true, else return A
_2 ]\ Get non-overlapping sublists of size 2
0 |: Move axis 0 to the end, equivalent to transpose
/@ Reduce [even-indexed, odd-indexed]
&$: Call recursively on each
# Get the length of the odd list
i.@ Range from 0 to that length exclusive
%# Divide each by the odd length
_1^ Compute (-1)^x for each x
] Get the odd list
% Divide each in that by the previous
+ Add the even values and modified odd values
- Subtract the even values and modified odd values
, Join the two lists and return
它使用基数为2的Cooley-Tukey FFT算法
from math import*
def F(x):N=len(x);t=N<2or(F(x[::2]),F(x[1::2]));return N<2and x or[
a+s*b/e**(2j*pi*n/N)for s in[1,-1]for(n,a,b)in zip(range(N),*t)]
测试结果
>>> import numpy as np
>>> x = np.random.random(512)
>>> np.allclose(F(x), np.fft.fft(x))
True
from x import*
,并且sum(([x for x in y] for y in z),[])
比更长[x for y in z for x in y]
。
简短版-简短而甜美,适合一条推文(感谢miles):
from math import*
def f(v):
n=len(v)
if n<2:return v
a,b=f(v[::2])*2,f(v[1::2])*2;return[a[i]+b[i]/1j**(i*4/n)for i in range(n)]
(在Python 2,/
是当双方都是整数舍去除法,所以我们替换(i*4/n)
通过(i*4.0/n)
,其中凸块长度115个字符)。
加长版-经典Cooley-Tukey FFT的内部结构更加清晰:
import cmath
def transform_radix2(vector):
n = len(vector)
if n <= 1: # Base case
return vector
elif n % 2 != 0:
raise ValueError("Length is not a power of 2")
else:
k = n // 2
even = transform_radix2(vector[0 : : 2])
odd = transform_radix2(vector[1 : : 2])
return [even[i % k] + odd[i % k] * cmath.exp(i * -2j * cmath.pi / n) for i in range(n)]
感谢@Giuseppe帮助我减少了32个 36字节!
f=function(x,n=sum(x|1),y=1:(n/2)*2)`if`(n>1,f(x[-y])+c(b<-f(x[y]),-b)*exp(-2i*(y/2-1)*pi/n),x)
这里的另一个技巧是使用主函数默认参数实例化一些变量。
用法仍然相同:
x = c(1,1,1,1)
f(x)
[1] 4+0i 0+0i 0+0i 0+0i
133字节的4年旧版本:
f=function(x){n=length(x);if(n>1){a=Recall(x[seq(1,n,2)]);b=Recall(x[seq(2,n,2)]);t=exp(-2i*(1:(n/2)-1)*pi/n);c(a+b*t,a-b*t)}else{x}}
缩进:
f=function(x){
n=length(x)
if(n>1){
a=Recall(x[seq(1,n,2)])
b=Recall(x[seq(2,n,2)])
t=exp(-2i*(1:(n/2)-1)*pi/n)
c(a+b*t,a-b*t)
}else{x}
}
它还使用Cooley-Tukey算法。这里唯一的技巧是使用Recall
允许递归的函数,以及使用R向量化来大大缩短实际计算的时间。
用法:
x = c(1,1,1,1)
f(x)
[1] 4+0i 0+0i 0+0i 0+0i
Recall
现在就不必要了,确实如此。我注意到几个月前,但懒得更改它:)我将对其进行修改。
y
那儿,但没有注意到它也可以用作exp(...)
零件。
这是从jakevdp的解决方案中大量借用的,因此我将其设置为社区Wiki。
from math import*
F=lambda x:x*(len(x)<2)or[a+s*b/e**(2j*pi*n/len(x))for s in(1,-1)for n,(a,b)in
enumerate(zip(F(x[::2]),F(x[1::2])))]
变化:
-12个字符:杀死t
。
def F(x):N=len(x);t=N<2or(F(x[::2]),F(x[1::2]));return ... in zip(range(N),*t)]
def F(x):N=len(x);return ... in zip(range(N),F(x[::2]),F(x[1::2]))]
-1 char:指数技巧,x*y**-z == x/y**z
(这可能会帮助其他人)
...[a+s*b*e**(-2j*pi*n/N)...
...[a+s*b/e**(2j*pi*n/N)...
-2个字符:替换and
为*
...return N<2and x or[
...return x*(N<2)or[
+1个字符:lambda
变身,杀死N
def F(x):N=len(x);return x*(N<2)or[a+s*b/e**(2j*pi*n/N) ... zip(range(N) ...
F=lambda x:x*(len(x)<2)or[a+s*b/e**(2j*pi*n/len(x)) ... zip(range(len(x)) ...
-2 char:使用enumerate
代替zip(range(len(
...for(n,a,b)in zip(range(len(x)),F(x[::2]),F(x[1::2]))]
...for n,(a,b)in enumerate(zip(F(x[::2]),F(x[1::2])))]
f=lambda x:x*(len(x)<2)or[u+v/1j**(4*i/len(x))for i,(u,v)in enumerate(zip(f(x[::2])*2,f(x[1::2])*2))]
for s in(1,-1)for
为for s in 1,-1for
甚至for s in-1,1for
。
typedef double complex cplx;
void fft(cplx buf[],cplx out[],int n,int step){
if(step < n){
fft(out, buf,n, step * 2);
fft(out+step,buf+step,n,step*2);
for(int i=0;i<n;i+=2*step){
cplx t=cexp(-I*M_PI*i/n)*out[i+step];
buf[i/2]=out[i]+t;
buf[(i+n)/2]=out[i]-t;
}}}
问题是,这样的实现是无用的,并且简单的算法要快得多。
step < n
可以将更改为step<n
,step * 2
将更改为step*2
。
EDIT6:感谢@algmyr另外一个字节!
function Y=f(Y);
n=numel(Y);
k=2:2:n;
if k;
c=f(Y(k-1));
d=f(Y(k)).*i.^(2*(2-k)/n);
Y=[c+d;c-d];
end
EDIT5:仍然越来越短:)感谢@sanchises
function Y=f(Y)
n=numel(Y);
k=2:2:n;
if k;
c=f(Y(k-1));
d=f(Y(k)).*(-1).^((2-k)/n);
Y=[c+d;c-d];
end
EDIT4:是的,还有-1个字符(如果没有,也可以完成k
):
function Y=f(Y)
n=numel(Y);
if n>1;
k=2:2:n;
c=f(Y(k-1));
d=f(Y(k)).*(-1).^((k/2-1)*2/n)';
Y=[c+d;c-d];
end
编辑2/3:感谢@sanchises进行进一步的改进!
function Y=f(Y)
n=numel(Y);
if n>1;
c=f(Y(1:2:n));
d=f(Y(2:2:n)).*(-1).^(-(0:n/2-1)*2/n).';
Y=[c+d;c-d];
end
编辑:可以进行一些改进,并注意到不需要缩放常数。
这是扩展版本,如果您删除换行符/空格,则字符数有效。(仅适用于列向量。)
function y=f(Y)
n=numel(Y);
y=Y;
if n>1;
c=f(Y(1:2:n));
d=f(Y(2:2:n));
n=n/2;
d=d.*exp(-pi*i*(0:n-1)/n).';
y=[c+d;c-d];
end
d=
行合并为:m=n/2;d=f(Y(2:2:n)).*exp(-pi*i*(0:m-1)/m).';
。此外,考虑换y=f(Y)
到Y=f(Y)
并删除3号线(并承诺你永远不会做代码高尔夫的那个之外)
function Y = f(Y)
有比不可读性其他任何disadvanteages?
m=n/2
可以去除,转而m
换成n/2
和n*2
分别。然后,我坚信,该程序与MATLAB中的程序一样短。
LḶ÷$N-*×,N$+ḷF
s2Z߀ç/µ¹Ṗ?
果冻是在挑战之后创造出来的,因此没有竞争。
这使用Cooley-Tukey基数2递归算法。对于非高尔夫版本,请参阅我在Mathematica中的答案。
LḶ÷$N-*×,N$+ḷF Helper link. Input: lists A and B
L Get the length of A
$ Operate on that length
Ḷ Make a range [0, 1, ..., length-1]
÷ Divide each by length
N Negate each
- The constant -1
* Compute -1^(x) for each x in that range
× Multiply elementwise between that range and B, call it B'
$ Operate on that B'
N Negate each
, Make a list [B', -B']
ḷ Get A
+ Add vectorized, [B', -B'] + A = [A+B', A-B']
F Flatten that and return
s2Z߀ç/µ¹Ṗ? Main link. Input: list X
Ṗ Curtail - Make a copy of X with the last value removed
? If that list is truthy (empty lists are falsey)
µ Parse to the left as a monad
s2 Split X into sublists of length 2
Z Transpose them to get [even-index, odd-index]
߀ Call the main link recursively on each sublist
ç/ Call the helper link as a dyad on the sublists and return
Else
¹ Identity function on X and return
#define d(a,b,c)f(a,b,c,1,0)
f(a,b,c,n,k)_Complex*a,*b;{_Complex z[c];*b=*a;if(n<c)for(f(a,z,c,n*2),f(a+n,z+n,c,n*2);k<c;k+=n*2)b[k+c>>1]=z[k]*2-(b[k/2]=z[k]+z[k+n]/cpow(1i,2.*k/c));}
少打些高尔夫球
#define d(a,b,c)f(a,b,c,1,0)
f(a,b,c,n,k)_Complex*a,*b;{
_Complex z[c];
*b=*a;
if(n<c)
for(f(a,z,c,n*2),f(a+n,z+n,c,n*2);k<c;k+=n*2)
b[k+c>>1]=z[k]*2-(b[k/2]=z[k]+z[k+n]/cpow(1i,2.*k/c));
}
f(f=@(f)@(x,n=rows(x)){@(d=f(f)(x(k=2:2:n)).*i.^((k*2-4)/n)')[d+(c=f(f)(x(k-1)));c-d],x}{1+(n<2)}())
哎呀,我的眼睛从这个递归的 lambda 流血了。大部分内容都来自@flawr的答案。
f( % lambda function
f=@(f) % defined in its own argument list,
% accepts itself as parameter (for recursion)
@(x,n=rows(x)){ % calls another lambda,
% 2nd parameter is just to define a variable
@(d=f(f)(x(k=2:2:n)).*i.^((k*2-4)/n)')% 1/4 of FFT (argument just defines a variable)
[d+(c=f(f)(x(k-1))); % 2/4 of FFT
c-d % 4/4 of FFT
], % This is in a @()[] to inhibit evaluation
% unless actually called
x % FFT of length 1
}{1+(n<2)} % if len(x)==1, return x
% else return [d+c;c-d]
() % this is probably important too
)
L(g,n,f)==>[g for i in 1..n|f]
h(a)==(n:=#a;n=1=>a;c:=h(L(a.i,n,odd? i));d:=h(L(a.i,n,even? i));n:=n/2;t:=1>0;v:=L(d.i*%i^(-2*(i-1)/n),n,t);append(L(c.i+v.i,n,t),L(c.i-v.i,n,t)))
即使h(a)可以通过所有测试,并且可以通过参加下面的fft()调用h()或hlp()来检查自变量,也可以作为此“竞争”的入口。我不知道该软件是否可以运行,因为我只看过别人写的东西,并搜索它可以在Axiom中运行的方式以返回一些可能的正确结果。在未注释的代码下面,带有少量注释:
-- L(g,n,f)==>[g for i in 1..n|f]
-- this macro L, build one List from other list, where in g, there is the generic element of index i
-- (as a.i, or a.i*b.i or a.i*4), n build 1..n that is the range of i, f is the condition
-- for insert the element in the list result.
hlp(a)==
n:=#a;n=1=>a
-- L(a.i,n,odd? i) it means build a list getting "even indices i of a.i as starting from index 0" [so even is odd and odd is even]
-- L(a.i,n,even? i) it means build a list getting "odd indices i of a.i as starting from index 0"
c:=hlp(L(a.i,n,odd? i));d:=hlp(L(a.i,n,even? i))
n:=n/2;t:=1>0
v:=L(d.i*%i^(-2*(i-1)/n),n,t)
append(L(c.i+v.i,n,t),L(c.i-v.i,n,t))
-- Return Fast Fourier transform of list a, in the case #a=2^n
fft(a)==(n:=#a;n=0 or gcd(n,2^30)~=n=>[];hlp(a))
(5) -> h([1,1,1,1])
(5) [4,0,0,0]
Type: List Expression Complex Integer
(6) -> h([1,2,3,4])
(6) [10,- 2 + 2%i,- 2,- 2 - 2%i]
Type: List Expression Complex Integer
(7) -> h([5.24626,3.90746,3.72335,5.74429,4.7983,8.34171,4.46785,0.760139])
(7)
[36.989359, - 6.2118552150 341603904 + 0.3556612739 187363298 %i,
1.85336 - 5.744741 %i, 7.1077752150 341603904 - 1.1333387260 812636702 %i,
- 0.517839, 7.1077752150 341603904 + 1.1333387260 812636702 %i,
1.85336 + 5.744741 %i,
- 6.2118552150 341603904 - 0.3556612739 187363298 %i]
Type: List Expression Complex Float
(8) -> h([%i+1,2,%i-2,9])
(8) [10 + 2%i,3 + 7%i,- 12 + 2%i,3 - 7%i]
Type: List Expression Complex Integer
在我见过的少数情况下,h()或fft()会返回精确的解决方案,但是如果简化效果不佳,例如:
(13) -> h([1,2,3,4,5,6,7,8])
(13)
+--+ +--+
(- 4 + 4%i)\|%i - 4 + 4%i (- 4 - 4%i)\|%i - 4 + 4%i
[36, --------------------------, - 4 + 4%i, --------------------------, - 4,
+--+ +--+
\|%i \|%i
+--+ +--+
(- 4 + 4%i)\|%i + 4 - 4%i (- 4 - 4%i)\|%i + 4 - 4%i
--------------------------, - 4 - 4%i, --------------------------]
+--+ +--+
\|%i \|%i
Type: List Expression Complex Integer
这样就足以更改列表中仅一个元素的类型,如下面的文字8所示。(浮点数)用于找到近似解:
(14) -> h([1,2,3,4,5,6,7,8.])
(14)
[36.0, - 4.0000000000 000000001 + 9.6568542494 923801953 %i, - 4.0 + 4.0 %i,
- 4.0 + 1.6568542494 92380195 %i, - 4.0, - 4.0 - 1.6568542494 92380195 %i,
- 4.0 - 4.0 %i, - 4.0 - 9.6568542494 923801953 %i]
Type: List Expression Complex Float
我写了它,看到了所有其他答案,因为在链接中,页面太难了,所以我不知道这段代码是否正确。我不是fft专家,所以所有这一切(可能)都是错误的。
{1≥k←≢⍵:⍵⋄(∇⍵[y∼⍨⍳k])(+,-)(∇⍵[y←2×⍳t])×0J1*t÷⍨2-2×⍳t←⌊k÷2}
测试
f←{1≥k←≢⍵:⍵⋄(∇⍵[y∼⍨⍳k])(+,-)(∇⍵[y←2×⍳t])×0J1*t÷⍨2-2×⍳t←⌊k÷2}
f 1 1 1 1
4J0 0J0 0J0 0J0
f 1 2 3 4
10J0 ¯2J2 ¯2J0 ¯2J¯2
f 1J1 2 ¯2J1 9
10J2 3J7 ¯12J2 3J¯7
f 5.24626,3.90746,3.72335,5.74429,4.7983,8.34171,4.46785,0.760139
36.989359J0 ¯6.211855215J0.3556612739 1.85336J¯5.744741 7.107775215J¯1.133338726 ¯0.517839J0
7.107775215J1.133338726 1.85336J5.744741 ¯6.211855215J¯0.3556612739
FFT
(3个字符):它在标准库中”是否满足“实施”要求?一些测试用例也会很好。