Flat Preloader Icon

Project Euler (answers)

Проект Эйлера: Ответ и решение 33 задачи (Дроби, сократимые по цифрам)

Проект Эйлера (33 задача). Задача легко решается банальным перебором. Однако нет предела совершенствованию.

Узнать больше в телеграм-канале: ProjectEuler++

Условия задачи

Дробь 49/98 является любопытной, поскольку неопытный математик, пытаясь сократить ее, будет ошибочно полагать, что 49/98 = 4/8, являющееся истиной, получено вычеркиванием девяток.

Дроби вида 30/50 = 3/5 будем считать тривиальными примерами.

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

Пусть произведение этих четырех дробей дано в виде несократимой дроби (числитель и знаменатель дроби не имеют общих сомножителей). Найдите знаменатель этой дроби.

Всегда Вам рады)))

Решение (проект Эйлера - 33 задача)

Проект Эйлера (33 задача) - решение, часть 1
Решение, часть 1

Поскольку функция main() получилась довольно-таки объемной, потому решил не “мельчить” и разбить ее аж на три скриншота.

Проект Эйлера (33 задача) - решение, часть 2
Решение, часть 2

Чем плох обычный перебор чисел

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

Потому что главная его проблема – в количестве этих чисел. Поскольку речь идет о двузначных числах (от 10 до 99) в числителе и знаменателе (ab/cd), потому речь может идти переборе около 10000 вариантов их сочетаний.

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

Продвинутый перебор

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

Т.е. для дроби вида:

  • aB /Bc 

Поскольку цифра B одинакова в числителе и знаменателе, потому перебирать нужно будет не два двузначных числа, а три числа от 1 до 9. Что в итоге может дать менее 1000 комбинаций и потому сократить количество вычислений сразу на порядок.

Описание алгоритма работы программы

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

Далее все просто:

  • Значения цифр чисел дроби перебираются в трех циклах.
  • Этими цифрами инициализируется дробь.
  • Далее дробь проверяется на соответствие условиям задачи.
  • Числитель  и знаменатель дроби в итоге (при необходимости) учитываются в ответе.

Две диагонали в дроби и прочие улучшения

Проект Эйлера (33 задача) - решение, часть 3
Решение, часть 3

Поскольку у дроби вида ab/cd может быть две диагонали с одинаковыми цифрами:

  • aB/Bc
  • Ab/cA

… потому понадобится еще раз перебирать множители в цикле для второго варианта расположения диагонали.

Их можно было бы расположить в один цикл, однако есть один нюанс, описанный в задании.

Числитель дроби  не должен быть больше знаменателя. Потому для дроби вида aB/Bc необходимо ограничивать значение числа (a) в числителе:

  • aB < Bc => a < B

И потому же для дроби Ab/cA ограничиваем число (c) в знаменателе:

  • Ab < cA => A < c

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

Также согласно заданию необходимо получить в вид в виде несократимой дроби. Потому в конце всех вычислений дробь сокращаем с помощью специальной функции simplify().

Измеряем время работы программы

Для измерения времени работы программы подключаем библиотеку time.h:

  • #include <time.h> 

С помощью функции clock() измеряем время начала (begin) и окончания (end) работы программы. В итоге время работы будем находить как разность между ними.

* Примечания:

clock_t это тип данных, определенный через typedef. По сути это обычный целочисленный long

(double) приводит число (в данном случае – результат вычислений end – begin) к вещественному типу данных double (числа с точкой)

CLOCKS_PER_SEC – это константа, сохраненная в библиотеке и зависящая от типа устройства, на котором запускается программа. В данном случае – это просто 1000

Функции, использующиеся в программе (проект Эйлера - 33 задача)

Функция для присваивания дроби начальных значений
Функция для присваивания дроби начальных значений

Функция для присваивания дроби начальных значений

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

Так же при каждой новой итерации циклов, получается новая дробь и ее значения необходимо менять.

Функция принимает отдельные цифры дроби, вычисляет значения числителя и знаменателя и в итоге заносит данные в структуру.

Функция для упрощения дроби

Функция для упрощения дроби
Функция для упрощения дроби

Данная функция перебирает делители и ищет тот, который нацело делит как числитель,  так и знаменатель. При этом, поскольку из задания известно, что числитель меньше знаменателя, потому в качестве делителя принимается ряд чисел вплоть до числителя.

Как только делитель найден, перебор останавливается и оба члена дроби сокращаются на него. Поскольку перебор идет от большего числа к меньшему, потому найденный в итоге делитель (если он есть) является максимальным и дальнейший поиск не нужен.

Функция для проверки, не является ли дробь тривиальной

Функция для проверки не является ли дробь тривиальной
Функция для проверки, не является ли дробь тривиальной

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

Далее обе дроби по возможности сокращаются функцией simplify().

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

Ответ и скорость работы программы (проект Эйлера - 33 задача)

Проект Эйлера (33 задача) - ответ
33 задача ответ

В итоге ответ на 33 задачу проекта Эйлера составил 100, программа считает ответ мгновенно. 

По традиции хотелось бы выразить глубочайшую признательность Сергею Балакиреву за его титанический труд по созданию бесплатных курсов и в частности, за “Язык программирования C/C++ для начинающих“.

проект Эйлера
Курс "C/C++ для начинающих"

Есть вопросы, господа? Отвечаем спокойно, четко и только по делу))

Есть вопросы, господа?

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

Проект Эйлера (ответы) | © 2023-2024 | eulerproject.ru

© 2023-2024 | eulerproject.ru