Os Complex numbers não são complicados.
Só precisam de um nome menos assustador.
São tão úteis, sobretudo em engenharia e ciência, que o Python inclui complex como tipo numérico padrão, a par dos inteiros (ints) e dos números de vírgula flutuante (floats).
Um valor complex em Python é, essencialmente, um par de números de vírgula flutuante.
Estas chamam-se as partes "real" e "imaginária", por razões históricas infelizes.
Mais uma vez, o melhor é focarmo-nos na simplicidade subjacente e não nos nomes estranhos.
Há duas formas comuns de criar números complexos.
complex(real, imag) recebe dois parâmetros do tipo float:>>> z1 = complex(1.5, 2.0)
>>> z1
(1.5+2j)
O construtor também consegue interpretar uma string de entrada. Tem a limitação estranha de falhar se a string contiver espaços.
>>> 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 apenas <complex part>j se a parte real for zero:>>> z2 = 2.0 + 1.5j
>>> z2
(2+1.5j)
O resultado final é idêntico ao de usar o construtor complex().
Há duas regras para a parte imaginária do número complexo:
É designada por j (e não i, como podes ver nos manuais de matemática).
O j tem de vir imediatamente a seguir a um número, para o Python não o interpretar como o nome de uma variável. Se for preciso, usa 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'>
A maioria dos engenheiros dá-se bem com o j.
A maioria dos cientistas e matemáticos prefere a notação matemática i para imaginário, mas essa notação entra em conflito com o uso de i para designar corrente em Engenharia Eletrotécnica.
Por isso, ao criar o Python, foram os engenheiros eletrotécnicos que ganharam.
Para aceder às partes de um número complexo individualmente:
>>> z2.real
2.0
>>> z2.imag
1.5
Qualquer das partes pode ser zero, e os matemáticos podem então falar de um número "totalmente real" ou "totalmente imaginário". No entanto, em Python continua a ser um número complexo:
>>> complex(0, 1)
1j
>>> type(complex(0, 1))
<class 'complex'>
>>> complex(1, 0)
(1+0j)
Podes ter ouvido dizer que "i (ou j) é a raiz quadrada de -1".
Por agora, tudo o que isto significa é que a parte imaginária satisfaz, por definição, a igualdade
1j * 1j == -1 # => True
É uma ideia simples, mas tem consequências interessantes.
A maioria dos operators usados com floats e inteiros também funciona com números complexos:
>>> 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)
Explicar as regras da multiplicação e da divisão de números complexos está fora do âmbito deste conceito (e é pouco provável que tenhas de fazer essas operações "à mão" com muita frequência).
Qualquer introdução matemática ou de engenharia eletrotécnica aos números complexos aborda isto, caso queiras aprofundar o tema.
Em alternativa, o Exercism tem um exercício de prática Complex Numbers onde podes implementar uma classe de números complexos com estas operações, a partir dos primeiros princípios.
A divisão inteira não é possível com números complexos, por isso os operadores // e % e a função divmod() falham com o tipo de número complexo.
Há duas funções implementadas para tipos numéricos que são muito úteis quando trabalhas com números complexos:
<complex number>.conjugate() simplesmente inverte o sinal da parte imaginária de um número complexo (de + para -, ou vice-versa).
abs(<complex number>) garante que devolve um número real sem parte imaginária.>>> 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
A biblioteca padrão do Python tem um módulo math cheio de funcionalidades úteis para trabalhar com números reais.
Também tem um módulo equivalente, cmath, para trabalhar com números complexos.
Encorajamos-te a ler o módulo e a experimentar, mas as principais categorias são:
Eis um exemplo que usa algumas 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
Assim, uma expressão simples com três das constantes mais importantes da natureza, e, i (ou j) e pi, dá o resultado -1.
Há quem acredite que este é o resultado mais belo de toda a matemática.
Remonta a cerca de 1740.
Podes saltar esta parte, a não ser que tenhas interesse.
Era estranho e novo no século XVI.
500 anos mais tarde, é central para a maior parte da engenharia e das ciências físicas.
Acontece que os números complexos são a forma mais simples de descrever tudo o que roda ou tudo o que tem uma propriedade ondulatória. Por isso são amplamente usados em engenharia eletrotécnica, processamento de áudio, física, jogos de computador e navegação, para só referir algumas aplicações.
Consegues ver coisas a rodar. Os números complexos podem não fazer o mundo girar, mas são ótimos para explicar o que acontece quando o mundo gira: olha para qualquer imagem de satélite de uma grande tempestade.
De forma menos óbvia, o som é ondulatório, a luz é ondulatória, os sinais de rádio são ondulatórios, e até a economia do teu país é, pelo menos em parte, ondulatória.
Muito deste processamento de ondas pode ser feito com funções trigonométricas (sin() e cos()), mas isso torna-se confuso muito depressa.
As exponenciais complexas são muito mais fáceis de usar.
Só é verdade se viveres numa caverna e andares a procurar comida.
Se estás a ler isto em qualquer tipo de ecrã, dependes completamente de alguns avanços úteis do século XX, alcançados através do uso de números complexos.
Chips semicondutores.
O algoritmo da Transformada Rápida de Fourier (FFT).
A FFT é uma aplicação dos números complexos, e está em tudo o que esteja ligado à transmissão de som, ao processamento de áudio, às fotografias e ao vídeo.
O MP3 e outros formatos de áudio usam a FFT para compressão, garantindo que cabe mais áudio num espaço de armazenamento menor.
A compressão JPEG e o vídeo MP4, entre muitos outros formatos de imagem e vídeo, também usam a FFT para compressão.
A FFT é também usada nos filtros digitais que permitem às torres de telemóvel separar o teu sinal pessoal do de todas as outras pessoas.
Por isso, provavelmente usas tecnologia que depende de cálculos com números complexos milhares de vezes por segundo.