Quais são algumas das diferenças entre DFT e FFT que tornam a FFT tão rápida?

16

Estou tentando entender as FFTs, eis o que tenho até agora:

Para encontrar a magnitude das frequências em uma forma de onda, é preciso investigá-las multiplicando a onda pela frequência que estão procurando, em duas fases diferentes (sin e cos) e fazendo a média de cada uma. A fase é encontrada por sua relação com as duas, e o código para isso é algo como isto:

//simple pseudocode
var wave = [...];                //an array of floats representing amplitude of wave
var numSamples = wave.length;
var spectrum = [1,2,3,4,5,6...]  //all frequencies being tested for.  

function getMagnitudesOfSpectrum() {
   var magnitudesOut = [];
   var phasesOut = [];

   for(freq in spectrum) {
       var magnitudeSin = 0;
       var magnitudeCos = 0;

       for(sample in numSamples) {
          magnitudeSin += amplitudeSinAt(sample, freq) * wave[sample];
          magnitudeCos += amplitudeCosAt(sample, freq) * wave[sample];
       }

       magnitudesOut[freq] = (magnitudeSin + magnitudeCos)/numSamples;
       phasesOut[freq] = //based off magnitudeSin and magnitudeCos
   }

   return magnitudesOut and phasesOut;
}

Para fazer isso com muitas frequências muito rapidamente, as FFTs usam muitos truques.

Quais são alguns dos truques usados ​​para tornar as FFTs muito mais rápidas que a DFT?

PS: Tentei analisar os algoritmos FFT completos na Web, mas todos os truques tendem a ser condensados ​​em um belo pedaço de código sem muita explicação. O que eu preciso primeiro, antes que eu possa entender tudo, é uma introdução a cada uma dessas mudanças eficientes como conceitos.

Obrigado.

Seph Reed
fonte
7
"DFT" não se refere a um algoritmo: refere-se a uma operação matemática. "FFT" refere-se a uma classe de métodos para calcular essa operação.
1
Só queria ressaltar que o uso de sudono seu exemplo de código pode ser confuso, pois esse é um comando bem conhecido no mundo dos computadores. Você provavelmente quis dizer psuedocode.
Rwfeather
1
@nwfeather Ele provavelmente quis dizer 'pseudocódigo'.
precisa saber é o seguinte

Respostas:

20

A implementação simples de um -ponto DFT é basicamente uma multiplicação por um N × N matriz. Isso resulta em uma complexidade de O ( N 2 ) .NN×NO(N2)

Um dos algoritmos mais comuns da Transformada Rápida de Fourier (FFT) é o algoritmo FFT radix-2 Cooley-Tukey Decimation-in-Time. Essa é uma abordagem básica de dividir e conquistar.

j

WNej2πN
X[k]x[n]X[k]= N - 1 n = 0 x[n]j1X[k]x[n]N N
X[k]=n=0N1x[n]WNkn.
N X[k]= N / 2 - 1 Σ n=0X[2N]W 2 k N N + N / 2 - 1 Σ n=0X[2n+1]W k ( 2 n + 1 ) N x[n]x[n]xeN2
X[k]=n=0N/21x[2n]WN2kn+n=0N/21x[2n+1]WNk(2n+1)
x[n]x[n]x o [ n ] x [ 2 n + 1 ]xe[n]x[2n]xo[n]x[2n+1] e usando o fato de que
  1. WNk(2n+1)=WN2knWNk , e
  2. WN2kn=WN/2kn ,

isso pode ser reescrito como onde e são as transformações DFT no ponto das amostras pares e ímpares de respectivamente. Então, acabamos de transformar uma única DFT de ponto em duas DFTs menores de . Isso reduz o custo computacional porque quando .

X[k]=n=0N/21xe[n]WN/2kn+WNkn=0N/21xo[n]WN/2kn=Xe[k]+WNkXo[k]
Xe[k]Xo[k]N2x[n]NN2
2(N2)2+N<N2
N>2

Podemos então reiterar o mesmo processo nessas duas DFTs menores. Essa abordagem de dividir e conquistar permite alcançar a complexidade de , que é muito melhor que o que tivemos com a ingênua implementação de DFT (como é ilustrado em grande parte pela resposta da esquerda ).O(NlogN)O(N2)

anpar
fonte
você gostaria de listar o que cada uma das variáveis ​​representa? Eu sou um pouco novo para isso, então W, j, X(), Ne kainda não têm definições para mim.
Seph Reed
W já está definido na minha resposta. Tentei definir melhor algumas outras notações. indica índice no domínio da frequência e índice no domínio do tempo. kn
anpar
19

http://nbviewer.jupyter.org/gist/leftaroundabout/83df89a7d3bdc24373ea470fb50be629

DFT, tamanho 16

Diagrama das operações em uma DFT ingênua de tamanho 16

FFT, tamanho 16

Diagrama das operações em uma FFT de tamanho 16 radix-2

A diferença de complexidade é bastante evidente disso, não é?


Aqui está como eu entendo a FFT.

Primeiramente, eu sempre pensaria nas transformadas de Fourier principalmente como transformadas de funções contínuas , ou seja, um mapeamento bijetivo . Sob essa luz, fica claro que não pode ser realmente necessário ir para o "nível mais profundo" e fazer um loop sobre elementos individuais , porque os "elementos individuais" são pontos únicos na linha real, dos quais existem quantidades infinitamente infinitas .FT:L2(R)L2(R)

Então, como essa transformação ainda está bem definida? Bem, é crucial que ele opere não no espaço de função geral mas apenas no espaço das funções integráveis (Lebesgue-, square-) . Agora, essa integrabilidade não é uma propriedade muito forte (muito mais fraca que a diferenciabilidade, etc.), mas exige que a função se torne “localmente describível com informações contáveis”. Essa descrição é dada pelos coeficientes de uma transformada de Fourier de curta duração . RCO caso mais simples é que sua função é contínua e você a divide em regiões tão pequenas que é basicamente constante em cada uma delas. Então, cada um dos STFTs tem um termo mais zerótico. Se você ignorar os outros coeficientes (de qualquer forma deteriorados), cada domínio será apenas um único ponto de dados. De todos esses coeficientes de curto prazo - limite LF, você pode fazer uma transformação de Fourier discreta. Na verdade, é exatamente isso que você faz ao realizar qualquer TF em dados medidos do mundo real!

Os dados medidos não precisam necessariamente corresponder a uma quantidade física fundamental. Por exemplo, quando você mede alguma intensidade de luz , está realmente apenas medindo a amplitude de uma onda eletromagnética cuja frequência é alta demais para ser amostrada com um ADC. Mas é claro que você também pode calcular a DFT de um sinal de intensidade de luz amostrado, e de forma barata, apesar da frequência insana da onda de luz.

Isso pode ser entendido como a razão mais importante pela qual a FFT é barata:

Não se preocupe em tentar ver os ciclos de oscilação individuais do nível mais alto. Em vez disso, transforme apenas informações de alto nível que já foram pré-processadas localmente.

Isso não é tudo o que existe, no entanto. O melhor da FFT é que ela ainda fornece todas as informações que uma DFT completa daria . Ou seja, todas as informações que você também obteria ao experimentar a onda eletromagnética exata de um feixe de luz. Isso pode ser conseguido através da transformação de um sinal de fotodiodo? - você pode medir a frequência de luz exata disso?

Bem, a resposta é não, você não pode. Ou seja, a menos que você aplique truques extras.
Primeiro de tudo, você precisa medir pelo menos aproximadamente a frequência nos curtos blocos de tempo. Bem, isso é possível com um espectrógrafo. Mas só é possível até uma precisão de , uma típica relação de incerteza .Δν=1/Δt

Por termos um período de tempo mais longo, também devemos reduzir a incerteza de frequência. E isso é realmente possível, se você medir localmente não apenas a frequência aproximada, mas também a fase da onda. Você sabe que um sinal de 1000 Hz terá exatamente a mesma fase se você o observar um segundo depois. Enquanto um sinal de 1000,5 Hz, embora indistinguível em pequena escala, terá a fase invertida um segundo depois.

Felizmente, essa informação de fase pode muito bem ser armazenada em um único número complexo. E é assim que a FFT funciona! Tudo começa com muitas pequenas transformações locais. Eles são baratos - por um lado, obviamente, porque eles usam apenas uma pequena quantidade de dados, mas, em segundo lugar, porque eles sabem que, devido ao curto período de tempo, eles não conseguem resolver a frequência com muita precisão de qualquer maneira - por isso ainda é acessível, mesmo que você faça muitas dessas transformações.

No entanto, eles também registram a fase e, a partir disso, é possível tornar a resolução da frequência mais exata no nível superior. A transformação necessária é novamente barata, porque ela mesma não se preocupa com oscilações de alta frequência, mas apenas com os dados de baixa frequência pré-processados.


Sim, minha argumentação é um pouco circular neste momento. Vamos chamá-lo de recursivo e estamos bem ...

Essa relação não é mecânica quântica, mas a incerteza de Heisenberg tem realmente a mesma razão fundamental.

leftaroundabout
fonte
2
bela representação pictórica da questão. :-)
robert bristow-johnson
2
Você não ama diagramas que se repetem em todos os lugares e nunca realmente explicados em qualquer lugar :)
user541686
1
Entendi a imagem depois de ter acabado de ler a resposta de anpar.
JDługosz
15

Aqui está uma figura a ser adicionada à boa resposta de Robert, demonstrando a "reutilização" das operações, neste caso para uma DFT de 8 pontos. Os "Fatores de Twiddle" são representados no diagrama usando a notação que é igual aWNnkej2πnkN

Observe o caminho mostrado e a equação abaixo mostra o resultado para o compartimento de frequência X (1), conforme indicado pela equação de Robert.

As linhas tracejadas não são diferentes das linhas sólidas apenas para esclarecer onde estão as somatórias.

Implementação da FFT

Dan Boschen
fonte
8

essencialmente, ao calcular o DFT ingênuo diretamente do somatório:

X[k]=n=0N1x[n]ej2πnkN

existem pesquisas de tabela para o fator twiddle , multiplicações complexas e adições de . e isso é apenas para um valor de e uma instância de . então o DFT ingênuo joga fora todos esses dados intermediários e repassa todos eles novamente para .N NN-1X[k]kX[k+1]ej2πnkNNN1X[k]kX[k+1]

  1. portanto, a FFT mantém alguns dados intermediários.
  2. a FFT também utilizará um pouco o fator twiddle para que o mesmo fator possa ser usado para uma combinação intermediária de dados.
Robert Bristow-Johnson
fonte
4

Eu sou uma pessoa visual. Prefiro imaginar a FFT como um truque de matriz, e não como um truque de soma.

Para explicar em um nível alto:

Um DFT ingênuo calcula cada amostra de saída independentemente e usa todas as amostras de entrada em cada cálculo (algoritmo N² clássico).

Uma FFT comum usa simetrias e padrões na definição de DFT para fazer o cálculo em "camadas" (log N layers), cada camada com requisitos de tempo constante por amostra, criando um algoritmo N log N.

Mais detalhes:

Uma maneira de visualizar essas simetrias é olhar para o DFT como uma entrada da matriz 1 × N multiplicada por uma matriz NxN de todos os seus exponenciais complexos. Vamos começar com o caso "radix 2". Vamos dividir as linhas pares e ímpares da matriz (correspondendo às amostras de entrada pares e ímpares) e considerá-las como duas multiplicações de matrizes separadas que se somam para obter o mesmo resultado final.

Agora observe estas matrizes: na primeira a metade esquerda é idêntica à metade direita. Na outra, a metade direita é a metade esquerda x −1. Isso significa que realmente precisamos usar a metade esquerda dessas matrizes para multiplicação e criar a metade direita mais barata multiplicando por 1 ou -1. Em seguida, observe que a segunda matriz difere da primeira matriz por fatores iguais em cada coluna, para que possamos fatorá-la e multiplicá-la na entrada. Agora, amostras pares e ímpares usam a mesma matriz, mas requerem um multiplicador primeiro. E a etapa final é observar que essa matriz N / 2 × N / 2 resultante é idêntica a uma matriz N / 2 DFT e podemos fazer isso repetidamente até chegarmos a uma matriz 1 × 1 onde a DFT é uma função de identidade.

Para generalizar além do radix 2, você pode dividir a cada terceira linha e ver três pedaços de colunas ou a cada quarta, etc.

No caso de entradas de tamanho primário, existe um método para zerar, FFT e truncar adequadamente, mas isso está além do escopo desta resposta.

Veja: http://whoiskylefinn.com/MatrixFFT.html

kylefinn
fonte
FFT principal , vários FFT . Usar o teclado zero não é a única opção. Desculpe, mas acho que o preenchimento zero é usado em excesso. Uma pequena pergunta, não entendo o que você quer dizer com "cada camada com requisito de tempo constante por amostra"; se você pudesse explicar, seria incrível.
Mal
1
Desculpe, eu não quis dizer que zero padding era o caminho, só queria apontar para uma leitura mais aprofundada. E "camada" significa uma recursão ou uma tradução de um N DFT para 2 N / 2 DFTs, com tempo constante por amostra, o que significa que essa etapa é O (N).
precisa saber é o seguinte
Até agora, de todas as descrições, essa parece ser a mais próxima de simplificar uma questão complexa. A grande coisa que está faltando, no entanto, é um exemplo dessas matrizes. Você teria um?
Seph Reed
Uploaded this, should help: whoiskylefinn.com/MatrixFFT.html
kylefinn
1

A DFT faz uma matriz N ^ 2 de força bruta se multiplicar.

As FFTs fazem truques inteligentes, explorando propriedades da matriz (degeneralizando a matriz multiplicada) para reduzir o custo computacional.

Vamos primeiro olhar para uma pequena DFT:

W = pés (olho (4));

x = rand (4,1) + 1j * rand (4,1);

X_ref = fft (x);

X = W * x;

assert (max (abs (X-X_ref)) <1e-7)

Ótimo, portanto, podemos substituir a chamada MATLABs da biblioteca FFTW por uma pequena multiplicação de matriz 4x4 (complexa) preenchendo uma matriz a partir da função FFT. Então, como é essa matriz?

N = 4

Wn = exp (-1j * 2 * pi / N),

f = ((0: N-1) '* (0: N-1))

f =

 0     0     0     0
 0     1     2     3
 0     2     4     6
 0     3     6     9

W = Wn. ^ F

W =

1 1 1 1

1 -i -1 i

1 -1 1 -1

1 i -1 -i

Cada elemento é +1, -1, + 1j ou -1j. Obviamente, isso significa que podemos evitar multiplicações complexas completas. Além disso, a primeira coluna é idêntica, o que significa que multiplicamos o primeiro elemento de x repetidamente pelo mesmo fator.

Acontece que os produtos tensores da Kronecker, "fatores duplos" e uma matriz de permutação em que o índice é alterado de acordo com a representação binária invertida são compactos e oferecem uma perspectiva alternativa de como as FFTs são computadas como um conjunto de operações de matriz esparsa.

As linhas abaixo são uma FFT simples de Decimation in Frequency (DIF) radix 2 forward. Embora as etapas possam parecer complicadas, é conveniente reutilizar para FFT direta / inversa, radix4 / split-radix ou dizimação no tempo, enquanto é uma representação justa de como as FFTs locais tendem a ser implementadas no mundo real, Acredito.

N = 4;

x = padrão (N, 1) + 1j * padrão (N, 1);

T1 = exp (-1j * 2 * pi * ([zeros (1, N / 2), 0: (N / 2-1)]). '/ N),

M0 = kron (olho (2), fft (olho (2))),

M1 = kron (fft (olho (2)), olho (2)),

X = ordem de bits (x. '* M1 * diag (T1) * M0),

X_ref = fft (x)

assert (max (abs (X (:) - X_ref (:))) <1e-6)

CF Van Loan tem um ótimo livro sobre esse assunto.

Knut Inge
fonte