#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Polynomial multiplication using a fast Fourier transform (FFT) algorithm.
Author: Fabian Reiter
Tested on Ubuntu 12.04 with Python 2.7.3 and SymPy 0.7.1.rc1
"""
from __future__ import print_function
from sympy import exp, I, pi, sympify
from math import log, ceil
# If verbose = True, vprint is the print function, otherwise it does nothing.
verbose = True
vprint = (print) if verbose else (lambda *a, **k: None)
# -----------------------------------------------------------------------------
def normal_form(d):
"""Return an array with the same elements as array d (i.e. complex numbers)
but make sure that their representation is in the form a + i*b (or close).
"""
return [sympify(d_i).expand(complex=True) for d_i in d]
# -----------------------------------------------------------------------------
def fft(n, a, depth=0):
"""Compute DFT_n(a) using FFT and print the tree of recursive calls.
Arguments:
n -- index of the DFT (at least length of a)
a -- array with the coefficients of the polynomial
depth -- recursion depth (needed for pretty printing, default: 0)
"""
# Check input.
if n < len(a):
raise ValueError("DFT index cannot be smaller than array length.")
if log(n,2) != ceil(log(n,2)):
raise ValueError("DFT index must be a power of 2.")
# Print function call.
vprint(" ", "│ " * depth, "┌─FFT(", n, ", ", a, ")", sep='')
# Base case (constant polynomial):
if len(a) == 1:
d = [a[0] for i in range(n)] # d: array of length n filled with a[0]
# Nontrivial case:
else:
a0 = [a[i] for i in range(0, len(a), 2)] # a0 = [a[0], a[2], ...]
a1 = [a[i] for i in range(1, len(a), 2)] # a1 = [a[1], a[3], ...]
d0 = fft(n/2, a0, depth+1) # d0 = DFT_{n/2}(a0)
d1 = fft(n/2, a1, depth+1) # d1 = DFT_{n/2}(a1)
w_n = exp(I*2*pi/n) # represented symbolically using SymPy
w = 1
d = [0 for i in range(n)] # d: array of length n (initialized with 0)
for k in range(n/2):
x = w * d1[k]
d[k] = d0[k] + x # p(ωₙᵏ) = p₀(ω½ₙᵏ) + ωₙᵏ·p₁(ω½ₙᵏ)
d[k+n/2] = d0[k] - x # p(ωₙᵏ⁺½ⁿ) = p₀(ω½ₙᵏ) − ωₙᵏ·p₁(ω½ₙᵏ)
w *= w_n
d = normal_form(d)
# Print function return.
vprint(" ", "│ " * depth, "└‣", d, sep='')
return d
# -----------------------------------------------------------------------------
def polymult(a, b=None):
"""Multiply two polynomials using FFT and print intermediate steps.
Arguments:
a -- array with the coefficients of the first polynomial
b -- array with the coefficients of the second polynomial (default: None)
If only one polynomial is given, it is multiplied with itself.
"""
vprint("\n", "─" * 35, "\n", " POLYNOMIAL MULTIPLICATION VIA FFT", sep='')
vprint("─" * 35, "\n", " a = ", a, "\n", " b = ", b if b else "a", sep='')
# Determine n, the minimum index for the DFT.
n = (len(a) + len(b) - 1) if b else (2 * len(a) - 1)
n = 2**int(ceil(log(n,2))) # Round up to next power of 2.
# 1. Evaluation:
vprint("\n", "1. Evaluation:", "\n ", "─" * 13, sep='')
d_a = fft(n, a) # d_a = DFT_n(a)
vprint("\n" if b else "", end='')
d_b = fft(n, b) if b else d_a # d_b = DFT_n(b)
# 2. Point-wise multiplication:
vprint("\n", "2. Point-wise multiplication:", "\n ", "─" * 28, sep='')
d = [d_a[i] * d_b[i] for i in range(n)] # d = d_a * d_b
d = normal_form(d)
vprint(d)
# 3. Interpolation:
vprint("\n", "3. Interpolation:", "\n ", "─" * 16, sep='')
f = fft(n, d) # f = DFT_n(d)
# Result: Reorder and divide by n.
r = [f[i]/n for i in range(1) + range(n-1, 0, -1)] # r = f[0,n-1,..,1] / n
vprint("\n", "Result:", "\n", " ", r, "\n", sep='')
return r
# -----------------------------------------------------------------------------
# If this script is called directly, compute square of p(x) = 2x³ − x² + 4x + 1
if __name__=="__main__":
n=3
a=[1,2,3]
y=[0]
for i in range(1,n+1):
y[i]=y[i-1]+a[i-1]
p1=[]
p2=[]
for i in range(1,y[n]+1):
p1[i]=0
for i in range(1,y[n]+y[n]+1):
p2[i]=0
for i in range(0,y[n]+1):
p1[y[i]]=1
p2[y[n]+y[i]]=1
polymult(p1,p2)
IyEvdXNyL2Jpbi9lbnYgcHl0aG9uCiMgLSotIGNvZGluZzogdXRmLTggLSotCgoiIiJQb2x5bm9taWFsIG11bHRpcGxpY2F0aW9uIHVzaW5nIGEgZmFzdCBGb3VyaWVyIHRyYW5zZm9ybSAoRkZUKSBhbGdvcml0aG0uCgpBdXRob3I6IEZhYmlhbiBSZWl0ZXIKVGVzdGVkIG9uIFVidW50dSAxMi4wNCB3aXRoIFB5dGhvbiAyLjcuMyBhbmQgU3ltUHkgMC43LjEucmMxCiIiIgoKZnJvbSBfX2Z1dHVyZV9fIGltcG9ydCBwcmludF9mdW5jdGlvbgpmcm9tIHN5bXB5IGltcG9ydCBleHAsIEksIHBpLCBzeW1waWZ5CmZyb20gbWF0aCBpbXBvcnQgbG9nLCBjZWlsCgojIElmIHZlcmJvc2UgPSBUcnVlLCB2cHJpbnQgaXMgdGhlIHByaW50IGZ1bmN0aW9uLCBvdGhlcndpc2UgaXQgZG9lcyBub3RoaW5nLgp2ZXJib3NlID0gVHJ1ZQp2cHJpbnQgPSAocHJpbnQpIGlmIHZlcmJvc2UgZWxzZSAobGFtYmRhICphLCAqKms6IE5vbmUpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRlZiBub3JtYWxfZm9ybShkKToKICAgICIiIlJldHVybiBhbiBhcnJheSB3aXRoIHRoZSBzYW1lIGVsZW1lbnRzIGFzIGFycmF5IGQgKGkuZS4gY29tcGxleCBudW1iZXJzKQogICAgYnV0IG1ha2Ugc3VyZSB0aGF0IHRoZWlyIHJlcHJlc2VudGF0aW9uIGlzIGluIHRoZSBmb3JtIGEgKyBpKmIgKG9yIGNsb3NlKS4KICAgICIiIgogICAgcmV0dXJuIFtzeW1waWZ5KGRfaSkuZXhwYW5kKGNvbXBsZXg9VHJ1ZSkgZm9yIGRfaSBpbiBkXQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpkZWYgZmZ0KG4sIGEsIGRlcHRoPTApOgogICAgIiIiQ29tcHV0ZSBERlRfbihhKSB1c2luZyBGRlQgYW5kIHByaW50IHRoZSB0cmVlIG9mIHJlY3Vyc2l2ZSBjYWxscy4KICAgIEFyZ3VtZW50czoKICAgIG4gICAgIC0tIGluZGV4IG9mIHRoZSBERlQgKGF0IGxlYXN0IGxlbmd0aCBvZiBhKQogICAgYSAgICAgLS0gYXJyYXkgd2l0aCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBwb2x5bm9taWFsCiAgICBkZXB0aCAtLSByZWN1cnNpb24gZGVwdGggKG5lZWRlZCBmb3IgcHJldHR5IHByaW50aW5nLCBkZWZhdWx0OiAwKQogICAgIiIiCiAgICAjIENoZWNrIGlucHV0LgogICAgaWYgbiA8IGxlbihhKToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKCJERlQgaW5kZXggY2Fubm90IGJlIHNtYWxsZXIgdGhhbiBhcnJheSBsZW5ndGguIikKICAgIGlmIGxvZyhuLDIpICE9IGNlaWwobG9nKG4sMikpOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoIkRGVCBpbmRleCBtdXN0IGJlIGEgcG93ZXIgb2YgMi4iKQogICAgIyBQcmludCBmdW5jdGlvbiBjYWxsLgogICAgdnByaW50KCIgIiwgIuKUgiAgIiAqIGRlcHRoLCAi4pSM4pSARkZUKCIsIG4sICIsICIsIGEsICIpIiwgc2VwPScnKQogICAgIyBCYXNlIGNhc2UgKGNvbnN0YW50IHBvbHlub21pYWwpOgogICAgaWYgbGVuKGEpID09IDE6CiAgICAgICAgZCA9IFthWzBdIGZvciBpIGluIHJhbmdlKG4pXSAgICAjIGQ6IGFycmF5IG9mIGxlbmd0aCBuIGZpbGxlZCB3aXRoIGFbMF0KICAgICMgTm9udHJpdmlhbCBjYXNlOgogICAgZWxzZToKICAgICAgICBhMCA9IFthW2ldIGZvciBpIGluIHJhbmdlKDAsIGxlbihhKSwgMildICAgICAgICMgYTAgPSBbYVswXSwgYVsyXSwgLi4uXQogICAgICAgIGExID0gW2FbaV0gZm9yIGkgaW4gcmFuZ2UoMSwgbGVuKGEpLCAyKV0gICAgICAgIyBhMSA9IFthWzFdLCBhWzNdLCAuLi5dCiAgICAgICAgZDAgPSBmZnQobi8yLCBhMCwgZGVwdGgrMSkgICAgICAgICAgICAgICAgICAgICAjIGQwID0gREZUX3tuLzJ9KGEwKQogICAgICAgIGQxID0gZmZ0KG4vMiwgYTEsIGRlcHRoKzEpICAgICAgICAgICAgICAgICAgICAgIyBkMSA9IERGVF97bi8yfShhMSkKICAgICAgICB3X24gPSBleHAoSSoyKnBpL24pICAgICAgICAgIyByZXByZXNlbnRlZCBzeW1ib2xpY2FsbHkgdXNpbmcgU3ltUHkKICAgICAgICB3ID0gMQogICAgICAgIGQgPSBbMCBmb3IgaSBpbiByYW5nZShuKV0gICAjIGQ6IGFycmF5IG9mIGxlbmd0aCBuIChpbml0aWFsaXplZCB3aXRoIDApCiAgICAgICAgZm9yIGsgaW4gcmFuZ2Uobi8yKToKICAgICAgICAgICAgeCA9IHcgKiBkMVtrXQogICAgICAgICAgICBkW2tdID0gZDBba10gKyB4ICAgICAgICAgICAgICAjICAgIHAoz4nigpnhtY8pID0gcOKCgCjPicK94oKZ4bWPKSArIM+J4oKZ4bWPwrdw4oKBKM+Jwr3igpnhtY8pCiAgICAgICAgICAgIGRbaytuLzJdID0gZDBba10gLSB4ICAgICAgICAgICMgcCjPieKCmeG1j+KBusK94oG/KSA9IHDigoAoz4nCveKCmeG1jykg4oiSIM+J4oKZ4bWPwrdw4oKBKM+Jwr3igpnhtY8pCiAgICAgICAgICAgIHcgKj0gd19uCiAgICAgICAgZCA9IG5vcm1hbF9mb3JtKGQpCiAgICAjIFByaW50IGZ1bmN0aW9uIHJldHVybi4KICAgIHZwcmludCgiICIsICLilIIgICIgKiBkZXB0aCwgIuKUlOKAoyIsIGQsIHNlcD0nJykKICAgIHJldHVybiBkCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRlZiBwb2x5bXVsdChhLCBiPU5vbmUpOgogICAgIiIiTXVsdGlwbHkgdHdvIHBvbHlub21pYWxzIHVzaW5nIEZGVCBhbmQgcHJpbnQgaW50ZXJtZWRpYXRlIHN0ZXBzLgogICAgQXJndW1lbnRzOgogICAgYSAtLSBhcnJheSB3aXRoIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIGZpcnN0IHBvbHlub21pYWwKICAgIGIgLS0gYXJyYXkgd2l0aCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBzZWNvbmQgcG9seW5vbWlhbCAoZGVmYXVsdDogTm9uZSkKICAgIElmIG9ubHkgb25lIHBvbHlub21pYWwgaXMgZ2l2ZW4sIGl0IGlzIG11bHRpcGxpZWQgd2l0aCBpdHNlbGYuCiAgICAiIiIKICAgIHZwcmludCgiXG4iLCAi4pSAIiAqIDM1LCAiXG4iLCAiIFBPTFlOT01JQUwgTVVMVElQTElDQVRJT04gVklBIEZGVCIsIHNlcD0nJykKICAgIHZwcmludCgi4pSAIiAqIDM1LCAiXG4iLCAiIGEgPSAiLCBhLCAiXG4iLCAiIGIgPSAiLCBiIGlmIGIgZWxzZSAiYSIsIHNlcD0nJykKICAgICMgRGV0ZXJtaW5lIG4sIHRoZSBtaW5pbXVtIGluZGV4IGZvciB0aGUgREZULgogICAgbiA9IChsZW4oYSkgKyBsZW4oYikgLSAxKSBpZiBiIGVsc2UgKDIgKiBsZW4oYSkgLSAxKQogICAgbiA9IDIqKmludChjZWlsKGxvZyhuLDIpKSkgICAgICAgICAgICAgICAgICAgIyBSb3VuZCB1cCB0byBuZXh0IHBvd2VyIG9mIDIuCiAgICAjIDEuIEV2YWx1YXRpb246CiAgICB2cHJpbnQoIlxuIiwgIjEuIEV2YWx1YXRpb246IiwgIlxuICIsICLilIAiICogMTMsIHNlcD0nJykKICAgIGRfYSA9IGZmdChuLCBhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBkX2EgPSBERlRfbihhKQogICAgdnByaW50KCJcbiIgaWYgYiBlbHNlICIiLCBlbmQ9JycpCiAgICBkX2IgPSBmZnQobiwgYikgaWYgYiBlbHNlIGRfYSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZF9iID0gREZUX24oYikKICAgICMgMi4gUG9pbnQtd2lzZSBtdWx0aXBsaWNhdGlvbjoKICAgIHZwcmludCgiXG4iLCAiMi4gUG9pbnQtd2lzZSBtdWx0aXBsaWNhdGlvbjoiLCAiXG4gIiwgIuKUgCIgKiAyOCwgc2VwPScnKQogICAgZCA9IFtkX2FbaV0gKiBkX2JbaV0gZm9yIGkgaW4gcmFuZ2UobildICAgICAgICAgICAgICAgICAgICAjIGQgPSBkX2EgKiBkX2IKICAgIGQgPSBub3JtYWxfZm9ybShkKQogICAgdnByaW50KGQpCiAgICAjIDMuIEludGVycG9sYXRpb246CiAgICB2cHJpbnQoIlxuIiwgIjMuIEludGVycG9sYXRpb246IiwgIlxuICIsICLilIAiICogMTYsIHNlcD0nJykKICAgIGYgPSBmZnQobiwgZCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGYgPSBERlRfbihkKQogICAgIyBSZXN1bHQ6IFJlb3JkZXIgYW5kIGRpdmlkZSBieSBuLgogICAgciA9IFtmW2ldL24gZm9yIGkgaW4gcmFuZ2UoMSkgKyByYW5nZShuLTEsIDAsIC0xKV0gICMgciA9IGZbMCxuLTEsLi4sMV0gLyBuCiAgICB2cHJpbnQoIlxuIiwgIlJlc3VsdDoiLCAiXG4iLCAiICIsIHIsICJcbiIsIHNlcD0nJykKICAgIHJldHVybiByCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgSWYgdGhpcyBzY3JpcHQgaXMgY2FsbGVkIGRpcmVjdGx5LCBjb21wdXRlIHNxdWFyZSBvZiBwKHgpID0gMnjCsyDiiJIgeMKyICsgNHggKyAxCmlmIF9fbmFtZV9fPT0iX19tYWluX18iOgogICAgbj0zCglhPVsxLDIsM10KCXk9WzBdCglmb3IgaSBpbiByYW5nZSgxLG4rMSk6CgkJeVtpXT15W2ktMV0rYVtpLTFdCglwMT1bXQoJcDI9W10KCWZvciBpIGluIHJhbmdlKDEseVtuXSsxKToKCQlwMVtpXT0wCglmb3IgaSBpbiByYW5nZSgxLHlbbl0reVtuXSsxKToKCQlwMltpXT0wCglmb3IgaSBpbiByYW5nZSgwLHlbbl0rMSk6CgkJcDFbeVtpXV09MQoJCXAyW3lbbl0reVtpXV09MQoJcG9seW11bHQocDEscDIpCiAgICA=