В этом листке мы полностью реализуем класс комплексных чисел.
Обращайтесь к статье в википедии для подробного текста.
Чуть ниже будет базовое напоминание.
Вообще в питоне уже есть встроенная поддержка комплексных чисел.
Но она поддерживает только коэффициенты типа float.
Мы же сделаем реализацию, поддерживающую коэффициенты типа int, float, Decimal, Fraction
.
Это позволит выполнять вычисления с большим числов знаков или точные вычисления.
Основные сложности как раз будут при взаимодействиях с классами Decimal и Fraction
и между ними.
Комплексное числоz — это выражение вида z=a+bi, где a,b∈R, а i — мнимая единица.
По определению i2=−1.
Число a называют вещественной частьюz (пишут a=Re(z)), а число b —
мнимой частьюz (пишут b=Im(z)).
Комплексные числа складывают и умножают,
«раскрывая скобки и приводя подобные».
Множество комплексных чисел обозначают буквой C.
Комплексные числа и вектора на плоскости
Каждому комплексному числу z=a+bi сопоставим точку (a,b)
и вектор с координатами
(a,b). Длина этого вектора называется модулем комплексного
числа z и обозначается ∣z∣.
Пусть z=0.
Угол (в радианах), отсчитанный против часовой стрелки
от вектора
(1,0) до вектора
(a,b),
называется аргументом комплексного
числа z и обозначается
Arg(z).
Аргумент
определен с точностью до прибавления числа вида 2πn, где n∈Z.
Тригонометрическая форма записи
Для любого ненулевого комплексного числа z имеет место
равенство z=r(cosφ+isinφ), где r=∣z∣,
φ=Arg(z).
Формула Муавра
Пусть z=r(cosφ+isinφ), n∈N.
Тогда для любого целого n выполнено равенство
zn=rn(cosnφ+isinnφ).
Формула Эйлера
Оказывается, eiφ=cosφ+isinφ.
Можно использовать выражение eiφ как короткое и удобное обозначение для cosφ+isinφ.
Однако удивительная часть этой формулы состоит в том, что вместо φ можно подставлять не только вещественные числа, но и
комплексные.
Правда тождество от этого перестанет быть в полной мере честным из-за возможной многозначности.
После этого путём нехитрых арифметических преобразований можно научиться возводить сначала вещественное число в любую степень, а потом
любое комплексное число в любую комплексную степень.
А также вычислять синус и косинус комплексных чисел.
И да, на комплексной плоскости синус вполне себе достигает значения 5.
Во всех задачах на доработку методов класса тестирование производится следующим образом:
вашей программе на вход передаётся тест, состоящий из последовательность питоновских команд, который нужно исполнить при помощи функции exec.
Сделать это удобнее всего одним из двух способов:
Создайте новый класс 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()))))
Реализуйте метод __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))
10
i
(2.73-12.14i)
0.0
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)
Вряд ли удастся придумать что-то удачнее, чем
def__str__(self):
if self.real != 0and self.imag != 0:
...
elif self.imag != 0:
...
else:
...
return res
Сравните реализации методов __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__ присвоить значение в виде списка из нескольких текстовых строк, то только перечисленные в данном списке имена могут использоваться в качестве имен полей (атрибутов) классов. Например:
Обновите метод __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'
Обновите метод __init__ так, чтобы на вход можно было передать строку, содержащую комплексное число, у которого вещественная и мнимая часть могут быть только целыми.
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)
Сначала нужно избавиться от пробелов при помощи .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 elseint(a), d={'': 0, 'i': 1, '+i': 1, '-i': -1}):
self.real, self.imag = (a, b) iftype(a) != strelse (lambda a, b: (c(a) if a else0, (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 > 0else100)(a.replace('+', '-').rfind('-'))))(a.replace(' ', '')))))
Обновите метод __init__ так, чтобы на вход можно было передать строку, содержащую комплексное число, у которого вещественная и мнимая часть могут быть типов int или float.
01
-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 elseint(a), d={'': 0, 'i': 1, '+i': 1, '-i': -1}):
self.real, self.imag = (a, b) iftype(a) != strelse (lambda a, b: (c(a) if a else0, (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 > 0else100)(a.replace('+', '-').rfind('-'))))(a.replace(' ', '')))))
Реализуйте методы __neg__ и __pos__.
Метод __neg__ должен возвращать новое комплексное число с противоположным знаком, метод __pos__ — само число (т.е. self).
Реализуйте метод __bool__, возвращающий False тогда и только тогда, когда вещественная и мнимая часть равны нулю, и True иначе.
Реализуете метод conjugate, возвращающий сопряжённое число.
Реализуйте метод __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)
Реализуйте методы __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'
Реализуйте методы __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)
33.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'
Реализуйте метод __abs__, возвращающий модуль комплексного числа.
Используйте функцию hypot() из модуля math.
Помните, что импортировать модули лучше всего в самом начале программы.
Если либо мнимая, либо вещественная часть комплексного числа имеет тип Decimal,
то для того, чтобы не потерять точность, необходимо вычислять модуль вручную.
Положите вещественную и мнимую часть в отдельные переменные, преобразовав к типу Decimal.
Для извлечения корня используйте метод .sqrt() (не путайте с функцией sqrt() из модуля math, он работает только со float'ами).
Реализуйте функцию phase(z), возвращающую аргумент числа (в диапазоне (−π,π]),
функцию 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)
Реализуйте методы __truediv__ и __rtruediv__ так, чтобы комплексное число можно было поделить на комплексное число и на число типа int, float, Decimal, Fraction, а также наоборот: обычное число поделить на комплексное.
При попытке выполнить операцию с другим типом возвращайте константу NotImplemented.
0.9999999999999999i
(0.6153846153846154-0.9230769230769231i)
i
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Чес-слово! :)
Чтобы не отстрелить себе что-нибудь при операциях с числами вида 1e-300,
использовать следующую формулу для деления чисел:
wz=(∣w∣z)⋅(∣w∣w)
(как обычно, ∣w∣ — это модуль комплексного числа, а w — это комплексно сопряжённое)
Реализуйте метод __iter__, который является генератором, возвращающим при помощи yield сначала вещественную часть, а затем мнимую. Это позволит совсем просто получать из комплексного числа пару чисел.
print(tuple(1+I))
print(list(-I))
print(*(1.12 * I - 34.29))
deffoo(x, y):
return x + y
cmx = Complex(170, 9)
print(foo(*cmx))
print(' '.join(map(str, Complex(2, -3))))
(1, 1)
[0, -1]
-34.291.121792 -3
Подсказка
Да, мне кажется, что я уже достаточно думал над этой задачей
Отсортируйте данные точки в порядке возрастания расстояния от начала координат.
Используете встроенную сортировку с указанием подходящего параметра key.
3
1 0
-1 -1
0 0
0 0
1 0
-1 -1
★
Сделайте это за три строчки, не считая определения класса Complex.
★★★
Сделайте это за одну строчку, не считая определения класса Complex.
Реализуйте метод __pow__, точно возводящий данное комплексное число в целую степень n за время порядка log(n),
и возводящий комплексное число в действительную степень (типа float) при помощи формулы Муавра.
Добавьте методу __pow__ возможности возводить комплексное число в комплексную степень.
Также реализуйте метод __rpow__ для возведения числа типа int или float в комплексную степень.