Задачка. Функция Капрекара

В этом форуме обсуждаем общие вопросы и проблемы языка, решаем задачи на разных диалектах, делимся успехами и неудачами его применения.
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

TRust писал(а): Сб апр 19, 2025 10:54 pm
Unanonimized писал(а): Сб апр 19, 2025 3:39 pm ...единственное, что вызывает сомнения = это применение reverse для обратной сортировки
Чтобы совсем снять все сомнения, предлагаю вместо reverse использовать здесь:

Код: Выделить всё

Regexp_replace(Orderly, '(\d)(\d)(\d)(\d)', '\4\3\2\1')
Супер!
Записываю в свою "копилку"!
Спасибо!
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

Моё решение для СУБД
SQLite
3.47.0 2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e
zlib version 1.3, msvc-1939 (64-bit)

Не использовались никакие расширения SQLite.

Код: Выделить всё

.echo on
.version
.timer on

-- Промежуточное VIEW
DROP VIEW IF EXISTS kaprekar_6174;
CREATE VIEW kaprekar_6174
AS
WITH RECURSIVE t AS (
-- Натуральные числа от 1 до 9999 (9999 штук)
SELECT 1 AS n UNION ALL SELECT n+1 FROM t WHERE n < 9999)
, f AS (-- Нарезка числа по четырём цифрам и их позициям
SELECT n, 1 AS pos, substr(n, 1, 1) AS f FROM t UNION ALL
SELECT n, 2       , substr(n, 2, 1)      FROM t UNION ALL
SELECT n, 3       , substr(n, 3, 1)      FROM t UNION ALL
SELECT n, 4       , substr(n, 4, 1)      FROM t)
--
 , nums AS ( -- Генерация всех 24-х возможных чисел перестановкой четырех цифр
SELECT DISTINCT f1.n
     , f1.f AS f1, f1.pos AS f1_pos
     , f2.f AS f2, f2.pos AS f2_pos
     , f3.f AS f3, f3.pos AS f3_pos
     , f4.f AS f4, f4.pos AS f4_pos
     , CAST(f1.f||f2.f||f3.f||f4.f AS integer) AS num 
FROM f f1 JOIN f f2 ON f1.n = f2.n -- Это цифры
          JOIN f f3 ON f1.n = f3.n -- одного и того же
          JOIN f f4 ON f1.n = f4.n -- числа
WHERE -- Число не состоит из одинаковых цифр
      NOT (f1.f = f2.f AND f2.f = f3.f AND f3.f = f4.f) 
  -- Каждая цифра участвует в перестановке только один раз
  AND NOT (f1.pos = f2.pos OR f1.pos = f3.pos OR f1.pos = f4.pos
                           OR f2.pos = f3.pos OR f2.pos = f4.pos
                                              OR f3.pos = f4.pos) )
--
 , res AS (
SELECT n, count(*) AS cnt -- перестановка 4 цифр. Кол-во перестановок = 4! = 1*2*3*4 = 24
     , MAX(num) AS max_num, MIN(num) as min_num
     , MAX(num)-MIN(num) AS diff 
FROM nums group by n)
--
SELECT n, cnt, min_num
       -- Наибольшее значение дополняем справо нулём (умножаем на 10), если оно меньше 1000
     , IIF(max_num < 1000, max_num*10, max_num) AS max_num
     , IIF(max_num < 1000, max_num*10, max_num) - min_num AS diff
FROM res;


-- Результат
WITH recursive KAP AS(
SELECT n AS for_n
     , CAST(n AS text) AS tree_path_n
     , 0 as steps
     , n, diff 
--     , cnt, max_num, min_num
FROM kaprekar_6174
WHERE n > 1000  
UNION ALL
SELECT EH.for_n
     , EH.tree_path_n || ' -> ' || E.n
     , EH.steps+1
     , E.n, E.diff 
--     , E.cnt, E.max_num, E.min_num
FROM kaprekar_6174 E
     JOIN KAP EH ON E.n = EH.diff AND EH.n <> EH.diff)
--
SELECT for_n       AS "Число"
     , tree_path_n AS "Цепочка"
     , steps       AS "Шагов"
FROM kap
WHERE tree_path_n LIKE '%6174'
ORDER BY for_n
;
Время выполнения 2.08 сек/
Скрипт и листинг его выполнения прилагаются в архиве Solution_09_SQL-Plus_01.zip

Для запуска использовал команду:
D:\SQLite\3.47\sqlite3.exe -table < Solution_09_SQL-Plus_01.sql > Solution_09_SQL-Plus_01.lst 2>&1

P. S. Это решение я нашел до того, как опубликовал условия задачи.
Нашел я его не мгновенно. Пришлось подумать, попробовать разные пути.
Улучшил познания в Recursive Subquery Factoring.
После того, как началась публикация других решений, я ничего не исправлял и не улучшал.
Вложения
Solution_09_SQL-Plus_01.zip
(56.5 КБ) 2 скачивания
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

Unanonimized писал(а): Пт апр 18, 2025 3:28 am Набросок 1 - словарь и консервативный connect by

Код: Выделить всё

with dataset(val, v) as 
( select d.val, l.v
                                                               4
...
Время выполнения 0.23 с

Решение принято
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

TRust писал(а): Сб апр 19, 2025 12:48 am Ну и, как мне кажется, самое лаконичное решение
Oracle

Код: Выделить всё

with
N1 as (Select To_char(level, 'FM0000') D from dual connect by level<10000),
. . .
P.S. Я знаю, что это решение не будет принято. Но поступиться первой тысячью строк не могу. Извините! Это принципиально!
Время выполнения 1.46 сек

Решение не принято

(1) В диапазоне от 1001 по 9998 значения получены правильные.
Но тысяча строк лишние с 1 по 1000. (Автор поступиться ими не мог! :-))
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

Unanonimized писал(а): Сб апр 19, 2025 12:58 am
SQL*Plus писал(а): Пт апр 18, 2025 1:35 pmCTE, в полный рост..." не принимается.
Ах значит вы воооо как?!
Ну и пажжалста!

Код: Выделить всё

with dataset(val) as  (select rownum val from dual connect by level < 10000)
, dsplit as (select val, mod(trunc(val/1000), 10) a, mod(trunc(val/100), 10) b
. . .
          model reference kap on (select * from dic) dimension by (val) measures(k)
          dimension by (val) 
          measures (val v, cast(val||'' as varchar2(50)) chain, 0 steps)
          rules iterate (7)
. . .
Время выполнения 0.98 сек

Решение принято
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

Unanonimized писал(а): Сб апр 19, 2025 7:29 pm Решил зайти еще с одной стороны - не пытаться искать порядок в хаосе, с сгенерировать хаос из порядка.
Заодно операции над множествами вспомнил.

Код: Выделить всё

with digits(d) as (select * from xmltable('0to 9'columns"."int))
, kap(a,b,c,d,k) as (-- Собираем нулевые перестановки 4 чисел и считаем для них Капрекара
. . .
order by 1
Время выполнения 1.31 сек

Решение принято
Аватара пользователя
SQL*Plus
Сообщения: 321
Зарегистрирован: Ср апр 20, 2022 1:09 pm

Re: Функция Капрекара

Сообщение SQL*Plus »

TRust писал(а): Пт апр 18, 2025 9:07 am PostgreSQL

Код: Выделить всё

with recursive Chain (Num, Cur, Lev) as (
  Select N, to_char(N, 'FM0000'), 0 from generate_series(1, 9999) N
  union all
  Select Num, To_Char(abs(
    (select String_agg(N,'' order by N)::integer from unnest(string_to_array(Cur::text, null)) N) -
. . .
having min(cur) > '0000'
Время выполнения 0.5 сек (в DBeaver)

Решение не принято

В диапазоне 1001 - 9998 всё ОК.

(1) лишние строки 1-1000
(2) Нет явной сортировки ORDER BY
Ответить