Статья Автор: Лебедев Дмитрий Алексеевич

Черепашка-ниндзя. Часть 3. Операции с векторами

В 1 части была создана подпрограмма bobik
def bobik(scom, t=None, m = 10 ):
  # Для демонстрации есть упрощенный вызов по умолчанию
  if t == None : 
    t = turtle.Pen()
  for k in scom.split() : # пробег по командам
    if k == 'U' : t.up() #поднятие пера
    elif k == 'D' : t.down() #опускание пера
    elif k == 'L' : t.left(90) #налево стандарт
    elif k == 'R' : t.right(90)#направо стандарт
    elif k[0] == 'F' : t.forward(float(k[1:])*m) #вперед
    elif k[0] == 'L' : t.left(float(k[1:])) #налево на градус
    elif k[0] == 'R' : t.right(float(k[1:])) #направо на градус
  turtle.done() # показ рисунка    

Эта подпрограмма, с использованием f'-строк позволяет упрощать создание рисунков, в том числе на "клетчатой бумаге.
Попробуем добавить команды, позводяющие передвигать/смещать исполнителя на вектор (a, b)
Блок рисования сетки оформим в виде подпрограммы setka с параметрами (<размер клетки>, <ширина>,< высота>, < исполнитель>,)
 

Для решения задач с векторами надо создать ряд подпрограмм, реализующий некоторые операции с векторами/числами.
Какие операции есть с двумерными векторами/числами?
  • сложение \((x_1, y_1) + (x_2, y_2) = (x_1 + x_2, y_1 + y_2) \) - результат вектор
  • умножение на скаляр \(k\cdot(x, y) = (k\cdot x, k\cdot y) \) - результат вектор
  • умножение векторов, со скалярным результатом 
    • скалярное произведение \(((x_1, y_1), (x_2, y_2)) = x_1 \cdot x_2 + y_1\cdot y_2 \) 
    • векторное/косое произведение \([(x_1, y_1), (x_2, y_2)] = x_1 \cdot y_2 - y_1\cdot x_2 \)
  • умножение векторов, с векторным результатом 
    • комплексное произведение \((x_1, y_1)\cdot (x_2, y_2) =(x_1 \cdot x_2 - y_1\cdot y_2, x_1 \cdot y_2 + y_1\cdot x_2) \) 
  • получение сопряженного вектрора \(\overline{(x,y)} = (x, -y)\)
Эти операции позловяют получать интересные результаты в геометрии. 
Нетрудно проверить, что для сложения и комплексного умножения выполняются все основные свойства чисел и что числа вида \((r, 0)\) обладают все свойствами действительных чисел.
Поэтому числа вида  \((r, 0)\) будем называть вещественными (или вещественным подмножеством), а все числа \((x,y)\) -комплексными, где x - вещественная часть, а y - мнимая часть
Для работы с комплексными числами в Python существует специальный тип complex(), который поддерживает некоторые арифметические операции и операции сравнения равно, не равно.
Также есть модуль cmath позволяющий производить вычисления над комплексными числами.
Мы попробуем хранить точки/вектора в формате комплексного числа, чтобы более просто выполнять рисование и преобразования на плоскости.
 

Пример.
Пусть есть три точки \(A = (4,3), B =(-3,4), С =(0, 5)\) 
Зададим их как комплексные числа, найдем вектора сторон и их длины
 

 

Модифицируем программу для рисования, чтобы использовать точки/вектора в формате комплексных чисел.
Для этого воспользуемся "извлечением" из числа вещественной части (real) и мнимой части (imag).
Изменения достаточно ввести только для команд G и V. Код программы даже станет проще.

 

Найдем "решение треугольника", то есть определим основания медиан, биссектрис, высот и точек их пересечения.
Пусть координаты вершин заданы числами \(A, B, C.\) Векотора сторон можно определить как числа \(a = C - B;\ b = A - C;\ c = B- A \) 
1) Нахождение оснований медиан и "центроида" (точки пересечения медиан)
координаты оснований медиан есть
 \(mA = (B + C)/2;\ mB = (A + C)/2;\ mC = (A + B)/2 \)
"центроид" будет иметь координаты  \(cR = (A + B + C)/3 \)  (доказательство несложное, оставлено в качестве упражнения)
2) Нахождение оснований биссектрис и "инцентра" (точки пересечения биссектрис).
Воспользуемся "геометрическим фактом", что биссектриса "разбивает" противоположную сторону в отношении длин прилегающих сторон.
Для нахождения длины стороны можно просто взять модуль (abs), а разделить отрезок с помощью "взевешенного среднего".
Приведем пример такого разбиения, оставив разбор для самостоятельного изучения:
Пусть основание биссектрисы \(\angle A\) точка \(D.\ Тогда |BD| :|DA| = |AB| : |AC|\)
Следовательно координата точки \(D.\ определяется\ формулой\ D = \frac{B\cdot|AB| + C\cdot |AC| }{|AB| + |AC| }\)- это "взешенное среднее" \((B, C)\ по\ (|AB|, |AC|)\)
Основания координат биссектрис есть:
 \(bA = \frac{B\cdot abs(b) + C\cdot abs(c)}{abs(b) + abs(c)}\)\(bB = \frac{A\cdot abs(a) + C\cdot abs(c)}{abs(a) + abs(c)}\)\(bC = \frac{A\cdot abs(a) + B\cdot abs(b)}{abs(a) + abs(b)}\)
Для "инцентра" \(cI \) логично предположить, что координаты можно вычислить по формуле \(cI = \frac{A\cdot abs(a) + B\cdot abs(b) + C\cdot abs(c)}{abs(a) + abs(b) + abs(c)}\) 
(это истина, доказательство оставлено в качестве упражнения)
Следующая программа рисует два одинатовых треугольника. В синем рисует медианы, а в зелёном биссектрисы вписанную окрухность (радиус вписанной находится "двойным" подсчетом площади)
PS. Некоторые формулы можно сократить использованием библиотеки cmath, но пока эта библиотека "сознательно" не используется

 

 
Печать