Les Complex numbers ne sont pas compliqués.
Ils ont juste besoin d'un nom moins effrayant.
Ils sont si utiles, en particulier en ingénierie et en science, que Python inclut complex comme type numérique standard, aux côtés des entiers (ints) et des nombres à virgule flottante (floats).
Une valeur complex en Python est essentiellement une paire de nombres à virgule flottante.
On les appelle les parties « réelle » et « imaginaire », pour de malheureuses raisons historiques.
Là encore, mieux vaut se concentrer sur la simplicité sous-jacente plutôt que sur ces noms étranges.
Il y a deux façons courantes de créer des nombres complexes.
complex(real, imag) prend deux paramètres float :>>> z1 = complex(1.5, 2.0)
>>> z1
(1.5+2j)
Le constructeur peut aussi analyser une string en entrée. Cela présente l'étrange limitation d'échouer si la string contient des espaces.
>>> complex('4+2j')
(4+2j)
>>> complex('4 + 2j')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: complex() arg is a malformed string
<real part> + <complex part>j, ou simplement <complex part>j si la partie réelle est nulle :>>> z2 = 2.0 + 1.5j
>>> z2
(2+1.5j)
Le résultat final est identique à celui obtenu avec le constructeur complex().
Deux règles régissent la partie imaginaire d'un nombre complexe :
Elle est désignée par j (et non par i, comme on peut le voir dans les manuels de mathématiques).
Le j doit suivre immédiatement un nombre, sinon Python le prend pour un nom de variable. Au besoin, utilise 1j.
>>> j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'j' is not defined
>>> 1j
1j
>>> type(1j)
<class 'complex'>
La plupart des ingénieurs se contentent de j.
La plupart des scientifiques et des mathématiciens préfèrent la notation mathématique i pour imaginaire, mais cette notation entre en conflit avec l'usage de i pour désigner le courant en génie électrique.
C'est donc l'ingénierie électrique qui l'a emporté au moment de concevoir Python.
Pour accéder séparément aux parties d'un nombre complexe :
>>> z2.real
2.0
>>> z2.imag
1.5
L'une ou l'autre partie peut être nulle, et les mathématiciens parleront alors d'un nombre « purement réel » ou « purement imaginaire ». Il n'en reste pas moins un nombre complexe pour Python :
>>> complex(0, 1)
1j
>>> type(complex(0, 1))
<class 'complex'>
>>> complex(1, 0)
(1+0j)
Tu as peut-être entendu dire que « i (ou j) est la racine carrée de -1 ».
Pour l'instant, tout ce que cela signifie, c'est que la partie imaginaire par définition vérifie l'égalité
1j * 1j == -1 # => True
C'est une idée simple, mais elle mène à des conséquences intéressantes.
La plupart des operators utilisés avec les nombres à virgule flottante et les entiers fonctionnent aussi avec les nombres complexes :
>>> z1 = (1.5+2j)
>>> z2 = (2+1.5j)
>>> z1 + z2 # addition
(3.5+3.5j)
>>> z1 - z2 # subtraction
(-0.5+0.5j)
>>> z1 * z2 # multiplication
6.25j
>>> z1 / z2 # division
(0.96+0.28j)
>>> z1 ** 2 # exponentiation
(-1.75+6j)
>>> 2 ** z1 # another exponentiation
(0.5188946835878313+2.7804223253571183j)
>>> 1j ** 2 # j * j == -1
(-1+0j)
Expliquer les règles de multiplication et de division des nombres complexes sort du cadre de ce concept (et tu auras rarement besoin d'effectuer ces opérations « à la main »).
Toute introduction mathématique ou au génie électrique aux nombres complexes couvrira ces points, si tu veux creuser le sujet.
Sinon, Exercism propose un exercice d'entraînement Complex Numbers dans lequel tu peux implémenter une classe de nombres complexes avec ces opérations, à partir des principes de base.
La division entière n'est pas possible sur les nombres complexes, donc les opérateurs // et % ainsi que la fonction divmod() échoueront pour le type complexe.
Deux fonctions implémentées pour les types numériques sont très utiles quand on travaille avec des nombres complexes :
<complex number>.conjugate() change simplement le signe de la partie imaginaire d'un nombre complexe (de + vers -, ou inversement).
abs(<complex number>) renvoie à coup sûr un nombre réel sans partie imaginaire.>>> z1
(1.5+2j)
>>> z1.conjugate() # flip the z1.imag sign
(1.5-2j)
>>> abs(z1) # sqrt(z1.real ** 2 + z1.imag ** 2)
2.5
cmath
La bibliothèque standard de Python contient un module math plein de fonctionnalités utiles pour travailler avec des nombres réels.
Elle contient aussi un module cmath équivalent pour travailler avec les nombres complexes.
On t'encourage à parcourir le module et à faire des expériences, mais les principales catégories sont :
Voici un exemple qui utilise quelques constantes :
>>> import cmath
>>> euler = cmath.exp(1j * cmath.pi) # Euler's equation
>>> euler.real
-1.0
>>> round(euler.imag, 15) # round to 15 decimal places
0.0
Ainsi, une expression simple faisant intervenir trois des constantes les plus importantes de la nature, e, i (ou j) et pi, donne le résultat -1.
Certains considèrent que c'est le plus beau résultat de toutes les mathématiques.
Il remonte aux environs de 1740.
Cette partie peut être ignorée, sauf si elle t'intéresse.
C'était étrange et nouveau au XVIe siècle.
500 ans plus tard, c'est au cœur de la plupart des branches de l'ingénierie et des sciences physiques.
Il se trouve que les nombres complexes sont la façon la plus simple de décrire tout ce qui tourne ou tout ce qui présente un caractère ondulatoire. Ils sont donc largement utilisés en génie électrique, en traitement audio, en physique, dans le jeu vidéo et en navigation, pour ne citer que quelques applications.
Tu peux voir des choses tourner. Les nombres complexes ne font peut-être pas tourner le monde, mais ils excellent à expliquer ce qui se passe quand le monde tourne : regarde n'importe quelle image satellite d'une grande tempête.
De façon moins évidente, le son est ondulatoire, la lumière est ondulatoire, les signaux radio sont ondulatoires, et même l'économie de ton pays est au moins en partie ondulatoire.
Une grande partie de ce traitement ondulatoire peut se faire avec des fonctions trigonométriques (sin() et cos()), mais cela devient très vite brouillon.
Les exponentielles complexes sont beaucoup plus faciles à manipuler.
Ce n'est vrai que si tu vis dans une grotte et que tu cherches ta nourriture.
Si tu lis ceci sur un écran, quel qu'il soit, tu dépends entièrement de quelques avancées utiles du XXe siècle rendues possibles par l'usage des nombres complexes.
Les puces à semi-conducteurs.
L'algorithme de la transformée de Fourier rapide (FFT).
La FFT est une application des nombres complexes, et elle est présente dans tout ce qui touche à la transmission du son, au traitement audio, aux photos et à la vidéo.
Le MP3 et d'autres formats audio utilisent la FFT pour la compression, ce qui permet de faire tenir plus d'audio dans un espace de stockage plus réduit.
La compression JPEG et la vidéo MP4, parmi bien d'autres formats d'image et de vidéo, utilisent elles aussi la FTT pour la compression.
La FFT est aussi mise à l'œuvre dans les filtres numériques qui permettent aux antennes de téléphonie mobile de distinguer ton signal personnel de celui de tous les autres.
Donc, tu utilises probablement une technologie qui repose sur des calculs de nombres complexes des milliers de fois par seconde.