Flat Preloader Icon

Project Euler (answers)

Проект Эйлера: Ответ и решение 42 задачи (Закодированные треугольные числа)

Проект Эйлера (42 задача). Позволяет поработать с файлами: открываем файл, копируем содержимое в массив, «парсим» текст на отдельные слова.

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

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

n член последовательности треугольных чисел задается как tn = ½n(n+1). Таким образом, первые десять треугольных чисел:

  • 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, …

Преобразовывая каждую букву в число, соответствующее ее порядковому номеру в алфавите, и складывая эти значения, мы получим числовое значение слова. Для примера, числовое значение слова SKY равно 19 + 11 + 25 = 55 = t10. Если числовое значение слова является треугольным числом, то мы назовем это слово треугольным словом.

Используя words.txt (щелкнуть правой кнопкой мыши и выбрать ‘Save Link/Target As…’), 16 КБ текстовый файл, содержащий около двух тысяч, часто используемых английских слов, определите, сколько в нем треугольных слов.

Добро пожаловать на сайт))

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

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

Уже привычно разбил функцию main() на два скриншота.

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

Вычисляем треугольные числа

В задании приведена формула для вычисления треугольного числа по его номеру. Для удобства данную формулу можно преобразовать в квадратное уравнение. Это позволит проверять соответствие кода слова условиям задачи, просто вычисляя его корни.

Однако в таком случае придется вычислять одни и те же треугольные числа снова и снова. Так произойдет, потому что в файле около двух тысяч слов и занимает он 16 килобайт. И в итоге слово в среднем будет содержать восемь букв. Даже если предположить, что оно состоит из одних букв ZZZZZZZZ, то получим максимальный цифровой код 8 * 26 = 208.

Потому, если мы вычислим заранее все треугольные числа до 1000 (возьмем с большим запасом), и сохраним в массив по индексу:

  • массив[число] = 1; // треугольное число
  • массив[число] = 0; // число не является треугольным

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

Второй вопрос, как заполнять этот массив.

Можно по формуле из задания, однако есть способ сократить вычисления. Если выписать ряд треугольных чисел:

  • 1 (1-е число), 3 (2-е), 6 (3-е), 10 (4-е), 15 (5-е)…

… то можно увидеть закономерность – каждое последующее число отличается от предыдущего ровно на его порядковый номер:

  • 2-е число = 1-е (1) + 2 = 3
  • 3-е = 2-е (3) + 3 = 6
  • 4-е = 3-е (6) + 4 = 10
  • И так далее.

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

«Парсинг» массива и вычисление закодированного значения слова

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

Из этого массива мы и «парсим» слова. Каждое слово в тексте обрамлено «кавычками», потому они и будут служить сигналом начала или окончания слова, переключая флаг start_word_fl.

Как только слово началось, числовые значения букв складываются между собой.

Чтобы получить порядковый номер буквы в алфавите, воспользуемся самим принципом их хранения в памяти. Символы ‘A’, ‘B’, ‘C’ и прочие хранятся в памяти устройства в виде числовых кодов: ‘A’ = 65, ‘B’ = 66 и так далее.

И работать с ними можно как с числами. Например: ‘A’ + 1 = ‘B’, ‘C’ – ‘A’ = 2 и т.п.

Потому порядковый номер буквы мы сможем получать вычитанием:

  • ‘A’ – 64 = 1
  • ‘B’ – 64 = 2
  • и так далее.

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

Значение слова обнуляется, подготавливаясь к вычислению следующего кода.

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

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

  • #include <time.h> 

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

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

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

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

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

Функция, которая открывает файл, выделяет место под массив и копирует содержимое в него (проект Эйлера - 42 задача)

Проект Эйлера (42 задача). Функция, которая открывает файл, выделяет место под массив и копирует содержимое в него
Функция, которая открывает файл, выделяет место под массив и копирует содержимое в него

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

Для хранения текста создадим структуру text. Она будет иметь два поля:

  • text.arr – ссылка на массив с текстом;
  • text.lenght – длина массива.

Это позволит не привязываться к фиксированной длине массива.

Сначала функция открывает файл на чтение при помощи fopen(). Если, однако, что-то пошло не так (например, неправильно указано название файла или путь к нему), то функция прервется и выведет предупреждение.

Далее с помощью функций fseek() и ftell() определяем длину файла и сохраняем в структуру.

С помощью функции malloc() выделяем в памяти массив нужной длины и указатель на него также сохраняем (text.arr).

Поскольку функция выделяет память, потому обязательно (!!!) следует после окончания работы с ней освобождать (используем free()). Так как память освобождается не внутри самой функции, потому напоминание об использовании malloc() записано прямо в названии:

  • оpen_file_malloc_ar()

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

Функция fgets() будет копировать данные в буфер до тех пор, пока не дойдет до конца файла и не вернет NULL.

Из буфера в массив информация будет копироваться при помощи fputs().

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

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

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

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

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

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

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

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

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

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

© 2023-2024 | eulerproject.ru