Мультиинструментальный Brute Force

Решение задачи из Project Euler с помощью 1С, а также дополнительных программ, серверов и прочих хитростей.

В одной из задач на Project Euler (здесь ссылка на русскую версию) предлагается найти простые 10-значные числа с повторениями. Под повторением понимаются не просто идущие подряд цифры, а все одинаковые цифры, которые встречаются в записи числа. Интересуют простые числа с максимальным количеством повторений. Для цифры 4 существует единственное простое число — 4444444447, в котором четверка повторяется 9 раз. Другим примером чисел, которые удовлетворяют условиям задачи, являются  7778777777 и 17777777777. Максимальное количество повторений семерки в 10-значном простом числе равно 9.

Оценим сложность задач. Для начала определим  размер области поиска, а именно, сколько существует простых чисел в диапазоне 10#k8SjZc9Dxk9…10#k8SjZc9Dxk10.  Для этого заглянем на сайт http://www.wolframalpha.com/ и в поле ввода наберем выражение primepi(10#k8SjZc9Dxk10)-primepi(10#k8SjZc9Dxk9). Функция primepi(x)  возвращает количество простых чисел,  не превосходящих х.

primepi

И так наши иголки мы будем искать в стоге из более чем 404 млн. простых чисел. Следующий вопрос — как эти числа получить?

Оказывается, есть люди, которым интересно разрабатывать программы для генерации простых чисел, более того они выкладывают свои разработки в открытом доступе.  Достижения автора поражают, его код работает черезвычайно быстро. Мы воспользуемся 64-х разрядным консольным приложением. Начнем с проверки количества простых чисел в интервале поиска. Ответ совпадает с полученным ранее, чего и следовало ожидать.

primesieve

Это замечательное консольное приложение может находить простые числа в заданном диапазоне и сохранять их во внешний файл. На приведенном изображении мы ищем простые числа в интервале 4444444447,…4444444447+1 и выводим результат сначала на экран, затем в файл.

Prime number in interval

Если сохранить список чисел из диапазона поиска в одном файле, то он будет занимать на диске  4,51 Гб. При таких размерах приложение Блокнот отказывается  файл открывать. А ведь нам надо в дальнейшем обработать полученные числа. Поэтому будем выводить данные порциями с шагом 10#k8SjZc9Dxk8, начиная с 10*10#k8SjZc9Dxk8. Результаты сохраним не в текстовый файл, а в базу данных на SQL сервере. На первом шаге создадим в базе данных 90 таблиц  c именами  PrimeNumber10,PrimeNumber11,…,PrimeNumber99, которые содержат единственный столбец [output] с типом (nvarchar(10),NULL). Таблицы  сформируем с помощью простого кода в 1С.

for Num=10 to 99 do
Text="CREATE TABLE [dbo].[PrimeNumber"+num+"]([output] [nvarchar](10) NULL) ON [PRIMARY]";
adoConnect.Execute(Text);
enddo

Здесь adoConnect — это com-объект (COMОбъект(«ADODB.Connection»)).

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


Text="DECLARE @start sysname, @step sysname,@cmd sysname;
|
|SET @start='[Num]e8';
|SET @step='-o1e8';
|
|SET @cmd='...primesieve.exe '+@start+' +@step+' -p'
|
|delete from PrimeNumber[Num]
|
|Insert PrimeNumber[Num] (output)
|EXEC xp_cmdshell @cmd;";

[Num] — это параметр с номером таблицы. Команда выполняется на сервере, поэтому и консольное приложение для генерации простых чисел следует разместить там же. Кроме этого, в настройках сервера необходимо разрешить выполнение команды xp_cmdshell. Запускать вручную данный скрипт мы не будем, а призовем на помощь фоновые задания. Создадим общий модуль ФоновыеЗапросы и пропишем в нем следующую функцию:

Function  BackGroundQuery(Text) Export

adoConnect = new COMОбъект("ADODB.Connection");

ConnectString="Driver={SQL Server};Server=...;Database=...;Trusted_Connection=yes";

adoConnect.ConnectionTimeout = 1000;
adoConnect.CommandTimeout = 1000;
adoConnect.Open(ConnectString);

adoConnect.Execute(Text);

adoConnect.Close();

EndFunction 

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

for i=10+iStart to 99 do
query=СтрЗаменить(Text,"[Num]",i);
param=new array;
param.Add(query);
UID=new УникальныйИдентификатор;
mKey=строка(UID);
BgTask=ФоновыеЗадания.Execute(
"ФоновыеЗапросы.BackGroundQuery",
param,mKey,
"Fill prime table i="+i);
i=i+9;
enddo;

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

Теперь, когда у нас есть исходные данные, можно приступить к извлечению нужных чисел. Для этого воспользуемся поиском по маске через оператор LIKE.  Для цифры 0 маска будет единственная: ‘_00000000_’. При этом искать числа надо только в таблицах с номерами 10,20,30… Всего таких чисел 8. Они приведены ниже.

1000000007
1000000009
4000000007
4000000009
6000000001
6000000007
7000000001
9000000001

Как показали дальнейшие исследования, максимальное количество повторяющихся цифр равно либо 9, либо 8. В первом случае надо использоваь маски вида:

‘_NNNNNNNNN’

‘N_NNNNNNNN’

‘NN_NNNNNNN’

………………………………………………….

‘NNNNNNNNN_’

Здесь N — цифра в интервале 1..9. Причем для четных цифр остается одна единственная маска — ‘NNNNNNNNN_’.

Во втором случае количество возможных масок — 45. Данные маски потребовалось применять для цифр 2 и 8. Тот факт, что эти цифры четные, позволило уменьшить количество перебираемых вариантов. В таблице приведена статистика для всех цифр. Первый столбец содержит повторяющуюся цифру, второй- максимальное количество повторений, третий — количество простых 10-значных чисел c повторением указанной цифры.

  Цифра  Кол-во повторений Количество простых чисел
0 8  8
1 9  12
2 8  39
3 9  7
4 9  1
5 9  1
6 9  1
7 9  9
8 9  32
9 9  8

Полученное  в  результате изложенного мультиинструментального BRUTE FORCE метода значение суммы простых чисел с заданным свойством оказалось правильным и позволило автору увеличить счет решенных задач.

Разумеется, данный метод не является оптимальным. Учитывая то, что консольное приложение позволяет проверить число на простоту, эффективнее было бы генерировать числа-кандидаты и отбирать из них  те, которые являются простыми. Тем не менее, приведенный подход позволяет расширить свой арсенал   и сделать будущую работу более продуктивной.

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

 

Номер строки треугольника Сумма искомых чисел
20 000 17 399 896 193
50 000 232 499 865 696
100 000 549 999 566 882
200 000 8 980 000 676 761
500 000 98 375 000 264 623
1 000 000  463 999 977 061 648

 

3 Comments

  1. vano-ekt

    Почувствуй себя хакером.

    Win+R

    cmd
    color 2
    cd \r
    dir /S /W
    
    Reply
  2. delete

    Напрашивается картинка про троллейбус из буханки

    Reply
  3. Chrizt

    Отлично! Очень понравился подход автора к решению задачи.

    Так и представилась картина: «Project Euler на 1С? CHALLENGE ACCEPTED! ]:-)»

    Кстати, пару интересностей для себя урвал, спасибо Вам!

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *