◐ Off-By-One · answer catalog

fft-polynomial-multiplication

1 answer(s)pythonpython3

ang = 2 math.pi / length (-1 if invert else 1)

📦 Source in repository (JSON)

Answer

import math
from typing import List, Union

def is_power_of_two(n: int) -> bool:
    return n > 0 and (n & (n - 1)) == 0

def next_power_of_two(n: int) -> int:
    return 1 << (n - 1).bit_length()

def fft(a: List[complex], invert: bool = False) -> List[complex]:
    """
    Cooley-Tukey radix-2 FFT (decimation-in-time).
    - a: list of complex numbers, length must be a power of two.
    - invert: if True, compute the inverse FFT (÷n scaling applied).
    """
    n = len(a)
    if n <= 1:
        return a[:]

    # Bit-reversal permutation
    j = 0
    for i in range(1, n):
        bit = n >> 1
        while j & bit:
            j ^= bit
            bit >>= 1
        j ^= bit
        if i < j:
            a[i], a[j] = a[j], a[i]

    # Iterative radix-2 DIT butterfly stages
    length = 2
    while length <= n:
        ang = 2 * math.pi / length * (-1 if invert else 1)
        wlen = complex(math.cos(ang), math.sin(ang))

        for i in range(0, n, length):
            w = 1 + 0j
            half = length // 2
            for j in range(i, i + half):
                u = a[j]
                v = a[j + half] * w
                a[j] = u + v
                a[j + half] = u - v
                w *= wlen
        length <<= 1

    if invert:
        for i in range(n):
            a[i] /= n

    return a

def multiply_polynomials(p: List[Union[float, int]],
                         q: List[Union[float, int]]) -> List[float]:
    """
    Multiply two polynomials via FFT-based convolution.
    Zero-pads to power-of-two length, forward FFTs, pointwise multiply,
    inverse FFT, rounds to integer coefficients.
    """
    n = next_power_of_two(max(len(p), len(q)) * 2)

    # Zero-pad
    fa = [complex(x, 0) for x in p] + [0j] * (n - len(p))
    fb = [complex(x, 0) for x in q] + [0j] * (n - len(q))

    # Forward FFTs
    fft(fa, invert=False)
    fft(fb, invert=False)

    # Pointwise multiply in frequency domain
    for i in range(n):
        fa[i] *= fb[i]

    # Inverse FFT
    fft(fa, invert=True)

    # Round to real integer coefficients
    deg = len(p) + len(q) - 2
    return [round(fa[i].real) for i in range(deg + 1)]

How it works: - Bit-reversal permutation reorders inputs for in-place DIT computation. - Iterative butterfly stages combine pairs using twiddle factors ( e^{\pm 2\pi i k / L} ), with sign flipped for inverse. - Polynomial multiplication: zero-pad both coefficient lists to a power of two (at least deg(p)+deg(q)+1), forward FFT each, pointwise multiply, inverse FFT, and round real components.


Evidence & signatures

Verified with `python3 fft_solution.py` — all 7 tests pass:

| Test | Inputs | Expected | Result |
|------|--------|----------|--------|
| 1 | `[1,2] * [3,4]` | `[3,10,8]` | ✅ |
| 2 | `[1,1] * [1,1]` | `[1,2,1]` | ✅ |
| 3 | `[1,0,-1] * [1,1,1,1]` | `[1,1,0,0,-1,-1]` | ✅ |
| 4 | `[5] * [0,0,3]` | `[0,0,15]` | ✅ |
| 5 | `[1,2,3] * [1]` (identity) | `[1,2,3]` | ✅ |
| 6 | `[7] * [3]` (scalars) | `[21]` | ✅ |
| 7 | FFT round-trip (4 pts) | ±1e-10 tolerance | ✅ |

**Edge cases covered:**
- **Single-element** (scalar × scalar)
- **Identity** (multiply by `[1]`)
- **Sparse** (e.g., `[5] * [0,0,3]` produces leading zeros)
- **Negative coefficients** (`1 - x²`)
- **Unequal lengths**
- **FFT round-trip** verifies forward+inverse identity to machine precision

---
{"model": "gpt-4o", "problem_class": "fft-polynomial-multiplication", "result": "passed", "tests": 7}
Generated from the verified corpus · MIT licensedBack to the catalog