Transformée de Fourier discrète

Transformée de Fourier discrète

algebra

On dispose en mathématiques de quatre opérations dites élémentaires : l’addition, la soustraction, la division et donc la multiplication. On sait tous multiplier deux entiers en base : il suffit de faire la multiplication de chaque chiffre du multiplicateur par chaque chiffre du multiplicande, puis d’additionner le tout. Pour deux nombres de taille , cela donne un algorithme de complexité . Mais dès que l’on veut multiplier de très grands chiffres (en informatique par exemple), cet algorithme montre très vite ses limites. Nous allons étudier ici le cas des polynômes en donnant un algorithme de multiplication utilisant la transformée de Fourier rapide.

Transformée de Fourier discrète sur

Définitions

L’idée va être d’identifier les polynômes de degré inférieur à de la forme au vecteur de . On fixe, pour toute la suite, une racine primitive -ième de l’unité.

Définition 1. On appelle transformée de Fourier discrète l’application

Remarque 2. Si est le polynôme associé au vecteur , on a fait que nous utiliserons plusieurs fois dans la suite.

Dans un premier temps, on peut écrire l’algorithme de calcul suivant.

Algorithme 3.

# Exponentiation rapide. Calcul x^n.
def power(x, n):
    if n == 0:
        return 1
    if n == 1:
        return x
    if n % 2 == 0:
        return power(x*x, n//2)
    return x * power(x*x, (n-1)//2)

# Algorithme naïf pour calculer la transformée de Fourier rapide de f.
def naive_dft(f, omega):
    n = len(f)
    return [sum(f[k] * power(omega, i*k) for k in range(n)) for i in range(n)]

Définition 4. Le produit de convolution de deux vecteurs et de est le vecteur de noté défini par

Exemple 5. et .

Propriétés

Proposition 6. Soient , deux vecteurs de . On pose ainsi que , et les polynômes associés respectivement à , et . Modulo , on a :

Démonstration. Écrivons : Comme est au plus de degré , on a (par abus de notation). Maintenant avec et , on a : En passant modulo ,  ◻

Théorème 7. est un isomorphisme d’algèbres entre et dont la matrice dans la base canonique est la matrice de Vandermonde : (où est le produit sur effectué composante par composante.)

Démonstration. Soient et deux vecteurs de .

  • , est bien une application linéaire.

  • Soit . On note , et les polynômes respectivement associés à , et .

    Soit . Par la Proposition 6, on a . Ainsi, Or, le -ième coefficient de est Donc , et ainsi, est bien un morphisme d’algèbres.

  • Clairement, donc la matrice dans la base canonique de est . Or, les sont distincts deux-à-deux, donc est inversible (car de déterminant non nul, en vertu de la formule de Vandermonde).

 ◻

Proposition 8. L’inverse de est donné par .

Démonstration. Déjà, remarquons que si est une racine primitive -ième de l’unité, alors aussi :

  • .

  • Et si on a tel que , alors , ce qui est absurde.

Il suffit donc de montrer que . Soient . En notant le coefficient à la -ième ligne et à la -ième colonne de , on a : Ce qu’on voulait. ◻

Application à la multiplication de polynômes

Notre but ici va être de trouver un moyen de calculer la transformée de Fourier discrète d’un polynôme de manière efficace, puis d’en déduire un algorithme de multiplication de deux polynômes.

Proposition 9. Soit , soit une racine -ième de l’unité et soit de degré inférieur ou égal à . On suppose qu’il existe et tels que et on pose et . Alors

Démonstration. Écrivons . On a donc Soit , on évalue en : et la deuxième égalité s’obtient par un calcul similaire (en utilisant le fait que ). ◻

Proposition 10. Si est une racine primitive -ième de l’unité, alors est une racine primitive -ième de l’unité.

Démonstration. Clairement, est une racine -ième de l’unité. Maintenant, si , alors  ◻

On déduit de la Proposition 9 et de la Proposition 10 un algorithme récursif de calcul de qui a une complexité de .

Algorithme 11.

# Renvoie les indices impairs d'une liste.
def odd_indices(l):
  return [l[2*k+1] for k in range(len(l)//2)]

# Fusionne deux listes de longueur égale en alternant les termes.
def alternate_merge(l1, l2):
  return [val for pair in zip(l1, l2) for val in pair]

# Calcule et renvoie les puissances successives de la racine primitive n-ième omega.
def primitive_root_powers(omega, n):
  return [power(omega, k) for k in range(n)]

# Cette fonction renvoie la transformée de Fourier discrète de F en omega. La liste l demandée est la liste des puissances de omega.
def fft(F, m, l):
  n = m + 1
  if n == 1:
    return [0] if F == 0 else [F.list()[0]]
  (F1, F0) = F.quo_rem(X^(n/2))
  R0 = F0+F1
  R1 = F0-F1
  l2 = odd_indices(l)
  return alternate_merge(fft(R0, n/2-1, l2), fft(R1.substitute(X=l[1]*X), n/2-1, l2))

Théorème 12. Soient et deux polynômes de degré strictement inférieur à dont on note et les vecteurs de associés. Alors est le polynôme associé au vecteur .

Démonstration. Comme , on a par la Proposition 6. Par le Théorème 7, on a Et par la Proposition 8. On obtient le résultat voulu. ◻

En utilisant l’algorithme écrit précédemment, on peut donc écrire un nouvel algorithme permettant de calculer le produit de deux polynômes de degré strictement inférieur à en .

Algorithme 13.

# Multiplie deux polynômes de degré n.
def fast_polynomial_multiply(F, G, n, omega):
  l1 = primitive_root_powers(omega, n)
  l2 = primitive_root_powers(power(omega, n-1), n)
  prod = [fft(F, n-1, l1)[i] * fft(G, n-1, l1)[i] for i in range(n)]
  h = fft(sum(prod[k] * X^k for k in range(len(prod))), n-1, l2)
  return sum(1/n * h[k] * X^k for k in range(len(h)))

Remarque 14. On pourrait imaginer un algorithme calculant le produit de deux polynômes de degrés quelconques et sur le même modèle en considérant et comme des polynômes de degré est tel que . Il suffit ensuite de choisir racine primitive -ième de l’unité.