簡體   English   中英

使用遞歸python函數進行FFT

[英]FFT using recursive python function

我正在嘗試使用以下代碼查找給定列表的FFT

經過大量試驗,我發現該代碼僅對具有2^m2^m+1元素的輸入列表運行。

您能否說明為什么會這樣,是否可以修改它以使用包含其他數量元素的輸入列表。 (PS我有一個16001個元素的輸入列表)

    from cmath import exp, pi

    def fft(x):
        N = len(x)
        if N <= 1: return x
        even = fft(x[0::2])
        odd =  fft(x[1::2])
        T= [exp(-2j*pi*k/N)*odd[k] for k in xrange(N/2)]
        return [even[k] + T[k] for k in xrange(N/2)] + \
        [even[k] - T[k] for k in xrange(N/2)]

    print( ' '.join("%5.3f" % abs(f) 
            for f in fft([1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0])) )

編輯1能否請您說明上一個和以下函數定義之間的區別:

def fft(x):
    N = len(x)
    T = exp(-2*pi*1j/N)
    if N > 1:
        x = fft(x[::2]) + fft(x[1::2])
        for k in xrange(N/2):
            xk = x[k]
            x[k] = xk + T**k*x[k+N/2]
            x[k+N/2] = xk - T**k*x[k+N/2]
    return x

編輯2:實際上,這段代碼(在編輯1下)確實有效(很抱歉縮進和變量命名的錯誤),這就是為什么我想了解兩者之間的區別(這也適用於16001個元素!)

算法

此算法僅適用於兩個輸入數據的冪。 看一下理論

這是檢查以下先決條件的改進版本:

from __future__ import print_function

import sys

if sys.version_info.major < 3:
    range = xrange

from cmath import exp, pi

def fft(x):
    N = len(x)
    if N <= 1: 
        return x
    if N % 2 > 0:
        raise ValueError("size of x must be a power of 2")
    even = fft(x[::2])
    odd =  fft(x[1::2])
    r = range(N//2)
    T = [exp(-2j * pi * k / N) * odd[k] for k in r]
    [even[k] for k in r]
    res = ([even[k] + T[k] for k in r] +
           [even[k] - T[k] for k in r])
    return res

input_data = [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
print(' '.join("%5.3f" % abs(f) for f in fft(input_data)))

NumPy帶有FFT函數的優化版本。 它適用於大小不等於2的冪的大小。 但這會慢很多,因為它需要應用其他算法。

例如:

from numpy.fft import fft as np_fft
​
n = 1000
a = np.arange(n ** 2)
b = np.arange(n ** 2 - 10)

2的冪的情況下的運行時:

%timeit np_fft(a)
10 loops, best of 3: 59.3 ms per loop

遠小於不是這種情況的情況:

%timeit np_fft(b)
1 loops, best of 3: 511 ms per loop

遞歸限制

Python的內置遞歸限制為1000:

>>> import sys
>>> sys.getrecursionlimit()
1000

但是您可以增加遞歸限制:

sys.setrecursion(50000)

文檔告訴您原因:

getrecursionlimit()

返回遞歸限制的當前值,即Python解釋器堆棧的最大深度。 此限制可防止無限遞歸導致C堆棧溢出和Python崩潰。 可以通過setrecursionlimit()進行設置。

setrecursionlimit()

將Python解釋器堆棧的最大深度設置為limit。 此限制可防止無限遞歸導致C堆棧溢出和Python崩潰。

可能的最高限制取決於平台。 當用戶擁有需要深度遞歸的程序和支持更高限制的平台時,他們可能需要將限制設置為更高。 這應該小心進行,因為過高的限制可能導致崩潰。

回答編輯版本

而這個版本:

from __future__ import print_function

import sys

if sys.version_info.major < 3:
    range = xrange

from cmath import exp, pi

def fft2(x):
    N = len(x)
    T = exp(-2*pi*1j/N)
    if N > 1:
        x = fft2(x[::2]) + fft2(x[1::2])
        for k in range(N//2):
            xk = x[k]
            x[k] = xk + T**k*x[k+N//2]
            x[k+N//2] = xk - T**k*x[k+N//2]
    return x

似乎適用於2的冪的輸入:

import numpy as np
from numpy.fft import fft as np_fft

data = [1, 2, 3, 4]
np.allclose(fft2(data), np_fft(data))

True

對於不同數量的輸入,它不會給出正確的結果。

data2 = [1, 2, 3, 4, 5]
np.allclose(fft2(data2), np_fft(data2))

False

它仍然基於這樣的假設:即使輸入沒有拋出異常,輸入的數量也是2的冪。

在以上代碼中的這一行:

T = [exp(-2j * pi * k / N) * odd[k] for k in r]

如果您在此部分仔細觀察: exp(-2j * pi * k / N)將導致所有輸出矢量沿順時針方向移動,對於右逆時針答案,將exp(-2j * pi * k / N)替換為exp(2j * pi * k / N) omega = exp(2j * pi * k / N)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM