AlgoVerse

تبدیل فوریه‌ی سریع (FFT)

مقدار چندجمله‌ای را در n ریشه‌ی واحد با تقسیم و حل در O(n log n) حساب می‌کند (و برعکس، با FFT وارون)؛ پایه‌ی ضرب سریع چندجمله‌ای‌ها.

چندجمله‌ای A(x) = a_0 + a_1·x + … + a_{n−1}·x^{n−1} را می‌خواهیم در n نقطه حساب کنیم. اگر نقطه‌ها ریشه‌های n‌ام واحد ω^0, ω^1, …, ω^{n−1} (با ω = e^{2πi/n}) باشند، ترفندی ممکن است:

ادامه‌ی توضیح ↓
ورودی:
coeffs 1×4 · inverse=0خروجی PDF

A(x) = 1 + 2x + 3x² + 4x³ در چهار ریشه‌ی چهارم واحد (1، i، −1، −i): ۱۰، −2−2i، −2، −2+2i.

  1. import cmath
  2. def transform(coeffs, inverse):
  3. a = coeffs[0]
  4. n = len(a)
  5. if n & (n - 1) != 0:
  6. return None
  7. y = fft(a, inverse)
  8. if inverse:
  9. y = [v / n for v in y]
  10. rows = [(k, show(v)) for k, v in enumerate(y)]
  11. return [show(v) for v in y]
  12. def fft(a, inverse):
  13. n = len(a)
  14. if n == 1:
  15. return [complex(a[0])]
  16. sign = -1 if inverse else 1
  17. w_n = cmath.exp(sign * 2j * cmath.pi / n) # the principal n-th root of unity
  18. even = fft(a[0::2], inverse)
  19. odd = fft(a[1::2], inverse)
  20. y = [0] * n
  21. w = 1
  22. for k in range(n // 2):
  23. t = w * odd[k]
  24. y[k] = even[k] + t
  25. y[k + n // 2] = even[k] - t
  26. w = w * w_n
  27. return y
  28. def show(z):
  29. # Rounded for reading, e.g. "2-1.414i".
  30. re, im = round(z.real, 3) + 0.0, round(z.imag, 3) + 0.0
  31. return f"{re:g}" if im == 0 else f"{re:g}{im:+g}i"

ناوردا: A(ω^k) = A_زوج(ω^(2k)) + ω^k · A_فرد(ω^(2k))، و مربع ریشه‌های n‌ام واحد همان ریشه‌های (n/2)‌ام‌اند؛ پس هر سطح نصف کار را دارد.

شروع اجرا

n—
k—

جدول هنوز خالی است.

درخت فراخوانی‌های بازگشتی

نارنجی پر = فراخوانی در حال اجرا · حاشیه‌ی نارنجی = هنوز روی پشته · خاکستری = تمام شده

متغیرهای محلی

inverse0

نارنجی = در همین گام عوض شد

coeffs list

[1, 2, 3, 4]

Call stack

  1. transform()خط 4
گام ۱ از ۱۰۱
خط سیاه = butterfly · خط نارنجی = گام کلیدیbase: 0/4 · butterfly: 0/4 · done: 0/1
    توضیح

    ایده

    چندجمله‌ای A(x) = a_0 + a_1·x + … + a_{n−1}·x^{n−1} را می‌خواهیم در n نقطه حساب کنیم. اگر نقطه‌ها ریشه‌های n‌ام واحد ω^0, ω^1, …, ω^{n−1} (با ω = e^{2πi/n}) باشند، ترفندی ممکن است:

    A(x) = A_زوج(x²) + x · A_فرد(x²)
    

    و مربع ریشه‌های n‌ام همان ریشه‌های (n/2)‌ام‌اند. پس دو مسئله‌ی نیم‌اندازه حل می‌کنیم و با پروانه‌ها ترکیب می‌کنیم:

    y[k] = y⁰[k] + ω^k·y¹[k]      y[k + n/2] = y⁰[k] − ω^k·y¹[k]
    

    T(n) = 2T(n/2) + O(n) = O(n log n)، در برابر O(n²) برای حساب جداگانه‌ی n مقدار.

    FFT وارون

    برگشت از مقدارها به ضریب‌ها همان FFT است با ω⁻¹ به‌جای ω و تقسیم بر n (پارامتر inverse = 1).

    در trace به چه چیزی نگاه کنی

    • درخت فراخوانی دودویی است (زوج و فرد)؛ جدول آخر مقدارهای گردشده است.