gifts2017

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

Опубликовал Валерий (scientes) в раздел Программирование - Теория программирования

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

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

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

primepi

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

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

primesieve

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

Prime number in interval

Если сохранить список чисел из диапазона поиска в одном файле, то он будет занимать на диске  4,51 Гб. При таких размерах приложение Блокнот отказывается  файл открывать. А ведь нам надо в дальнейшем обработать полученные числа. Поэтому будем выводить данные порциями с шагом 10^8, начиная с 10*10^8. Результаты сохраним не в текстовый файл, а в базу данных на 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

 

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Ivan Khorkov (vano-ekt) 30.10.15 14:28
Почувствуй себя хакером.

Win+R
cmd
color 2
cd \
dir /S /W
...Показать Скрыть
2. delete (delete) 31.10.15 13:21
Напрашивается картинка про троллейбус из буханки
3. Алексей Крайст (Chrizt) 05.11.15 12:25
Отлично! Очень понравился подход автора к решению задачи.
Так и представилась картина: "Project Euler на 1С? CHALLENGE ACCEPTED! ]:-)"

Кстати, пару интересностей для себя урвал, спасибо Вам!
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа