Как найти шестизначные числа, которые делятся на свое обратное значение (перевертыш), в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике программирования на платформе 1С иногда возникают задачи, требующие не только знания объектов метаданных, но и навыков построения эффективных алгоритмов для перебора и анализа чисел. Рассмотрим классическую математическую задачу: найти все шестизначные числа, которые не делятся на 10 (не заканчиваются на ноль), не являются палиндромами, но при этом делятся на число, записанное теми же цифрами в обратном порядке.

Проанализируем ситуацию: нам нужно найти такие числа abcdef, для которых abcdef = k * fedcba, где k — целое число больше единицы. Разберем несколько способов реализации этой задачи: от простого перебора до использования языка запросов и математической оптимизации.

Способ 1. Простой перебор (Брутфорс)

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


Функция ПолучитьРазвороченноеЧисло(Знач ЧислоИсточник)
    Результат = 0;
    Пока ЧислоИсточник <> 0 Цикл
        Остаток = ЧислоИсточник % 10;
        Результат = Результат * 10 + Остаток;
        ЧислоИсточник = (ЧислоИсточник - Остаток) / 10;
    КонецЦикла;
    Возврат Результат;
КонецФункции

Процедура НайтиЧислаБрутфорсом()
    Для ТекущееЧисло = 100001 По 999999 Цикл
        // Число не должно делиться на 10 по условию
        Если ТекущееЧисло % 10 = 0 Тогда
            Продолжить;
        КонецЕсли;

        ОбратноеЧисло = ПолучитьРазвороченноеЧисло(ТекущееЧисло);

        // Проверяем, что число делится на свой перевертыш и не равно ему
        Если ТекущееЧисло <> ОбратноеЧисло И ОбратноеЧисло <> 0 Тогда
            Если ТекущееЧисло % ОбратноеЧисло = 0 Тогда
                Сообщить("Найдено число: " + ТекущееЧисло + " / " + ОбратноеЧисло + " = " + (ТекущееЧисло / ОбратноеЧисло));
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

Способ 2. Оптимизация через работу с двоичными данными

Рассмотрим необычный и эффективный способ разворота строки в 1С, предложенный опытными разработчиками. Вместо математического цикла можно использовать возможности платформы по работе с буфером двоичных данных. Метод Перевернуть() у объекта БуферДвоичныхДанных работает крайне быстро.


Процедура НайтиЧислаЧерезБуфер()
    ТекущееЧисло = 100001;
    Пока ТекущееЧисло <= 999999 Цикл
        Если ТекущееЧисло % 10 <> 0 Тогда
            // Превращаем число в строку, затем в буфер двоичных данных
            Буфер = ПолучитьБуферДвоичныхДанныхИзСтроки(XMLСтрока(ТекущееЧисло));
            // Инвертируем порядок байтов (символов) в буфере
            ОбратноеЧисло = Число(ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер.Перевернуть()));

            Если ТекущееЧисло <> ОбратноеЧисло И ТекущееЧисло % ОбратноеЧисло = 0 Тогда
                Сообщить("" + ТекущееЧисло + " / " + ОбратноеЧисло + " = " + ТекущееЧисло / ОбратноеЧисло);
            КонецЕсли;
        КонецЕсли;
        ТекущееЧисло = ТекущееЧисло + 1;
    КонецЦикла;
КонецПроцедуры

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

Способ 3. Решение с помощью языка запросов

Проанализируем ситуацию, когда нам нужно найти такие числа средствами SQL-движка. В 1С это можно сделать через формирование временной таблицы цифр и их последующее соединение «само на себя» шесть раз. Это позволит нам сконструировать число и его перевертыш прямо в запросе.


ВЫБРАТЬ 0 КАК Разряд ПОМЕСТИТЬ Разряды ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 
ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 
ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВсеЧисла.ТекущееЧисло КАК ТекущееЧисло,
    ВсеЧисла.ОбратноеЧисло КАК ОбратноеЧисло
ИЗ
    (ВЫБРАТЬ
        R1.Разряд + 10 * (R2.Разряд + 10 * (R3.Разряд + 10 * (R4.Разряд + 10 * (R5.Разряд + 10 * R6.Разряд)))) КАК ТекущееЧисло,
        R6.Разряд + 10 * (R5.Разряд + 10 * (R4.Разряд + 10 * (R3.Разряд + 10 * (R2.Разряд + 10 * R1.Разряд)))) КАК ОбратноеЧисло
    ИЗ
        Разряды КАК R1, Разряды КАК R2, Разряды КАК R3, Разряды КАК R4, Разряды КАК R5, Разряды КАК R6
    ГДЕ
        R6.Разряд <> 0 И R1.Разряд <> 0) КАК ВсеЧисла
ГДЕ
    ВсеЧисла.ТекущееЧисло <> ВсеЧисла.ОбратноеЧисло
    И ВсеЧисла.ТекущееЧисло % ВсеЧисла.ОбратноеЧисло = 0

Этот запрос генерирует все возможные комбинации цифр. Условие R6.Разряд <> 0 гарантирует, что число шестизначное, а R1.Разряд <> 0 — что число не делится на 10. Подобный подход удобен тем, что расчеты переносятся на сторону СУБД.

Математическая справка и глубокая оптимизация

Выясним причину, по которой поиск можно ускорить еще сильнее. В теории чисел доказано, что для десятичной системы счисления множитель k (результат деления) для таких чисел может принимать только два значения: 4 или 9.

  1. Если множитель равен 9, то искомое число должно начинаться на 1 и заканчиваться на 9 (так как 1...9 * 9 = 9...1).
  2. Если множитель равен 4, то число должно начинаться на 2 и заканчиваться на 8 (так как 2...8 * 4 = 8...2).

Все подобные числа строятся вокруг базовых палиндромных кратных: 1089 * 9 = 9801 и 2178 * 4 = 8712. Для шести знаков такими числами являются:

Проанализировав эти свойства, мы понимаем, что вместо перебора 900 000 чисел достаточно проверить только те, что кратны 9, и начинаются на 1 или 2. Это превращает задачу из «тяжелого» перебора в мгновенное вычисление.

В результате мы рассмотрели три различных подхода к решению: классический алгоритм 1С, оптимизированный метод с двоичными данными и мощь языка запросов. Использование математических свойств чисел позволяет сократить время работы программы с секунд до миллисекунд.

← На главную