В пятницу можно смешивать всё и со всем!

Мы смешаем пифагоровы тройки и библиотеку RxJS.

Пифагоровой тройкой называют три натуральных числа, из которых можно составить прямоугольный треугольник.

Такие числа известны людям с очень древних времён. Например, их использовали ещё в Египте, откуда до нас дошла самая известная пифагорова тройка — это числа 3, 4 и 5.

a^2 + b^2 = c^2 \qquad \qquad 3^2 + 4^2 = 5^2

RxJS — это библиотека для реактивного программирования. Она поможет сделать генерацию троек красивой и простой.

Сразу замечу, что статья претендует только на развлекающую роль

Часть с математикой

Начнём с небольшой теоретической подготовки. Как ранее было замечено, пифагоровой тройкой называют три натуральных числа, которые удовлетворяют уравнению:

a^2 + b^2 = c^2

Назовём число a малым катетом, число b большим катетом, а число c — гипотенузой.

Все пифагоровы тройки описываются параметрическими формулами, которые называют формулами Евклида.

Чтобы оставить статью скорее развлекательной, мы дальше будем рассматривать только такие пифагоровы тройки, у которых гипотенуза c отличается от большего катета b ровно на единицу.

Вот первые три такие тройки:

3^2 + 4^2 = 5^2 \qquad \qquad 5^2 + 12^2 = 13^2 \qquad \qquad 7^2 + 24^2 = 25^2

Попробуем вывести формулы этого семейства троек. Подставим c = b + 1 и получим:

a^2 + b^2 = (b + 1)^2 = b^2 + 2b +1 a^2 = 2b +1

Видно, что квадрат числа a — это всегда нечётное число. А значит и число a тоже всегда нечётное. Теперь можно легко записать выражения для a, b и c:

a = 3,\,5,\,7,\,... \qquad \qquad b = \frac{a^2 - 1}{2} \qquad \qquad c = b + 1

Часть с программированием

Напишем несколько простых функций для преобразований, которые нам понадобятся.

Малый катет a надо уметь возводить в квадрат.

function toSquare(n: number): number {
  return (n ** 2);
}

Квадрат малого катета a в нашей задаче — это всегда нечётное число, а больший катет b, как видно из формулы, — это порядковый номер этого нечётного числа.

function toNatural(n: number): number {
  return ((n - 1) / 2);
}

Наконец, если у нас есть больший катет b, то гипотенузу c можно получить прибавлением единицы.

function addOne(n: number): number {
  return (n + 1);
}

Теперь мы можем делать все основные преобразования; осталось получить последовательность нечётных чисел. Последовательность чисел будем представлять в виде потока.

RxJS даёт достаточно много способов создания потоков, но самый подходящий нам — это функция interval(). Она создаёт поток, который будет испускать последовательность натуральных чисел с заданным интервалом.

function generateNaturalNumbers(): Observable <number> {
  return interval(500);
}

const naturalNumbers = generateNaturalNumbers();

naturalNumbers.subscribe((value) => {
  console.log(value);
});

Подпишемся на созданный поток и раз в полсекунды будем получать новое натуральное число. Легко понять, как перейти к нужной нам последовательности нечётных чисел:

n = 0,\,1,\,2,\,... \qquad \qquad m = n + 1 \qquad \qquad a = 2m + 1

Для этого напишем вспомогательную функцию, которая по порядковому номеру нечётного числа будет возвращать само число. Эта функция будет обратной к функции toNatural().

function toOdd(n: number): number {
  return ((n * 2) + 1);
}

Теперь препятствий точно не осталось!

Создадим поток с малыми катетами a. Для этого к числу из потока натуральных чисел прибавим единицу, а затем получим соответствующее нечётное число. При испускании нового натурального числа будет испускаться и новый малый катет a.

const smallCathetuses = naturalNumbers.pipe(
  map(addOne),
  map(toOdd),
);

Из потока с малыми катетами a, опираясь на формулы, создим поток с большими катетами b. При испускании малого катета будет испускаться соответствующий ему большой катет.

const bigCathetuses = smallCathetuses.pipe(
  map(toSquare),
  map(toNatural),
);

Поток с гипотенузами получить проще всего, надо всего лишь прибавить единицу к большему катету

const hypotenuses = bigCathetuses.pipe(
  map(addOne),
);

Теперь у нас есть три потока: smallCathetuses, bigCathetuses и hypotenuses. Если выбирать из каждого потока по последнему значению, то будем получать искомые пифагоровы тройки.

Сделать это можно с помощью функции withLatestFrom(). Она принимает несколько потоков и возвращает массив, первый элемент которого — это элемент обрабатываемого потока, а следующие значения — это последние значения из переданных потоков.

const pythagoreanTriples = smallCathetuses.pipe(
  withLatestFrom(bigCathetuses, hypotenuses),
);

Подпишемся на поток пифагоровых троек и будем наблюдать их

pythagoreanTriples.subscribe((value) => {
  console.log(value);
});

// [3, 4, 5]
// [5, 12, 13]
// [7, 24, 25]
// ...

Генерация таких пифагоровых троек (где гипотенуза отличается от большего катета на единицу) имеет и практическое применение. Например, можно построить тысячеугольник, у которого все стороны и диагонали будут целыми.

Подробный алгоритм построения от Бориса Трушина

Весь код доступен на ГитХабе

P. S. За обложку спасибо Тане Лишаевой

Комментарии (1)


  1. Format-X22
    15.04.2023 10:45
    +1

    Спасибо, было интересно.