Класс комплексных чисел

В этом листке мы полностью реализуем класс комплексных чисел. Обращайтесь к статье в википедии для подробного текста. Чуть ниже будет базовое напоминание.

Вообще в питоне уже есть встроенная поддержка комплексных чисел. Но она поддерживает только коэффициенты типа float. Мы же сделаем реализацию, поддерживающую коэффициенты типа int, float, Decimal, Fraction . Это позволит выполнять вычисления с большим числов знаков или точные вычисления. Основные сложности как раз будут при взаимодействиях с классами Decimal и Fraction и между ними.

Ликбез по комплексным числам.

Определение

Комплексное число $z$ — это выражение вида $z=a+bi$, где $a,\ b\in\R$, а $i$ — мнимая единица. По определению $i^2=-1$. Число $a$ называют вещественной частью $z$ (пишут $a={\rm Re}\,(z)$), а число $b$ — мнимой частью $z$ (пишут $b={\rm Im}\,(z)$). Комплексные числа складывают и умножают, «раскрывая скобки и приводя подобные». Множество комплексных чисел обозначают буквой $\mathbb{C}$.

Комплексные числа и вектора на плоскости

Каждому комплексному числу $z=a+bi$ сопоставим точку $(a,b)$ и вектор с координатами $(a,b)$. Длина этого вектора называется модулем комплексного числа $z$ и обозначается $|z|$.
Пусть $z\ne0$. Угол (в радианах), отсчитанный против часовой стрелки от вектора $(1,0)$ до вектора $(a,b)$, называется аргументом комплексного числа $z$ и обозначается ${\rm Arg}\,(z)$. Аргумент определен с точностью до прибавления числа вида $2\pi n$, где $n\in\Z$.

Тригонометрическая форма записи

Для любого ненулевого комплексного числа $z$ имеет место равенство $z=r (\cos \varphi +i\sin \varphi )$, где $r=|z|$, $\varphi={\rm Arg}\,(z)$.

Формула Муавра

Пусть $z=r(\cos\varphi+i\sin\varphi)$, $n\in\N$. Тогда для любого целого $n$ выполнено равенство $z^n=r^n(\cos n\varphi+i\sin n\varphi)$.

Формула Эйлера

Оказывается, $e^{i\varphi}=\cos\varphi+i\sin\varphi$. Можно использовать выражение $e^{i\varphi}$ как короткое и удобное обозначение для $\cos\varphi+i\sin\varphi$. Однако удивительная часть этой формулы состоит в том, что вместо $\varphi$ можно подставлять не только вещественные числа, но и комплексные. Правда тождество от этого перестанет быть в полной мере честным из-за возможной многозначности. После этого путём нехитрых арифметических преобразований можно научиться возводить сначала вещественное число в любую степень, а потом любое комплексное число в любую комплексную степень. А также вычислять синус и косинус комплексных чисел. И да, на комплексной плоскости синус вполне себе достигает значения 5.

Ввод-вывод при тестировании

Во всех задачах на доработку методов класса тестирование производится следующим образом: вашей программе на вход передаётся тест, состоящий из последовательность питоновских команд, который нужно исполнить при помощи функции exec. Сделать это удобнее всего одним из двух способов:

"""Описание класса"""

import sys
exec(sys.stdin.read())
или
"""Описание класса"""

exec(open('input.txt').read())
В первом случае ввод производится с клавиатуры, но не работает debug. Во втором случае ввод производится из файла.

Список операторов, которые мы перегрузим в этом листке

Полная документация на английском: http://docs.python.org/py3k/reference/datamodel.html.

Метод Использование
Операторы сравнения
x < y
__le__(self, other) x <= y
__eq__(self, other) x == y
__ne__(self, other) x != y
__gt__(self, other) x > y
__ge__(self, other) x >= y
Арифметические операторы
Сложение
__add__(self, other) x + y
__radd__(self, other) y + x
__iadd__(self, other) x += y
Вычитание
__sub__(self, other) x - y
__rsub__(self, other) y - x
__isub__(self, other) x -= y
Умножение
__mul__(self, other) x * y
__rmul__(self, other) y * x
__imul__(self, other) x *= y
Деление
__truediv__(self, other) x / y
__rtruediv__(self, other) y / x
__itruediv__(self, other) x /= y
Целочисленное деление
__floordiv__(self, other) x // y
__rfloordiv__(self, other) y // x
__ifloordiv__(self, other) x //= y
__divmod__(self, other) divmod(x, y)
Остаток
__mod__(self, other) x % y
__rmod__(self, other) y % x
__imod__(self, other) x %= y
Возведение в степень
__pow__(self, other) x ** y
__rpow__(self, other) y ** x
__ipow__(self, other) x **= y
Отрицание, модуль
__pos__(self) +x
__neg__(self) -x
__abs__(self) abs(x)
Преобразование к стандартным типам
__bool__(self) bool(x), if x
__int__(self) int(x)
__float__(self) float(x)
__str__(self) str(x), print(x)
__repr__(self) repr(x)
__round__(self, digits = 0) round(x, digits)

A: __repr__

Создайте новый класс Complex с двумя атрибутами: real — вещественной частью, и imag — мнимой частью комплексного числа. Создайте конструктор, принимающий два необязательных параметра — вещественную и мнимую часть комплексного числа (по умолчанию параметры равны нулю). Реализуйте метод __repr__, возвращающий строку, из которой можно создать в точности такое же комплексное число. Если метод __str__ не реализован, то автоматически будет вызываться метод __repr__, поэтому наше комплексное число уже будет можно вывести на экран.

one = Complex(1)
zero = Complex()
i = Complex(0, 1)
other = Complex(2.73, -12.14)
print(one)
print(zero)
print(i)
print(other)
print(Complex(*(map(int, '4 2'.split()))))
Complex(1, 0)
Complex(0, 0)
Complex(0, 1)
Complex(2.73, -12.14)
Complex(4, 2)
IDE

B: __str__

Реализуйте метод __str__, который будет выводить комплексное число в удобной для человека форме: для комплексного число с нулевой мнимой частью выводится только вещественная часть, для комплексного число с нулевой вещественной частью выводится только мнимая часть (не забудьте про символ 'i'), в остальных случаях выводится число вида (a+bi), (a-bi), (-a+bi), (-a-bi). Если мнимая часть равна плюс-минус единице, то единица не выводится.

one = Complex(1)
zero = Complex()
i = Complex(0, 1)
other = Complex(2.73, -12.14)

print(one)
print(zero)
print(i)
print(other)
print(Complex(0.0))
1
0
i
(2.73-12.14i)
0.0
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)

Вряд ли удастся придумать что-то удачнее, чем

    def __str__(self):
        if self.real != 0 and self.imag != 0:
            ...
        elif self.imag != 0:
            ...
        else:
            ...
        return res
IDE

C: __add__

Реализуйте метод __add__, принимающий на вход два комплексных числа и возвращающий их новое комплексное число — их сумму.

one = Complex(1)
zero = Complex()
i = Complex(0, 1)
other = Complex(2.73, -12.14)

print(one + zero)
print(one + i)
print(other + other)
print(other + zero)
print(i + Complex(0, -1))
print(i + i + i + i)
1
(1+i)
(5.46-24.28i)
(2.73-12.14i)
0
4i
IDE

D: __repr__ — 2 и __slots__

Сравните реализации методов __str__ и __repr__ у наших классов Decimal и Fraction:

from fractions import *
from decimal import *
a = Fraction(3, 17)
print(str(a))
print(repr(a))
b = Decimal('1.23')
print(str(b))
print(repr(b))

В отличие от __str__ метод __repr__ выдаёт такое представление, из текста которого можно создать исходных объект.

Обновите метод __repr__ так, чтобы возвращалась представление комплексной и мнимой части, соответствующее repr.

Если специальному атрибуту класса с именем __slots__ присвоить значение в виде списка из нескольких текстовых строк, то только перечисленные в данном списке имена могут использоваться в качестве имен полей (атрибутов) классов. Например:

class Date:
    __slots__ = ['day', 'month', 'year']

    def __init__(self, ...):
        ...

Это также приведет к уменьшению времени обращения к атрибутам класса за счет другого способа хранения атрибутов в экземпляре класса.

Так как у комплексного числа не будет других атрибутов, то заполните эту переменную вещественной и мнимой частью.

from fractions import Fraction
from decimal import Decimal

print(repr(Complex(Fraction(1,3))))
print(repr(Complex(1.31, Decimal('123.321'))))
print(repr(Complex(Fraction(1,3), Fraction('0.333'))))
print(repr(Complex(Decimal('1.1'), Decimal('0.333'))))
a = Complex(3)
a.foo = 'fail'
Complex(Fraction(1, 3), 0)
Complex(1.31, Decimal('123.321'))
Complex(Fraction(1, 3), Fraction(333, 1000))
Complex(Decimal('1.1'), Decimal('0.333'))
Traceback (most recent call last):
  File "<string>", line 420, in run_nodebug
  File "<module3>", line 35, in <module>
AttributeError: 'Complex' object has no attribute 'foo'
IDE

E: __add__ — 2

Обновите метод __add__ так, чтобы к комплексному числу можно было корректно прибавить комплексное число, а также число типа int, float, Decimal, Fraction. Коэффициенты типа Decimal не поддерживают операции с числами типа float или Fraction, соответствующие ошики обрабатывать не нужно.
При попытке прибавить элемент другого класса возвращайте константу NotImplemented.

one = Complex(1)
zero = Complex()
i = Complex(0, 1)
other = Complex(2.73, -12.14)

print(one + 0)
print(i + 1)
print(other + Fraction(1,3))
print(i + Decimal('3.14'))
print(Complex(Fraction(1,3), Fraction('0.33')) + 1)
print(i + 'a')
1
(1+i)
(3.0633333333333335-12.14i)
(3.14+i)
(4/3+33/100i)
Traceback (most recent call last):
  File "<string>", line 420, in run_nodebug
  File "<module3>", line 45, in <module>
TypeError: unsupported operand type(s) for +: 'Complex' and 'str'
IDE

F: __init__ — 2

Обновите метод __init__ так, чтобы на вход можно было передать строку, содержащую комплексное число, у которого вещественная и мнимая часть могут быть только целыми.

print(Complex('0'))
print(Complex('1'))
print(Complex('-1'))
print(Complex('i'))
print(Complex('1i'))
print(Complex('-i'))
print(Complex('2-i'))
print(Complex('-3i + 4'))
print(Complex('   -3i     +      7        '))
0
1
-1
i
i
-i
(2-i)
(4-3i)
(7-3i)
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)

Сначала нужно избавиться от пробелов при помощи .replace(' ', ''). Затем нужно найти самый правый знак + или - (если он есть). Для этого можно, например, поместить в отдельную переменную нашу строку, в которой заменили знак + на -. После этого при помощи метода rfind('-') найти этот знак. И разрезать строку на две части. В мнимой части будет символ i (можно писать if 'i' in part:), в вещественной его не будет. Дальше избавляемся от символа i. Пустая строчка — это 0, + и - превращаются в +1 и -1, а в противном случае нужно применить int.

Для тех, кто уже месяц не может решить эту задачу

Просто игнорируйте её и решайте дальше, начиная задачи H. В качестве конструктора используйте следующий код:

    def __init__(self, a=0, b=0, c=lambda a: float(a) if '.' in a else int(a), d={'': 0, 'i': 1, '+i': 1, '-i': -1}):
        self.real, self.imag = (a, b) if type(a) != str else (lambda a, b: (c(a) if a else 0, (lambda a: d[a] if a in d else c(a[:-1]))(b)))(*(lambda a, b: (b, a) if 'i' in a else (a, b))(*(lambda a, b: (a[:b], a[b:]))(*(lambda a: (a, (lambda x: x if x > 0 else 100)(a.replace('+', '-').rfind('-'))))(a.replace(' ', '')))))
IDE

G: __init__ — 3

Обновите метод __init__ так, чтобы на вход можно было передать строку, содержащую комплексное число, у которого вещественная и мнимая часть могут быть типов int или float.

print(Complex('0'))
print(Complex('1'))
print(Complex('-1.0'))
print(Complex('i'))
print(Complex('1.12i'))
print(Complex('-2i'))
print(Complex('2.13-i'))
print(Complex('-3.12i + 4.54'))
print(Complex('   - 3.12  i     +      7.65        '))
0
1
-1.0
i
1.12i
-2i
(2.13-i)
(4.54-3.12i)
(7.65-3.12i)
Для тех, кто уже месяц не может решить эту задачу

Просто игнорируйте её и решайте дальше, начиная задачи H. В качестве конструктора используйте следующий код:

    def __init__(self, a=0, b=0, c=lambda a: float(a) if '.' in a else int(a), d={'': 0, 'i': 1, '+i': 1, '-i': -1}):
        self.real, self.imag = (a, b) if type(a) != str else (lambda a, b: (c(a) if a else 0, (lambda a: d[a] if a in d else c(a[:-1]))(b)))(*(lambda a, b: (b, a) if 'i' in a else (a, b))(*(lambda a, b: (a[:b], a[b:]))(*(lambda a: (a, (lambda x: x if x > 0 else 100)(a.replace('+', '-').rfind('-'))))(a.replace(' ', '')))))
IDE

H: __neg__, __pos__, __bool__ и conjugate

Реализуйте методы __neg__ и __pos__. Метод __neg__ должен возвращать новое комплексное число с противоположным знаком, метод __pos__ — само число (т.е. self). Реализуйте метод __bool__, возвращающий False тогда и только тогда, когда вещественная и мнимая часть равны нулю, и True иначе. Реализуете метод conjugate, возвращающий сопряжённое число.

a = Complex(1,2)
print(-a)
print(+a)
print(id(a) == id(+a))
print(id(a) == id(-a))
print(bool(Complex()))
print(bool(Complex('2i')))
print(Complex('1+2i').conjugate())
(-1-2i)
(1+2i)
True
False
False
True
(1-2i)
IDE

I: __radd__

Реализуйте метод __radd__ так, чтобы к числу типа int, float, Decimal, Fraction можно было прибавить комплексное число. При попытке прибавить комплексное число к другому типу возвращайте константу NotImplemented.

one = Complex(1)
zero = Complex()
other = Complex(2.73, -12.14)
print(0 + one)
i = Complex(0, 1)
print(1 + i)
print(Fraction(1,3) + other)
print(Decimal('3.14') + i)
print(1 + Complex(Fraction(1,3), Fraction('0.33')))
try:
    print('a' + i)
except TypeError as e:
    print(e)

1
(1+i)
(3.0633333333333335-12.14i)
(3.14+i)
(4/3+33/100i)
Can't convert 'Complex' object to str implicitly
IDE

J: __sub__ и __rsub__

Реализуйте методы __sub__ и __rsub__ так, чтобы из комплексного числа можно было вычесть комплексное число и число типа int, float, Decimal, Fraction, а также наоборот: из обычных чисел вычитать комплексные. При попытке выполнить операцию с другим типом возвращайте константу NotImplemented.

one = Complex(1)
zero = Complex()
other = Complex(2.73, -12.14)
strong = Complex(Fraction(1,3), Decimal('0.33'))
i = Complex(0, 1)
print(one - zero)
print(zero - i)
print(strong - 1)
print(3.13 - other)
try:
    print('a' - i)
except TypeError as e:
    print(e)
try:
    print(i - 'a')
except TypeError as e:
    print(e)
1
-i
(-2/3+0.33i)
(0.3999999999999999+12.14i)
unsupported operand type(s) for -: 'str' and 'Complex'
unsupported operand type(s) for -: 'Complex' and 'str'
IDE

K: __mul__ и __rmul__

Реализуйте методы __mul__ и __rmul__ так, чтобы из комплексное число можно было умножить на комплексное число и число типа int, float, Decimal, Fraction, а также наоборот: обычное число умножить на комплексное. При попытке выполнить операцию с другим типом возвращайте константу NotImplemented.

После определения класса добавьте определение константы I = Complex(0, 1). Тогда для создания комплексного числа можно будет пользоваться как конструкцией Complex(a, b), так и конструкцией a + b * I (хотя первая, конечно, будет работать в 4 раза быстрее).

one = Complex(1)
zero = Complex()
other = 2.73 - 12.14 * I
fr = Complex(Fraction(1,3), Fraction(-4, 13))
dc = Complex(Decimal('-12.12'), Decimal('2.33'))
print(one * zero)
print(other * fr)
print(dc * I)
print(one * 3)
print(3.12 * I)
print(fr * 5)
print(9 * dc)
print(dc * dc)
try:
    print('a' * I)
except TypeError as e:
    print(e)
try:
    print(I * 'a')
except TypeError as e:
    print(e)
0
(-2.825384615384616-4.886666666666667i)
(-2.33-12.12i)
3
3.12i
(5/3-20/13i)
(-109.08+20.97i)
(141.4655-56.4792i)
can't multiply sequence by non-int of type 'Complex'
can't multiply sequence by non-int of type 'Complex'
IDE

L: __abs__

Реализуйте метод __abs__, возвращающий модуль комплексного числа. Используйте функцию hypot() из модуля math. Помните, что импортировать модули лучше всего в самом начале программы.
Если либо мнимая, либо вещественная часть комплексного числа имеет тип Decimal, то для того, чтобы не потерять точность, необходимо вычислять модуль вручную. Положите вещественную и мнимую часть в отдельные переменные, преобразовав к типу Decimal. Для извлечения корня используйте метод .sqrt() (не путайте с функцией sqrt() из модуля math, он работает только со float'ами).

from fractions import Fraction
from decimal import Decimal
print(abs(Complex(1)))
print(abs(Complex()))
print(abs(Complex(0, 1)))
print(abs(2.73 - 12.14 * I))
print(abs(Complex(Fraction(1,3), Fraction(-4, 13))))
print(abs(Complex(Decimal('-12.12'), Decimal('2.33'))))
print(abs(Complex(-12.12, Decimal('2.33'))))
1.0
0.0
1.0
12.443170817761846
0.45363605161420856
12.34193258772709388517385744
12.34193258772709311763152273
IDE

M: Тригонометрическая запись

Реализуйте функцию phase(z), возвращающую аргумент числа (в диапазоне $(-\pi,\pi]$), функцию polar(z), возвращающую пару (модуль, аргумент), и функцию rect(r, phi), возвращающую комплексное число по его тригонометрической записи.

numbers = (Complex(1),
           Complex(1,1),
           Complex(1,-1),
           Complex(Fraction(-2,3),
               Fraction(-4,5)),
           Complex(Decimal('3.25'),
               Decimal('-123.12')))
for number in numbers:
    print(number,
        phase(number),
        polar(number),
        rect(abs(number), phase(number)),
        sep='\n', end='\n' * 2)
1
0.0
(1.0, 0.0)
1.0

(1+i)
0.7853981633974483
(1.4142135623730951, 0.7853981633974483)
(1.0000000000000002+i)

(1-i)
-0.7853981633974483
(1.4142135623730951, -0.7853981633974483)
(1.0000000000000002-i)

(-2/3-4/5i)
-2.2655346029916
(1.0413666234542207, -2.2655346029916)
(-0.6666666666666669-0.7999999999999999i)

(3.25-123.12i)
-1.544405444351767
(Decimal('123.1628876731948015590474067'), -1.544405444351767)
(3.2499999999999907-123.12i)

IDE

N: __eq__ и __ne__

Реализуйте методы __eq__ и __ne__, позволяющие сравнивать комплексные числа друг с другом и с обычными числами.

print(Complex() == 0)
print(Complex(1) == 1)
print(Complex(Fraction(3,4)) == .75)
print(Complex(2,3) == Complex(2,3))
print(Complex(2,3) == Complex(2,3.001))
print(Complex() != 0)
print(Complex(1) != 1.2)
print(Complex(Fraction(3,4)) != .75)
print(Complex(2,3) != Complex(2,3))
print(Complex(2,3) != Complex(2,3.001))
True
True
True
True
False
False
True
False
False
True
IDE

O: __truediv__ и __rtruediv__

Реализуйте методы __truediv__ и __rtruediv__ так, чтобы комплексное число можно было поделить на комплексное число и на число типа int, float, Decimal, Fraction, а также наоборот: обычное число поделить на комплексное. При попытке выполнить операцию с другим типом возвращайте константу NotImplemented.

print(Complex(1,1) / Complex(1,-1))
print(4 / Complex(2,3))
print(Complex(1e-300, 1e-300) / Complex(1e-300, -1e-300))
0.9999999999999999i
(0.6153846153846154-0.9230769230769231i)
i
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)

Чтобы не отстрелить себе что-нибудь при операциях с числами вида 1e-300, использовать следующую формулу для деления чисел: $$ \dfrac{z}{w} = \left(\dfrac{z}{|w|}\right) \cdot \left(\dfrac{\overline{w}}{|w|}\right) $$ (как обычно, \(|w|\) — это модуль комплексного числа, а \(\overline{w}\) — это комплексно сопряжённое)

IDE

P: __iter__

Реализуйте метод __iter__, который является генератором, возвращающим при помощи yield сначала вещественную часть, а затем мнимую. Это позволит совсем просто получать из комплексного числа пару чисел.

print(tuple(1+I))
print(list(-I))
print(*(1.12 * I - 34.29))
def foo(x, y):
    return x + y
cmx = Complex(170, 9)
print(foo(*cmx))
print(' '.join(map(str, Complex(2, -3))))
(1, 1)
[0, -1]
-34.29 1.12
179
2 -3
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)
    def __iter__(self):
        yield ???
        yield ???
IDE

Используем комплексные числа в геометрии

Q: Самая дальняя точка

Программа получает на вход число $N$, далее координаты $N$ точек с целыми координатами. Выведите координаты точки, наиболее удаленной от начала координат.

2 1 2 2 3
2 3
Сделайте это за две строчки, не считая определения класса Complex.
IDE

R: Центр масс

Выведите координаты центра масс данного множества точек (учтите, что это —два действительных числа).

2 1 2 2 3
1.5 2.5
Сделайте это за три строчки, не считая определения класса Complex.
★★
Сделайте это за одну строчку, не считая определения класса Complex.
IDE

S: Диаметр множества

Выведите диаметр данного множества – максимальное расстояние между двумя данными точками.

3 1 1 1 0 0 0
1.4142135623731
Сделайте это за три строчки, не считая определения класса Complex.
★★★
Сделайте это за одну строчку, не считая определения класса Complex.
IDE

T: Сортировка

Отсортируйте данные точки в порядке возрастания расстояния от начала координат. Используете встроенную сортировку с указанием подходящего параметра key.

3 1 0 -1 -1 0 0
0 0 1 0 -1 -1
Сделайте это за три строчки, не считая определения класса Complex.
★★★
Сделайте это за одну строчку, не считая определения класса Complex.
IDE

U: Максимальный периметр

Среди данных точек найдите три точки, образующие треугольник с наибольшим периметром. Выведите данный периметр.

Для нахождения периметра треугольника напишите отдельную функцию Perimeter(A, B, C).

4 0 0 0 1 1 0 1 1
3.41421356237309
IDE

V: Максимальная площадь

Среди данных точек найдите три точки, образующие треугольник с наибольшей площадью. Выведите данную площадь.

Для нахождения площади треугольника напишите отдельную функцию Area(A, B, C).

4 0 0 0 1 1 0 1 1
0.5
IDE

W: Поворот

Дан угол ϕ и центр поворота — точка A. После этого идёт число n и координаты n точек на плоскости.

Для каждой из этих точек выведите её образ при повороте на угол ϕ вокруг точки A.

1.5707963267948966 1+i 3 1+i 2+i 1+2i
1.0+i 1.0+2.0i i
IDE

X: Система линейных уравнений

Даны шесть комплексных чисел — \(a_1, b_1, c_1, a_2, b_2, c_2\).

Выведите решение системы уравнений $$ \left\{ \begin{array}{c} a_1z+b_1w=c_1 \\ a_2z+b_2w=c_2 \end{array} \right. $$ Гарантируется, что у этой системы ровно одно решение.

2+i 1 4-i 3-i 1-2i 2+i
(4-2i) (-6-i)
IDE

Возведение в степень, синус, и т.д.

Y: __pow__

Реализуйте метод __pow__, возводящий данное комплексное число в целую степень n за время порядка log(n).

print(Complex(2) ** 10)
print(I ** 1234567)
print((1 + I) ** 500 * (1 - I) ** (-498))
1024
-i
-2.0i
IDE

Z: __pow__ — 2

Реализуйте метод __pow__, точно возводящий данное комплексное число в целую степень n за время порядка log(n), и возводящий комплексное число в действительную степень (типа float) при помощи формулы Муавра.

print(I ** 1234567)
print(I ** 0.5)
print(4 ** 0.5)
print(-1 ** 0.5)
print((-11 - 2*I) ** (1/3))
print((Complex(1, 2) ** 8))
print((Complex(1, 2) ** 8) ** (1/8))
print(((Complex(1, 2) ** 8) ** (1/8)) ** 8)
-i
(0.7071067811865476+0.7071067811865475i)
2.0
-1.0
(1.2320508075688774-1.8660254037844386i)
(-527+336i)
(2.1213203435596424+0.7071067811865476i)
(-526.9999999999998+335.99999999999955i)
IDE

ZA: __pow__ — 3 и __rpow__

Добавьте методу __pow__ возможности возводить комплексное число в комплексную степень. Также реализуйте метод __rpow__ для возведения числа типа int или float в комплексную степень.

print(I ** 0.5)
print(I ** 1234567)
print(I ** I)
print(2.71828 ** (3.1415 * I))
try:
    print('i' ** I)
except TypeError as e:
    print(e)
try:
    print(I ** 'i')
except TypeError as e:
    print(e)
(0.7071067811865476+0.7071067811865475i)
-i
0.20787957635076193
(-0.9999999955096336+9.476672816471575e-05i)
unsupported operand type(s) for ** or pow(): 'str' and 'Complex'
unsupported operand type(s) for ** or pow(): 'Complex' and 'str'
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)

$$ z = e^{\ln|z| + i\cdot\text{Arg}(z)} $$

IDE

ZB: Косинус

Реализуйте функцию ccos, вычисляющую косинус комплексного числа. Формула Эйлера — ключ к этой задаче.

print(ccos(I))
print(ccos(Complex(3.1415, 0)))
print(ccos(-2.2924316695611777*I))
print(ccos(Complex(1.5707963267948967, -0.8813735870195429)))
1.5430806348152437
-0.9999999957076562
5.0
(-2.274228861132057e-16+0.9999999999999999i)
IDE

ZC: Синус

Где косинус, там и синус.

print(csin(I))
print(csin(Complex(3.1415, 0)))
print(csin(0.8813735870195429*I))
print(csin(Complex(1.5707963267948966, 5.880525183806337)))
1.1752011936438014i
9.265358966049024e-05
0.9999999999999999i
(178.99999999999994+1.09604178109785e-14i)
IDE