Flat Preloader Icon

Project Euler (answers)

Проект Эйлера: Ответ и решение 32 задачи (Пан-цифровые произведения)

Проект Эйлера (32 задача) может быть решена и простым перебором. Однако мы пойдем другим путем и получим ответ менее чем за 0.1 секунды.

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

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

Каждое n-значное число, которое содержит каждую цифру от 1 до n ровно один раз, будем считать пан-цифровым; к примеру, 5-значное число 15234 является пан-цифровым, т.к. содержит цифры от 1 до 5.

Произведение 7254 является необычным, поскольку равенство 39 × 186 = 7254, состоящее из множимого, множителя и произведения является пан-цифровым, т.е. содержит цифры от 1 до 9.

Найдите сумму всех пан-цифровых произведений, для которых равенство “множимое × множитель = произведение” можно записать цифрами от 1 до 9, используя каждую цифру только один раз.

ПОДСКАЗКА: Некоторые произведения можно получить несколькими способами, поэтому убедитесь, что включили их в сумму лишь единожды.
Всегда Вам рады))

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

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

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

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

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

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

Главная его проблема – в количестве этих чисел. Поскольку всего в данных пан-произведениях должно быть девять чисел (от 1 до 9), потому будут возможны следующие сочетания множителей:

  • a * bbbb = cccc;
  • aaaa * b = cccc;
  • aa * bbb = cccc;
  • aaa * bb = cccc

Видно, что в итоге один из множителей может быть четырехзначным. И потому придется перебирать два множителя от 1 до 10000. Что в итоге уже составит сто миллионов комбинаций. А ведь их нужно еще перемножать и считать в них цифры (и в их произведении тоже).

Чем лучше перестановка цифр

Более перспективным является перебор не чисел, а перестановок цифр в числе. Потому что, если первой перестановкой будет являться 123456789, а последней – 987654321, то это составит всего 9! комбинаций (около 360000).

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

[0,1,2,3,4,5,6,7,8,9]

[0,9,8,7,6,5,4,3,2,1]

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

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

Получив очередную перестановку цифр в массиве (смотрите скриншот выше):

  • [0,4,8,3,7,1,2,5,9,6]

… в двух циклах for() из цифр массива формируются два множителя: num1 и num2

  • num1 = 4, num2 = 8;

Они перемножаются 4* 8 и их произведение (32) начинает по одной цифре сравниваться со следующими цифрами массива:

  • младшая цифра произведения (32) – 2 сравнивается с [3] из массива.

Поскольку они не совпадают, потому num2 увеличивается, включая в себя следующую цифру:

  • num1 = 4, num2 = 38;

Их произведение 4 * 38 = 152 сравнивается таким же образом (2 c [7]).

После этого num2 опять увеличивается:

  • num1 = 4, num2 = 738;

Не будем тут расписывать – опять не подойдет.

  • num1 = 4, num2 = 1738;

Вот их произведение уже даст 4*1738 = 6952 и совпадет с массивом [2,5,9,6]. Обратите внимание, что цифры в числе 6952 сравниваются, начиная с младшего разряда (2->5->9->6).

Сохранение результатов

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

  • cash[число] = 1; // есть такое число
  • cash[число] = 0; // нет такого числа

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

Также результат засчитывается, только если произведение точно укладывается в массив:

  • product == 0 – после их сравнения в произведении не осталось цифр
  • ptr_prod == LEN_ARR – указатель на произведение точно соответствует длине массива

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

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

  • #include <time.h> 

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

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

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

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

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

Получение перестановок цифр в числе

Меняем систему исчисления
Рисунок 1: Меняем систему исчисления
Меняем систему исчисления
Рисунок 2: По порядковому номеру получаем перестановку

Меняем систему исчисления

Для простоты рассмотрим всего три цифры из примера в задании 12 и 3. Их можно переставить шестью различными способами:

123 -> 132 -> 213 ->231 -> 312 ->321

Однако как понять, почему цифры переставляются именно таком порядке?

Рассмотрим, как формируется первая (нулевая) выборка цифр – 123 (Рисунок 1):

  • Цифры в числе используются только один раз – не повторяются и как бы хранятся в отсортированном (!!!) по возрастанию массиве arr[] = {1,2,3}, уменьшающемся, когда из него забирают цифру:
  • [1,2,3] забираем 1 arr[0]
  • [2,3] забираем 2 arr[0] – не arr[1]!!!
  • [3] забираем 3 arr[0] – не arr[2]!!!
  • При этом по мере укорачивания массива индекс цифры (в массиве) может меняться и это важно!
  • Таким образом, чтобы получить число 123 требуется из массива [1,2,3] взять arr[0] == 1, из массива [2,3] взять arr[0] == 2, из массива [3] взять arr[0] == 3.
  • Вот это промежуточное число-из-массивов – 000 и нужно для дальнейших вычислений

Если остальные числа перестановки чисел 1 2 3 преобразовать подобным образом, вся перестановка обретает смысл и становится видна логика перестановки:

  • 123 -> 000
  • 132 -> 010
  • 213 -> 100
  • 231 -> 110
  • 312 -> 200
  • 321 -> 210

Получаем перестановку чисел по порядковому номеру

В итоге мы получаем шесть искомых перестановок в виде чисел-из-индексов. При этом нулевая перестановка будет 000, а последняя, пятая – 210 (Рисунок 2). Однако нужно понимать ряд особенностей такой системы:

  • В отличие от двоичной и десятичной систем исчисления цифры в каждом разряде числа-из-индексов  не будут иметь одинаковое максимальное значение. Так происходит потому, что каждый разряд забирает числа из массивов разный длины. Потому для старшего разряда максимальным может быть индекс 2 (массив из трех элементов [1,2,3]), а для младшего – 0 (массив из одного элемента [2])
  • Чтобы получить значение перестановки, необходимо сложить номера индексов, умноженные на факториалы максимального значения для данного разряда.

Например, для 4-й перестановки цифр 123, мы получаем число-из-индексов 200 (смотрите рисунок 2). Далее чтобы получить искомое значение перестановки промежуточное число 200 трансформируется следующим образом:

  • 2 00 -> [1,2,3] -> извлекаем arr[2] = 3
  • 2 0 0 -> [1,2] -> извлекаем arr[0] = 1
  • 20 0 -> [2] -> извлекаем arr[0] = 2

В итоге мы имеем следующую цепочку:

  • 4-я перестановка -> число-из-индексов 200 -> искомая перестановка 312

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

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

Функция для увеличения промежуточного числа на единицу

Поскольку, как указано выше, последняя ячейка всегда имеет значение 0, потому при получении следующего промежуточного числа сразу увеличиваем предпоследнюю ([LEN_ARR – 2]) ячейку:

  • [0,0,0,0,0,0,0,0,0,0] // нулевая перестановка, соответствует [0,1,2,3,4,5,6,7,8,9]
  • [0,0,0,0,0,0,0,0,1,0] // первая перестановка, [0,1,2,3,4,5,6,7,9,8]

При этом для (9-значных чисел)  максимально возможным промежуточным числом будет:

  • [0,9,8,7,6,5,4,3,2,1]

Если это число еще увеличить, то получим:

  • [1,0,0,0,0,0,0,0,0,0]

… ненулевое значение в левой ячейке. Это в итоге  послужит сигналом о превышении длины числа. 

Именно по этому сигналу в программе останавливается перебор комбинаций цифр.

Функция для вычисления пан-цифрового числа по массиву с индексами перестановок

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

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

Из отсортированного массива берется число с соответствующим индексом, а остальные числа смещаются.

  • {>0<,1,2,3}  // берем число (0) из отсортированного массива
  • [>0<,1,1,0] // по индексу 0 в массиве с промежуточным числом-из-индексов
  • [0] // записываем число в массив с ответом
  • {1,2,3}  // смещаем цифры в отсортированном списке
  • {1,>2<,3}  // берем число (2) из отсортированного массива
  • [0,>1<,1,0] // по индексу 1
  • [0,2] // записываем число в массив 
  • {1,3}  // смещаем цифры
  • {1,>3<}  // берем число (3) из отсортированного массива
  • [0,1,>1<,0] // по индексу 1
  • [0,2,3] // записываем число в массив 
  • {1}  // смещаем цифры
  • {>1<}  // берем число (1) из отсортированного массива
  • [0,1,1,>0<] // по индексу 1
  • [0,2,3,1] // получаем искомую перестановку цифр

Также как и в предыдущей функции здесь предусмотрен сигнал о превышении длины 9-значного числа.

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

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

В итоге ответ на 32 задачу проекта Эйлера составил 45228, программа считает ответ около 0.1 секунды. 

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

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

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

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

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

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

© 2023-2024 | eulerproject.ru