Os Complex numbers não são complicados.
Só precisam de um nome menos alarmante.
São tão úteis, especialmente em engenharia e ciência, que o R inclui os números complexos como tipos atómicos padrão, a par de integer, double, logical, character e raw.
Um número complex em R é um único valor.
No entanto, na prática, pode ser visto como um par de números.
Chamam-se partes "real" e "imaginária", por razões históricas infelizes.
Mais uma vez, é melhor concentrarmo-nos na simplicidade subjacente e não nos nomes estranhos.
Para criar números complexos a partir de dois números reais, basta adicionar o sufixo i à parte imaginária.
2+3i
#> [1] 2+3i
typeof(2+3i)
#> [1] "complex"
Para criar um número complexo a partir de variáveis reais, a sintaxe acima não funciona.
Escrever r + imi confunde o analisador sintático, levando-o a pensar que imi é um nome de variável (inexistente).
Ou multiplica a parte imaginária por 1i ou usa a função [complex()][ref-complex]:
r <- 2
im <- 3
r + im * 1i
#> [1] 2+3i
# must include the `real` and `imaginary` keywords
complex(real = r, imaginary = im)
#> [1] 2+3i
Talvez te perguntes: porque i e não j?
A maioria dos engenheiros prefere 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 significar corrente em Engenharia Eletrotécnica.
O R escolheu i, o Python escolheu j e a Julia evitou a polémica ao escolher im.
Tem cuidado ao alternar entre linguagens de programação [CTEM][wiki-STEM].
Para aceder individualmente às partes de um número complexo:
z <- 2+3i
Re(z) # real part
#> [1] 2
Im(z) # imaginary part
#> [1] 3
Qualquer das partes pode ser zero e os matemáticos podem então falar do número ser "totalmente real" ou "totalmente imaginário". No entanto, continua a ser um número complexo em R.
1.2 + 0i
#> [1] 1.2+0i
typeof(1.2 + 0i)
#> [1] "complex"
2i
#> [1] 0+2i
typeof(2i)
#> [1] "complex"
Talvez já tenhas ouvido que "i (ou j) é a raiz quadrada de -1".
Para já, tudo isto significa apenas que a parte imaginária por definição satisfaz a seguinte igualdade:
1i * 1i == -1 # more precisely, 1i * 1i == -1 + 0i
#> [1] TRUE
É uma ideia simples, mas conduz a consequências interessantes.
A maioria dos operadores usados com números de vírgula flutuante e inteiros também funciona com números complexos:
z1 = 1.5 + 2i
z2 = 2 + 1.5i
z1 + z2 # addition
#> [1] 3.5+3.5i
z1 * z2 # multiplication
#> [1] 0+6.25i
z1 / z2 # division
#> [1] 0.96+0.28i
z1^2 # exponentiation
#> [1] -1.75+6i
2^z1 # another exponentiation
#> [1] 0.518895+2.780422i
A maioria das funções matemáticas funciona com valores de entrada complexos.
Existem várias funções, para além de Re() e Im(), com particular relevância para números complexos.
Conj() simplesmente inverte o sinal da parte imaginária de um número complexo (de + para - ou vice-versa).
abs(<complex number>) tem a garantia de devolver um número real sem parte imaginária (a sua "magnitude"), e Mod(<complex number>) é um sinónimo.Arg(<complex number>) devolve o ângulo de fase em radianos.z1
#> [1] 1.5+2i
abs(z1)
#> [1] 2.5
Mod(z1) # same as previous
#> [1] 2.5
Conj(z1) # flip sign
#> [1] 1.5-2i
Arg(z1)
#> [1] 0.9272952
Acabaste de ser contratado por um novo serviço de táxis que está a tentar distinguir-se de outras ofertas num mercado já saturado. Para o conseguir, o CEO decidiu oferecer um serviço de ultra-luxo em Manhattan, com assistentes em cada cruzamento para ajudar os clientes antes e depois da viagem. Agora, precisas de escrever algum código que ajude nas operações do serviço.
O teu CEO decidiu que, como Manhattan tem um traçado de ruas sobretudo em grelha, os números complexos são uma forma conveniente de representar os cruzamentos das ruas. Assim, os números reais positivos e negativos representam este e oeste, e os números imaginários positivos e negativos representam norte e sul, respetivamente.
Como instanciar um número complexo em R é um pouco, hmm... único, foi-te pedido que implementes uma função simples de número complexo.
Implementa a função simplex(a, b), que recebe os inteiros a e b.
Devolve o número complexo a+bi.
simplex(1, 1)
#> 1+1i
simplex(-3, 4)
#> -3+4i
Quando um cliente indica o cruzamento de destino, os teus motoristas recebem direções sobre como lá chegar. Estas direções vêm na forma de um número complexo, em que a parte real representa quantos quarteirões a norte/sul percorrer e a parte imaginária representa a distância a este/oeste. Para ficar alinhado com uma bússola canónica, o teu gestor de projeto decidiu que o norte fica na direção imaginária positiva e o sul na direção imaginária negativa. Da mesma forma, o este fica na direção real positiva e o oeste na direção real negativa.
Implementa a função driver_directions(start, end), que recebe as coordenadas do cruzamento inicial (start) e as coordenadas do destino (end).
Devolve um número complexo que representa quantos quarteirões a este/oeste + norte/sul o motorista tem de percorrer.
driver_directions(0+0i, 7+5i)
#> 7+5i
driver_directions(-2+3i, 4-5i)
#> 6-8i
O cliente paga uma taxa pela distância que o táxi tem de percorrer. Como Manhattan tem um traçado de ruas em grelha, os táxis só podem circular por essas vias. Esta estrutura dá origem a uma medida de distância que é obrigada a seguir as linhas da grelha. A distância pode ser obtida somando o número de linhas percorridas ou o número de cruzamentos alcançados (ou seja, sem contar o cruzamento de partida). Esta medida é habitualmente chamada de distância de Manhattan ou norma L1.
Implementa a função manhattan(start, end), que recebe os cruzamentos start e end.
Devolve a distância de Manhattan entre os cruzamentos start e end.
manhattan(0+0i, 3+4i)
#> 7
manhattan(-2-3i, -1+1i)
#> 5
O CEO conseguiu convencer os bancos de investimento ao destacar que o serviço não é só de ultra-luxo, mas também ecológico, porque vai usar corvos treinados para transportar mensagens entre os assistentes nos cruzamentos (que transmitem informações sobre os passageiros para uma experiência personalizada). Parece que nenhum dos banqueiros de investimento se preocupou em averiguar devidamente porque é que isto é amigo do ambiente, ou sequer que tipo de CEO gosta de treinar corvos nos tempos livres, porque o dinheiro dos investimentos tem entrado a rodos.
Este serviço também é cobrado pela distância percorrida. No entanto, como os corvos conseguem voar, não têm de seguir a rede de estradas em grelha e podem seguir o caminho mais curto. Claro que isto pode não ser o caso quando há arranha-céus enormes, mas o CEO vai lançar este serviço em Manhattan, no Kansas (também conhecida como "The Little Apple"), onde os corvos conseguem facilmente sobrevoar os edifícios baixos. A procura potencial de um serviço assim em Manhattan, no Kansas, e o que os peões locais vão achar de todos estes pássaros a voar por cima são questões para outro dia.
Implementa a função as_crow_flies(start, end), que recebe os cruzamentos start e end.
Devolve a distância a voo de pássaro entre os dois.
as_crow_flies(0+0i, 3+4i)
#> 5
O teu CEO treinou os corvos para seguirem tipos de direções semelhantes às dos motoristas. No entanto, o CEO adora o Hemisfério Sul e treinou os corvos com uma bússola diferente da que o teu gestor de projeto usa para o resto do sistema. O teu CEO está a revelar-se uma pessoa bastante excêntrica. A bússola do CEO tem o norte na direção imaginária negativa e o sul na direção imaginária positiva. Isto é o oposto das direções norte/sul dos motoristas, enquanto este/oeste permanecem iguais.
Implementa a função crows_directions(start, end), que recebe os cruzamentos start e end.
Devolve o número complexo análogo às direções do motorista, mas com norte/sul invertidos.
driver_directions(0+0i, 7+5i)
#> 7-5i
driver_directions(-2+3i, 4-5i)
#> 6+8i
Inscreve-te no Exercism para aprenderes e dominares R com 21 conceitos111 exercícios, e mentoria humana real, tudo grátis.