Flat Preloader Icon

Project Euler (answers)

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

Проект Эйлера (41 задача). Самый быстрый способ решить эту задачу – сразу вычислять пан-цифровые числа, не перебирая весь числовой ряд. 

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

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

Будем считать n-значное число пан-цифровым, если каждая из цифр от 1 до n используется в нем ровно один раз. К примеру, 2143 является 4-значным пан-цифровым числом, а также простым числом.

Какое существует наибольшее n-значное пан-цифровое простое число?

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

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

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

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

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

Основная идея алгоритма

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

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

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

После чего следует проверять восьмизначные числа: от 87654321 до 12345678, потом семизначные и так далее.

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

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

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

Для простоты рассмотрим всего три цифры 123

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

Алгоритм работы программы

Для хранения пан-цифрового числа создадим структуру. В ней будет храниться:

  • само число – 987654321;
  • длина числа – 9;
  • массив индексов [0,8,7,6,5,4,3,2,1,0] для получения данного пан-цифрового числа из отсортированного массива [1,2,3,4,5,6,7,8,9];

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

Как конкретно инициализируется и декрементируется  структура будет описано ниже, в описании соответствующих функций.

Если среди чисел нет простого, то при достижении минимально возможного (девятизначного!) пан-цифрового числа (123456789), функция pandig_decrement() вернет 0 и цикл while() прервется.

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

  •  num_init %= div
  • 987654321 % 10000000 = 87654321

Структура инициализируется данным восьмизначным числом и цикл идет заново.

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

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

  • #include <time.h> 

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

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

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

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

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

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

Функция для инициализации пан-цифрового числа, часть 1
Функция для инициализации пан-цифрового числа, часть 1
Функция для инициализации пан-цифрового числа, часть 2
Функция для инициализации пан-цифрового числа, часть 2

Функция для инициализации пан-цифрового числа

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

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

Однако лучше пояснить на простом примере.

Внесение индексов цифр числа в массив

Вносим пан-цифровое число 213 в структуру.

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

Т.е. в массив индексы будут вноситься не с начала:

  • [->0<-,0,0,0,0,0,0,0,0,0]

… а начиная с седьмого:

  • [0,0,0,0,0,0,0,->0<-,0,0]

Отрезаем старшую цифру от вносимого числа:

  • 213 / 100 = 2

Определяем индекс этой цифры в отсортированном массиве:

  • [1,->2<-,3,4,5,6,7,8,9] //индекс равен 1.

Заносим этот индекс в массив для хранения индексов чисел в структуре:

  • [0,0,0,0,0,0,0,->1<-,0,0]

Удаляем использованную цифру в отсортированном массиве и смещаем следующие за ней влево:

  • [1,<<<,3,4,5,6,7,8,9] -> [1,3,4,5,6,7,8,9,9]

Отрезаем следующую цифру от числа:

  • 13 / 10 = 1

Ее индекс в отсортированном массиве будет 0:

[->1<-,3,4,5,6,7,8,9,9]

Заносим индекс (0) в массив структуры и убираем цифру (1) из отсортированного массива:

  • [0,0,0,0,0,0,0,1,->0<-,0]
  • [<<<,3,4,5,6,7,8,9,9] -> [3,4,5,6,7,8,9,9,9]

На следующей итерации заносим индекс последней цифры – 3:

  • [0,0,0,0,0,0,0,1,0,->0<-]
  • [<<<,4,5,6,7,8,9,9,9] -> [4,5,6,7,8,9,9,9,9]

Проверка корректности внесенного в структуру числа

Каждая занесенная цифра числа помечается в массиве fl_ar[] по индексу:

  • fl_ar[3] = 1; // цифра 3 была занесена в число
  • fl_ar[4] = 0; // цифра 4 не использовалась

После занесения всех цифр функция проверяет, что все цифры от 1 до len (длина числа), были занесены и потому число является пан-цифровым.

Функция для получения следующего меньшего пан-цифрового числа из данных цифр

Проект Эйлера (41 задача) Функция для получения следующего меньшего пан-цифрового числа из данных цифр, часть 1
Функция для получения следующего меньшего пан-цифрового числа из данных цифр, часть 1

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

Максимально возможное число 987654321 потому будет иметь массив с индексами [0,8,7,6,5,4,3,2,1,0]

Минимально возможное (девятизначное) 123456789: [0,0,0,0,0,0,0,0,0,0].

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

  • [0,8,7,6,5,4,3,2,1,0] // число 987654321
  • [0,8,7,6,5,4,3,2,0,0]  // число 987654312
  • [0,8,7,6,5,4,3,1,1,0] // число 987654231
  • [0,8,7,6,5,4,3,1,0,0] // число 987654213
  • и так далее.
По мере уменьшения “числа-из-индексов”, оно будет укорачиваться:
  • [0,0,7,6,5,4,3,2,1,0]
  • [0,0,0,6,5,4,3,2,1,0]

Потому для корректной работы необходимо определять индекс “последнего нуля”, после которого уже не нужно проводить операцию декрементирования.

Однако в итоге массив полностью заполнится нулями. И это значит, что достигнута минимально возможная перестановка. Функция в таком случае прервется и вернет ноль.

Проект Эйлера (41 задача) Функция для получения следующего меньшего пан-цифрового числа из данных цифр, часть 2
Функция для получения следующего меньшего пан-цифрового числа из данных цифр, часть 2

Декрементирование цифр в массиве

Значение которое могут принимать числа в массиве ограничено их максимальными значениями:

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

Так происходит, потому что при изъятии числа из отсортированного массива:

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

… он укорачивается.

При декрементировании цифр в массиве уменьшается значение предпоследней ячейки:

  • [0,8,7,6,5,4,3,2,->1<-,0] -> 
  • [0,8,7,6,5,4,3,2,->0<-,0]

Если это невозможно, и ячейка имеет нулевое значение:

  • [0,8,7,6,5,4,3,2,->0<-,0]

…декрементируется следующая (старшая) ячейка

  • [0,8,7,6,5,4,3,->2<-,0,0] -> [0,8,7,6,5,4,3,->1<-,0,0]

…а младшая принимает максимально возможное для данной позиции значение:

  • [0,8,7,6,5,4,3,1,->0<-,0] -> [0,8,7,6,5,4,3,1,->1<-,0]

Формирование пан-цифрового числа

После получения массива с индексами формируем следующее (меньшее) число. 

Например, мы имеем массив [0,1,2,3,4,4,3,2,1,0].

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

Берем первую цифру из отсортированного массива по его индексу (1):

  • [0,->1<-,2,3,4,4,3,2,1,0] // индекс цифры (1)
  • [1,->2<-,3,4,5,6,7,8,9] // по индексу 1 расположена цифра 2
  • [1,<<<,3,4,5,6,7,8,9] -> [1,3,4,5,6,7,8,9,9] // смещаем цифры в отсортированном массиве
  • Получаем число 2

Берем цифру со следующим индексом:

  • [0,1,->2<-,3,4,4,3,2,1,0] // индекс цифры (2)
  • [1,3,->4<-,5,6,7,8,9,9] // по индексу 2 расположена цифра 4
  • [1,3,<<<,5,6,7,8,9,9] -> [1,3,5,6,7,8,9,9,9] // смещаем цифры в отсортированном массиве
  • Увеличиваем на порядок число 2 -> 20 и прибавляем к нему полученную цифру 20 + 4 = 24.
  • и так далее.

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

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

Данная функция перебирает делители числа. Если число не делится нацело ни на один, то оно – простое.

Особенности функции:

  • Делители проверяются только до квадратного корня из проверяемого число. Потому что, если делитель до этого момента не нашелся, то и дальше его не будет.
  • Вычисление квадратного корня вынесены перед циклом for(), потому что иначе некоторые компиляторы вычисляют квадратный корень на каждой итерации.
  • Числа 0 и 1 не являются простыми, потому сразу возвращается false.

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

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

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

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

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

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

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

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

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

© 2023-2024 | eulerproject.ru