двоично-десятичные палиндромы
От: Кодт Россия  
Дата: 16.12.15 11:18
Оценка: 5 (1)
По мотивам пары недавних статей на хабре. (Ну и для тех, кто не читал эти статьи, тем более может быть интересно — размять голову).

Постановка задачи:

1. d-ричный палиндром — это число, чьё d-ричное представление без ведущих нулей является, внезапно, палиндромом. Например, 12321 и 123321 — палиндромы, 12321000 — не палиндром.
2. Двоично-десятичный палиндром — число, являющееся и двоичным, и десятичным палиндромом.
0. Считать 0 палиндромом или нет — дело вкуса.

Задача: найти n-ный двоично-десятичный палиндром.

Брутфорс
def is_palindrom_str(s):
  return s == s[::-1]

def is_decimal_palindrom(x):
  return is_palindrom(dec(x))

def is_binary_palindrom(x):
  return is_palindrom(bin(x)[2:]) # '0b.....'

def is_bindec_palindrom(x):
  return is_decimal_palindrom(x) and is_binary_palindrom(x)

def nth_bindec_palindrom(n):
  k = 0
  x = 0
  while True:
    if is_bindec_palindrom(x):
      if k == n:
        return x
      k += 1
    x += 1
  # можно в одну строку
  return next(itertools.islice(itertools.ifilter(is_bindec_palindrom, itertools.count(0)), n,None))

Это ОЧЕНЬ медленно.

Предложите наиболее быстрый способ.
Перекуём баги на фичи!
Re: двоично-десятичные палиндромы
От: _DAle_ Беларусь  
Дата: 16.12.15 12:23
Оценка:
Здравствуйте, Кодт, Вы писали:

К>По мотивам пары недавних статей на хабре. (Ну и для тех, кто не читал эти статьи, тем более может быть интересно — размять голову).


К>Предложите наиболее быстрый способ.


Как-то нет сейчас времени особо подумать, но первое, что можно сделать — это перебирать половинки (n/2 цифр для четной длины, n/2+1 для нечетной) 10-палиндромов, зеркально отражать, а потом проверять на 2-палиндром. Во всяком случае по сравнению с тупым брутфорсом — это существенно быстрее. Потом я бы попробовал сгенерировать эти 2-10-палиндромы и посмотреть на них
Отредактировано 16.12.2015 12:44 _DAle_ . Предыдущая версия .
Re[2]: двоично-десятичные палиндромы
От: Кодт Россия  
Дата: 16.12.15 13:18
Оценка:
Здравствуйте, _DAle_, Вы писали:

_DA>Как-то нет сейчас времени особо подумать, но первое, что можно сделать — это перебирать половинки (n/2 цифр для четной длины, n/2+1 для нечетной) 10-палиндромов, зеркально отражать, а потом проверять на 2-палиндром. Во всяком случае по сравнению с тупым брутфорсом — это существенно быстрее. Потом я бы попробовал сгенерировать эти 2-10-палиндромы и посмотреть на них


O(P^(1/2)) против O(P), конечно. (Где P — значение n-ного палиндрома).

Причём генерировать палиндром из половинки можно двумя способами:
— разложением каждого получисла на разряды (задействуя деление)
— формируя последовательность палиндромов как сложение арифметических прогрессий (задействуя только сложение)

А вот как генерировать сразу 2-10-палиндром, это интересный вопрос.
Перекуём баги на фичи!
Re[3]: двоично-десятичные палиндромы
От: _DAle_ Беларусь  
Дата: 16.12.15 14:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Причём генерировать палиндром из половинки можно двумя способами:

К>- разложением каждого получисла на разряды (задействуя деление)
К>- формируя последовательность палиндромов как сложение арифметических прогрессий (задействуя только сложение)

Ну, самый, имхо, простой способ — это работать просто с массивом разрядов, единственное, что в двоичное представление будет дольше перевести. А с помощью арифметических прогрессий — это как?
Re: двоично-десятичные палиндромы
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 16.12.15 14:30
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Предложите наиболее быстрый способ.


Во первых, палиндром должен быть нечётным.

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

12321, какой следующий? Первая половина 123, добавляем единицу получаем 124, строим палиндром 12421. Таким образом мы будем быстро получать десятичные палиндромы в порядке возрастания: 12521, 12621, 12721, 12821, 12921, 13031, 13131, ...

В-третьих, с учётом во-первых, если старший равен четному числу, то его надо увеличить его ещё на единицу. Если получили 20002, то переходим на 30003.



Осталось только проверить, будет ли палиндром двоичным? Как-то так:

Если 1 или 11, то палиндром (оптимизация).
Обнулить нулевой бит (одна команда)
Получить индекс самого старшего бита (одна машинная инструкция) и обнулить его.
Если получили нуль, то палиндром.
Получить индекс самого младшего установленного бита.
Вычислить индекс бита, который должен быть самым старшим установленным. Например, Если самый старший установленный бит был 12, а новый самый младший 3, то старший будет 9
Далее, проверить, что все биты, старше девятого, установлены в нуль, например, при помощи OR. Если проверка зафейлилась, то не палиндром.
Сдвинуть число вправо на 3 и goto на начало.
Отредактировано 16.12.2015 14:37 Mystic . Предыдущая версия .
Re: двоично-десятичные палиндромы
От: Chorkov Россия  
Дата: 16.12.15 14:57
Оценка:
Здравствуйте, Кодт, Вы писали:

К>По мотивам пары недавних статей на хабре. (Ну и для тех, кто не читал эти статьи, тем более может быть интересно — размять голову).


К>Постановка задачи:


К>1. d-ричный палиндром — это число, чьё d-ричное представление без ведущих нулей является, внезапно, палиндромом. Например, 12321 и 123321 — палиндромы, 12321000 — не палиндром.

К>2. Двоично-десятичный палиндром — число, являющееся и двоичным, и десятичным палиндромом.
К>0. Считать 0 палиндромом или нет — дело вкуса.

К>Задача: найти n-ный двоично-десятичный палиндром.


А число таких палиндромов не конечно?
Вероятность, что число P является десятичным палиндромом, убывает с ростом P : ~O( P^(-1/2) ).
Аналогично для двоичных палиндромов. Т.е. произведение вероятностей порядка ~O( P^-1 ).
Т.е. для любого разумного ограничения на длину палиндрома, получим не очень большое ограничение по порядковому номеру.
Затабулировать их и все.
Re: двоично-десятичные палиндромы
От: T4r4sB Россия  
Дата: 16.12.15 15:46
Оценка:
работает быстро, но только до миллиарда

  Скрытый текст
#include "stdio.h"

int Log2n(uint32_t n)
{
    int result = 0;
    if (n>=(1<<16)) { n>>=16; result+=16; }
    if (n>=(1<< 8)) { n>>= 8; result+= 8; }
    if (n>=(1<< 4)) { n>>= 4; result+= 4; }
    if (n>=(1<< 2)) { n>>= 2; result+= 2; }
    if (n>=(1<< 1)) { n>>= 1; result+= 1; }
    return result;
}

bool Pan2(uint32_t n)
{
    int ord = Log2n(n);
    for (int i=0,j=ord; i<j; ++i, --j)
        if (((n>>i)^(n>>j))&1)
            return false;
    return true;
}

void EnumPan10(int digits)
{
    int counters[16];
    counters[0]=1;
    for (int i=1; i<(digits+1)/2; ++i)
        counters[i]=0;

    int adds[16];
    for (int i=0; i<digits; ++i)
        adds[i]=0;
    int n=1;
    for (int i=0, j=digits-1; i<digits; ++i, --j)
    {
        adds[i<j?i:j] += n;
        n *= 10;
    }

    for (;;)
    {
        int cd=0;
        for (;;)
        {
            ++counters[cd];
            if (counters[cd]<=9)
                break;
            if (cd>=digits-cd-2)
                return;
            counters[cd]=0;
            ++cd;
        }
        if (counters[0]==0)
            continue;

        uint32_t res=0;
        for (int i=0; i<(digits+1)/2; ++i)
            res += counters[i]*adds[i];

        if (Pan2(res))
            printf("%i ", res);

    }
}

int main ()
{
    for (int i=1; i<=9; ++i)
        EnumPan10(i);

    
    getchar();
    return 0;
}


вывод
  Скрытый текст
3 5 7 9 33 99 313 717 585 9009 7447 32223 53235 15351 73737 53835 39993 585585 5841485 5071705 1934391 1758571 3129213 5259525 1979791 13500531 719848917 910373019 939474939
Отредактировано 16.12.2015 15:48 T4r4sB . Предыдущая версия .
Re[4]: двоично-десятичные палиндромы
От: Кодт Россия  
Дата: 16.12.15 16:56
Оценка: 10 (2)
Здравствуйте, _DAle_, Вы писали:

_DA>Ну, самый, имхо, простой способ — это работать просто с массивом разрядов, единственное, что в двоичное представление будет дольше перевести. А с помощью арифметических прогрессий — это как?


1-разрядные: [1 +1=2 +1=3 ... +1=9]
2-разрядные: 9 +2 [11 +11=22 +11=33 ... +11=99]
3-разрядные: 99 +2 [101 +10=111 +10=121 ... +10=191] +11 [202 +10=212 +10=222 ... +10=292] +11 [303 +10=313 ...]
4-разрядные: 999 +2 = [1001 +110=1111 +110=1221 ... +110=1991] +11 [2002 +110=2112 ... +110=2992] +11 [3003 +110=3113 ...]
5-разрядные: 9999 +2 [[10001 +100=10101 ... +100=10901] +110 [11011 +100=11111 +100=11211 ... 11911] +110 [12021...] ... [... 19991]] +11 [[20002 +100...] +11 ... [... 29992]]
и т.д.

Схема довольно мутная, не берусь её с разбегу формализовать, — но регулярная.
Только инкременты-декременты и нужны.
Перекуём баги на фичи!
Re[2]: двоично-десятичные палиндромы
От: Кодт Россия  
Дата: 16.12.15 16:59
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Затабулировать их и все.


Против таких хитрых есть задача: для любых двух произвольных оснований b и d найти b-и-d-ричные палиндромы
Тут уже не затабулируешь!

Кстати, для (2, 3) — таких палиндромов 64-битных всего штук десять, что ли. Говорят...
Перекуём баги на фичи!
Re[5]: двоично-десятичные палиндромы
От: Буравчик Россия  
Дата: 16.12.15 19:54
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Схема довольно мутная, не берусь её с разбегу формализовать, — но регулярная.


Все очень просто:

Рассмотрим разложение 4-значного палиндром в 10-тичной системе на цифры (d — цифры):
    -----------
N = d0 d1 d1 d0


Значение N = d0*1000 + d1*100 + d1*10 + d0*1 = d0*(1000+1) + d1*(100+10) = d0*1001 + d1*110

Теперь подставляя в выражение N = d0*1001 + d1*110 конкретные значения d0 и d1 получаем ВСЕ 4-значные палиндромы

Общая формула для палиндромов четной длины L: 
SUM {dk * (10k + 10L-1-k)}, где k = [0, (L-2)/2]

Для палиндромов НЕчетной длины L: 
d(L-1)/2*10(L-1)/2 + SUM {dk * (10k + 10L-1-k)}, где k = [0, (L-3)/2]


Формула верна и для двоичных палиндромов, только основание сменить с 10 на 2.

Далее можно даже составить уравнение в целых числах, которое описывает все искомые палиндромы.
Best regards, Буравчик
Re: двоично-десятичные палиндромы
От: Буравчик Россия  
Дата: 17.12.15 07:55
Оценка: 15 (2)
Здравствуйте, Кодт, Вы писали:

К>Предложите наиболее быстрый способ.


1. Формируем список десятичных палиндромов
2. Формируем список двоичных палиндромов
3. Мержим оба списка
4. Просматриваем объединенный список на повторы. Каждый повтор — искомый палиндром

Для простоты разделить п.1 (да и п.2):
а) сформировать палиндромы четной длины
б) сформировать палиндромы нечетной длины
в) смержить оба списка
Так получается проще

Главное, не проверять на палиндромность, а сразу формировать палиндромы.
Это возможно, применяя только сложение (обсуждалось выше)

На Питоне:
  Скрытый текст
1 : 1
2 : 3
3 : 5
4 : 7
5 : 9
6 : 33
7 : 99
8 : 313
9 : 585
10 : 717
11 : 7447
12 : 9009
13 : 15351
14 : 32223
15 : 39993
16 : 53235
17 : 53835
18 : 73737
19 : 585585
20 : 1758571
21 : 1934391
22 : 1979791
23 : 3129213
24 : 5071705
25 : 5259525
26 : 5841485
27 : 13500531
28 : 719848917
29 : 910373019
30 : 939474939
31 : 1290880921
32 : 7451111547
33 : 10050905001
34 : 18462126481
35 : 32479297423
36 : 75015151057
37 : 110948849011
38 : 136525525631

real 0m7.668s
user 0m7.364s
sys 0m0.028s
Далее сложнее (досчитал за минуту до 48-го члена)
Best regards, Буравчик
Отредактировано 17.12.2015 8:39 Буравчик . Предыдущая версия . Еще …
Отредактировано 17.12.2015 7:55 Буравчик . Предыдущая версия .
Re[2]: двоично-десятичные палиндромы
От: Буравчик Россия  
Дата: 17.12.15 21:01
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Здравствуйте, Кодт, Вы писали:


К>>Предложите наиболее быстрый способ.


Б>1. Формируем список десятичных палиндромов

Б>2. Формируем список двоичных палиндромов
Б>3. Мержим оба списка
Б>4. Просматриваем объединенный список на повторы. Каждый повтор — искомый палиндром

Б>Для простоты разделить п.1 (да и п.2):

Б>а) сформировать палиндромы четной длины
Б>б) сформировать палиндромы нечетной длины
Б>в) смержить оба списка
Б>Так получается проще

На Haskell получилось лучше, чем на Python
Скорость в три раза выше. И главное, ближе к описанию алгоритма

type Digits = [Integer]
type Radix = Integer


-- объединяет два отсортированных списка
merge :: Ord a => [a] -> [a] -> [a]
merge xs [] = xs
merge [] xs = xs
merge a@(h:first) b@(c:second)
        | h <= c = h : merge first b
        | h > c = c : merge a second


-- выбирает из списка элементы, которые совпадают с предыдущим элементом списка
filter_repeat xs = [a | (a,b) <- zip xs (drop 1 xs), a == b]


-- числа представляем как список цифр в системе radix
-- младшая цифра находится в начале списка


-- получение следующего числа (для формирования половинок палиндромов)
increment :: Radix -> Digits -> Digits
increment radix [] = [1]
increment radix (d:ds)
    | d < radix-1 = (d+1:) $ ds
    | otherwise   = (0:) $ increment radix ds 
    

-- на основе половинки палиндрома создаем палиндром четный длины
create_even_palindrome :: Digits -> Digits
create_even_palindrome digits = reverse digits ++ digits


-- на основе половинки палиндрома создаем палиндром нечетный длины
create_odd_palindrome :: Digits -> Digits
create_odd_palindrome digits = reverse (tail digits) ++ digits


-- палиндромы по основанию radix
palindromes :: Radix -> [Integer]
palindromes radix = 
    let    
        position_weights = iterate (radix*) 1
        to_number digits = sum $ zipWith (*) digits position_weights

        hs = iterate (increment radix) [1]
        even_palindromes = map to_number $ map create_even_palindrome hs
        odd_palindromes = map to_number $ map create_odd_palindrome hs
    in
        merge even_palindromes odd_palindromes


-- вывод чисел, одновременно являющимися палиндромами в двоичной и десятичной системах
main = 
    let    
        ps = filter_repeat $ merge (palindromes 2) (palindromes 10)
    in     
        mapM_ putStrLn [ show idx ++ ": " ++ show p | (idx,p) <- zip [1..] ps ]
Best regards, Буравчик
Re: двоично-десятичные палиндромы
От: Chorkov Россия  
Дата: 23.12.15 09:59
Оценка:
Здравствуйте, Кодт, Вы писали:

К>По мотивам пары недавних статей на хабре. (Ну и для тех, кто не читал эти статьи, тем более может быть интересно — размять голову).


К>Постановка задачи:


К>1. d-ричный палиндром — это число, чьё d-ричное представление без ведущих нулей является, внезапно, палиндромом. Например, 12321 и 123321 — палиндромы, 12321000 — не палиндром.

К>2. Двоично-десятичный палиндром — число, являющееся и двоичным, и десятичным палиндромом.
К>0. Считать 0 палиндромом или нет — дело вкуса.

К>Задача: найти n-ный двоично-десятичный палиндром.

>...
К>Предложите наиболее быстрый способ.

Алгоритм O( P^.5 * log(P) ), где P — значение палиндрома:

Возьмем два генератора палиндромов.
Каждый из генераторов умеет генерировать строго возрастающею последовательность палиндромом, по заданному основанию. Время генерации следующего палиндрома O( log(P) ).

Для этого в генераторе храним текущий палиндром в виде разложения на цифры из которых он состоит (верхнею половину).
Для составления числа домножаем цифы на заранее вычисленные множители и складываем.

Запаршивевшем следующий палиндром у каждого из генераторов.
Если значения не совпали, запрашиваем следующий палиндром у того генератора, который выдал меньшее число, и так, пока не совпадут.
(Аналог merge sort.)

Оптимизация:

(1)
Обращаю внимание, что мы крутим запросы следующего числа для одного генератора, пока число не превысит заданное (текущее значение другого генератора).
Это значение можно разложить обратно в цифровое представление и сразу найти следующий палиндром. Это дорого, потому что требует задействовать деление.
Я реализовал эту возможность, для генераторов, для которых возможно дешевое деление (т.е. если основание равно двум).
Это привело к замедлению работы примерно в 1.5 раза.

(2)
Если одно основание кратно другому основанию, (например, основания 2 и 10 ), то последняя(первая) цифра палиндрома по меньшему основанию равна
остатку от деления последней(первой) цифры палиндрома по большему основанию, на меньшее основание.
Но, первая/последняя цифра не может быть нулем. Таким образом, для оснований 2 и 10, первая цифра десятичного палиндрома — всегда нечетная!

Это отбрасывает половину десятичных палиндромов и ускоряет работу в 2 раза (в 1.5 раза, с учетом замедления в (1) ).

Поэтому, основание другого генератора и отбрасывают серии если они заведомо не проходят.
Проверка срабатывает после обновления n-й цифры (в цифровом представлении), и если первые n-цифр не могут быть началом палиндрома,
то делаю инкремент сразу n-й цифре, а не последней.

(3)
Если одно основание равно 2, а другое нечетное (например, основания 2 и 3 ), то палиндром в представлении по большему основанию не может содержать четное
число цифр, а "центральная" цифра всегда нечетная.

(4)
Если одно основание кратно другому основанию, (например, основания 2 и 10 , то для группы палиндромов начинающихся с одних и тех-же n цифр (в представлении
по большему основанию), содержат одни и те же последнии n фцифр (в представлении по основанию 2).
Поэтому когда обновилась n-я цифра: смотрим сколько первых цифр (в двоичном представлении) не будут меняться, для группы палиндромов, начинающихся с этих
n десятичных цифр. Для этого строим двоичное представление палиндрома, в котором n-я цифра на 1 больше, и далее битовые операции...
Если есть несколько сохраняющихся цифр в двоичном представлении, и они не спадают переставленными цифрами в хвосте, то пропускам серию палиндромов.

Оптимизация (4) самая важная, поскольку в сочетании с (1) позволяет понизить порядок сложности. (С высокой вероятностью пропускаются группы из первых 3-4 десятичных цифр.)

Результат:

bits   : 64
base 1 : 2
base 2 : 10
  1 :                    1   0.000003 
  2 :                    3   0.000093 
  3 :                    5   0.000106 
  4 :                    7   0.000117 
  5 :                    9   0.000126 
  6 :                   33   0.000135 
  7 :                   99   0.000148 
  8 :                  313   0.000164 
  9 :                  585   0.000178 
 10 :                  717   0.000186 
 11 :                 7447   0.000214 
 12 :                 9009   0.000225 
 13 :                15351   0.000251 
 14 :                32223   0.000278 
 15 :                39993   0.000305 
 16 :                53235   0.000320 
 17 :                53835   0.000328 
 18 :                73737   0.000359 
 19 :               585585   0.000507 
 20 :              1758571   0.000884 
 21 :              1934391   0.000962 
 22 :              1979791   0.000987 
 23 :              3129213   0.001033 
 24 :              5071705   0.001325 
 25 :              5259525   0.001389 
 26 :              5841485   0.001553 
 27 :             13500531   0.002219 
 28 :            719848917   0.013922 
 29 :            910373019   0.016190 
 30 :            939474939   0.016916 
 31 :           1290880921   0.019714 
 32 :           7451111547   0.033399 
 33 :          10050905001   0.039802 
 34 :          18462126481   0.049348 
 35 :          32479297423   0.053009 
 36 :          75015151057   0.070960 
 37 :         110948849011   0.083456 
 38 :         136525525631   0.086801 
 39 :        1234104014321   0.147498 
 40 :        1413899983141   0.158043 
 41 :        1474922294741   0.164553 
 42 :        1792704072971   0.185397 
 43 :        1794096904971   0.185774 
 44 :        1999925299991   0.199493 
 45 :        5652622262565   0.266082 
 46 :        7227526257227   0.290563 
 47 :        7284717174827   0.294871 
 48 :        9484874784849   0.339864 
 49 :       34141388314143   0.460011 
 50 :      552212535212255   1.442231 
 51 :      933138363831339   1.768494 
 52 :     1793770770773971   2.211756 
 53 :     3148955775598413   2.344692 
 54 :    10457587478575401   3.780816 
 55 :    10819671917691801   3.878976 
 56 :    18279440804497281   5.089340 
 57 :    34104482028440143   5.919683 
 58 :    37078796869787073   6.287511 
 59 :    37629927072992673   6.400443 
 60 :    55952637073625955   7.331036 
 61 :   161206152251602161  11.377276 
 62 :   313558153351855313  12.226421 
 63 :  7036267126217626307  41.622048 

Overload!
 Total CPU time: 49.163395 (s)

real    0m49.169s
user    0m49.196s
sys    0m0.008s

( последняя колонка — CPU timе )
По полученным данных трудно оценить порядок метода, но он не выше O( P^ 0.35 )


  C++ код в макоронном стиле. (Я предупредил!)
// http://rsdn.ru/forum/etude/6281286.1
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <numeric>
#include <memory>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/cstdint.hpp>
#include <boost/timer/timer.hpp>

//typedef unsigned long long used_uint;
//typedef std::uint_fast64_t used_uint;
typedef boost::uint_fast64_t used_uint;
//typedef boost::uint_fast32_t used_uint;
//typedef boost::uint_least16_t used_uint;
//typedef boost::uint_least8_t used_uint;

//const used_uint limit = std::numeric_limits<used_uint>::max();
//const used_uint limit = 1000000000000000UL;
const used_uint limit = ~((used_uint)0);

#define LIMIT_OF_BASE    32
#define LIMIT_OF_POWER   65
#define LIMIT_OF_SWAP_BITS 7
#define LIMIT_OF_SWAP_BITS_NUMBER 128

static_assert( LIMIT_OF_POWER  > std::numeric_limits<used_uint>::digits, "Need to update LIMIT_OF_POWER" );
static_assert( (1<<LIMIT_OF_SWAP_BITS)==LIMIT_OF_SWAP_BITS_NUMBER , "Need to update LIMIT_OF_SWAP_BITS_NUMBER" );

static const size_t bad_index = ~((size_t)0);

namespace compiletime
{
    template<unsigned base, unsigned p>
    struct pow
    {
        static const used_uint value = pow<base, p-1>::value * base;
    };
    template<unsigned base>
    struct pow<base, 0>
    {
        static const used_uint value = 1;
    };


    template<unsigned base, used_uint number = limit >
    struct digits_count;

    template<unsigned base>
    struct digits_count<base, 0>
    {
    public:
        enum { value = 0 };
    };

    template<unsigned base, used_uint number >
    struct digits_count
    {
        typedef digits_count<base, (number/base) > pref;
    public:
        enum { value = pref::value +1 };
    };

    // return low n-bites of number at reversed order
    template<unsigned bits_count, used_uint number >
    struct swap_bits;

    template<used_uint number >
    struct swap_bits<1, number>
    {
        enum { value= (number & 1) };
    };

    template<used_uint number >
    struct swap_bits<0, number>
    {
        enum { value= (number & 1) };
    };

    template<unsigned bits_count, used_uint number >
    struct swap_bits // <bits_count, number>
    {
        enum { value= ( swap_bits<bits_count-1, number/2 >::value )
                    + (number&1) * (1<<(bits_count-1))          };
    };
}

const used_uint table_pow[LIMIT_OF_BASE][LIMIT_OF_POWER] = // table_pow[ base ][ power ] == compiletime::pow< base, power >::value
{
    #define IMPL_p(z, POWER, BASE) (compiletime::pow< BASE, POWER >::value ),
    #define IMPL_b(z, BASE, unused ) { BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_POWER, IMPL_p, BASE ) },
    BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_BASE, IMPL_b, unused_text )
    #undef IMPL_p
    #undef IMPL_b
};

const used_uint swap_bits_table[LIMIT_OF_SWAP_BITS][LIMIT_OF_SWAP_BITS_NUMBER] =
{
    #define IMPL_p(z, NUMBER, BITS) (compiletime::swap_bits< BITS, NUMBER >::value ),
    #define IMPL_b(z, BITS, unused ) { BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_SWAP_BITS_NUMBER, IMPL_p, BITS ) },
    BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_SWAP_BITS, IMPL_b, unused_text )
    #undef IMPL_p
    #undef IMPL_b
};



used_uint uint_pow( used_uint base, unsigned power )
{
    if( base  < LIMIT_OF_BASE
     && power < LIMIT_OF_POWER )
        return table_pow[base][power];


    switch(power)
    {
    case 0 : return 1;
    case 1 : return base;
    case 2 : return base*base;
    case 3 : return base*base*base;
    case 4 : return uint_pow(uint_pow(base,2),2);
    default:
        if(power>16)
            return  uint_pow( uint_pow(base, 16), power/16 ) * uint_pow(base, power%16 );
        else
            return  uint_pow( uint_pow(base, 4), power/4 ) * uint_pow(base, power%4 );
    }
}

template<unsigned base>
size_t digits_count(used_uint number)
{
    const used_uint* begin = table_pow[base];
    const used_uint* end = begin + compiletime::digits_count<base>::value +1;
    const used_uint* it=std::upper_bound(begin, end, number);
    // *it > number
    return (it-begin)-1;
}


// Return position on higest updated digit.
// Return bad_index on 'out of digits number'
template<unsigned base>
size_t lexical_next(used_uint* begin, used_uint*end )
{
    for(;  begin != end ; )
    {
        used_uint& back = end[-1];
        ++back;
        if(back<base)
            return end-begin -1;

        back=0;
        --end;
    }
    return bad_index;
}


struct i_generator
{
    virtual i_generator* next() =0; // return pointer to generetor with value of next palindrome (next from value of current generator value.)
    virtual i_generator* next(used_uint v) =0; // return pointer to generator with palindromee great or equal given value.
    virtual void reset()=0;
    virtual std::string to_str() const =0;

    bool operator< ( const i_generator& r ) const
    {
        return (used_uint)(*this) < (used_uint)(r);
    }

    used_uint value;
    operator used_uint() const { return value; }
};

void on_overload()
{
    throw std::runtime_error("Overload!");
}

struct bad_generator : i_generator
{
    bad_generator()                     { value =0; }
    operator used_uint() const          { on_overload(); return 0; }
    i_generator* next()                 { on_overload(); return this; }
    i_generator* next(used_uint)        { on_overload(); return this; }
    void reset()                        {}
    virtual std::string to_str() const  { return "Overload!"; }
};


template< unsigned base     ,
          unsigned base2    , //< base of alternate generator. Used at skip condition.
          unsigned n_digits = 1,
          bool overload     = ( n_digits > compiletime::digits_count<base>::value ) >
struct fixed_generator;

template< unsigned b, unsigned b2, unsigned n        > struct fixed_generator<b, b2, n, true> : bad_generator {};
template<             unsigned b2, unsigned n, bool o> struct fixed_generator<0, b2, n, o   > : bad_generator {};
template<             unsigned b2, unsigned n, bool o> struct fixed_generator<1, b2, n, o   > : bad_generator {};

template< unsigned base, unsigned base2, unsigned n_digits >
struct fixed_generator<base, base2, n_digits, false> : i_generator
{
    static_assert( base  < LIMIT_OF_BASE , "Update LIMIT_OF_BASE!" );
    static_assert( base2 < LIMIT_OF_BASE , "Update LIMIT_OF_BASE!" );
private:
    enum { even = ( n_digits%2 == 0 ) };
    enum { half_size = (n_digits/2) + ( even ? 0 : 1 ) };

    used_uint digits_max [half_size];
    used_uint multiplers [half_size];
    used_uint digits     [half_size];

    fixed_generator< base, base2, n_digits+1 > next_instance;

    void fill_multiplers()
    {
        if(even)
        {
            for(size_t i=0; i<half_size; ++i )
                multiplers[i] = uint_pow(base,  n_digits-1 -i) + uint_pow(base,  i);
        }
        else
        {
            for(size_t i=0; i<half_size-1; ++i )
                multiplers[i] = uint_pow(base,  n_digits-1 -i) + uint_pow(base,  i);
            multiplers[half_size-1] = uint_pow(base, n_digits/2);
        }

        used_uint max = limit;
        for(size_t i=0; i<half_size; ++i )
        {
            used_uint m = uint_pow(base, n_digits-1 -i );
            //used_uint m = multiplers[i];
            digits_max[i] = max / m;
            max -= digits_max[i] *m;
        }
        if( ::lexical_next<base>(std::begin(digits_max),std::end(digits_max)) == bad_index )
            for(size_t i=0; i<half_size; ++i )
                digits_max[i] = base+1;
    }
    void calc_value()
    {
        value = std::inner_product(std::begin(digits), std::end(digits),
                                   std::begin(multiplers), (used_uint)0 );
    }

    bool skip_condition( size_t update_digit_index ) const
    {
        if( ( base % base2) ==0 )
        {
            if(update_digit_index==0)
                if( digits[0] % base2 == 0 )
                    return true;

        }
#if 1
        if( base2==2 && (( base % base2) ==0 ) )
        {
            enum { stored_bits_per_digit = ( base % (1<<4) ==0 ) ? 4
                                         : ( base % (1<<3) ==0 ) ? 3
                                         : ( base % (1<<2) ==0 ) ? 2
                                         : ( base % (1<<1) ==0 ) ? 1
                                         : ( base % (1<<0) ==0 ) ? 0
                                         : 0 };
            //assert( stored_bits_per_digit>=1 );

            enum { skip_digits_to_half_size = 4 };

            if( half_size > skip_digits_to_half_size
            &&  update_digit_index>=1
            &&  update_digit_index*stored_bits_per_digit< LIMIT_OF_SWAP_BITS
            &&  update_digit_index < half_size - skip_digits_to_half_size // optimization. No reason to skip short step series.
            )
            {
                size_t value_bits = ::digits_count<2>(value);
                used_uint updated = value ^ ( value + multiplers[update_digit_index] );
                size_t updated_bits = ::digits_count<2>(updated);
                if( updated_bits < value_bits-1 )
                {
                    size_t bits_to_test = value_bits - updated_bits;
                    bits_to_test  = std::min( bits_to_test , stored_bits_per_digit*(update_digit_index+1)  );
                    bits_to_test  = std::min( bits_to_test , (size_t) LIMIT_OF_SWAP_BITS  ); // tabulated
                    {
                        used_uint mask =  ( used_uint(1) << bits_to_test ) -1;
                        used_uint top_digits= (value >>(value_bits - bits_to_test+1) ) & mask;
                        used_uint low_digits= value & mask;
                        if(swap_bits_table[bits_to_test][low_digits] != top_digits )
                            return true;
                    }
                }
            }
        }
#endif
        return false;
    }

    //
    bool lexical_next_with_skip_bad_values()
    {
        size_t i=::lexical_next<base>( std::begin(digits), std::end(digits) );
        calc_value();
        if(i==bad_index)
            return false;

        while(skip_condition(i))
        {
            i=::lexical_next<base>( std::begin(digits), std::begin(digits)+i+1 );
            calc_value();
            if(i==bad_index)
                return false;
        }

        return true;
    }

    bool value_out_of_limit() const
    {
        if( n_digits >= compiletime::digits_count<base>::value ) // compile time condition
            if( ! std::lexicographical_compare(std::begin(digits), std::end(digits),
                                               std::begin(digits_max), std::end(digits_max)
                                               ) )
                return true;

        return false;
    }

public:

    void reset()
    {
        digits[0]=1;
        for(size_t i=1; i<half_size; ++i )
            digits[i]=0;
        calc_value();
        next_instance.reset();
    }

    fixed_generator()
    {
        digits[0]=1;
        for(size_t i=1; i<half_size; ++i )
            digits[i]=0;
        fill_multiplers();
        calc_value();
    }


    static std::string char_to_str(used_uint char_)
    {
        if(char_<10)
            return std::string(1, (char)'0'+char_ );
        if(char_ < 10 + ('Z'-'A') )
            return std::string(1, (char)'A'+char_-10 );
        return "(" + std::to_string(char_) +")";
    }
    std::string to_str() const
    {
        std::string ret;
        ret.reserve(n_digits*( base<40 ? 1 : 5));
        for(size_t i=0; i<half_size; ++i )
            ret += char_to_str(digits[i]);
        for(size_t i=( even ? half_size-1 : half_size-2 ) ; i!= std::numeric_limits<size_t>::max() ; --i )
            ret += char_to_str(digits[i]);
        return ret;
    }

    i_generator* next();
    i_generator* next(used_uint v);
};

template< unsigned base, unsigned base2, unsigned n_digits >
i_generator* fixed_generator<base, base2, n_digits, false>::next()
{
    if(!lexical_next_with_skip_bad_values())
        return &next_instance;

    //calc_value();

    if( value_out_of_limit() )
            return &next_instance;

    return this;
}

template< unsigned base, unsigned base2, unsigned n_digits >
i_generator* fixed_generator<base, base2, n_digits, false>::next(used_uint v)
{
    if(v==value)
        return this;

    if(base==2)
    {
        if( v >= (((used_uint)1)<<n_digits) )
            return next_instance.next(v);

        for(size_t i=0; i<half_size; ++i )
            digits[i]= (v >>(n_digits-i-1) ) & 1 ;
        calc_value();

        if( value_out_of_limit() )
                return next_instance.next(v);

        if(value>=v)
            return this;
    }

    if(!lexical_next_with_skip_bad_values())
        return next_instance.next(v);

    //calc_value();
    if( value_out_of_limit() )
            return next_instance.next(v);

    return this;
}

struct cross_generator : i_generator
{
    std::unique_ptr< i_generator > holder1;
    std::unique_ptr< i_generator > holder2;
    i_generator* g1;
    i_generator* g2;
    cross_generator(std::unique_ptr< i_generator >&& h1,
                    std::unique_ptr< i_generator >&& h2)
        : holder1( std::move(h1) ), holder2(std::move(h2))
    {
        reset();
    }
    void reset()
    {
        g1=holder1.get();
        g2=holder2.get();
        g1->reset();
        g2->reset();
        value=1;
    }

    i_generator* next()
    {
        g1=g1->next();
        g2=g2->next();
        while( *g1 != *g2  )
        {
            if( *g1 < *g2 )
                g1=g1->next(*g2);
            if( *g2 < *g1 )
                g2=g2->next(*g1);
        }
        value=g1->value;
        return this;
    }

    i_generator* next(used_uint v)
    {
        g1=g1->next(v);
        g2=g2->next(v);
        while( *g1 != *g2  )
        {
            if( *g1 < *g2 )
                g1=g1->next(*g2);
            if( *g2 < *g1 )
                g2=g2->next(*g1);
        }
        value=g1->value;
        return this;
    }

    std::string to_str() const
    {
        return g1->to_str() +" "+g2->to_str();
    }
};

std::unique_ptr< i_generator > make_bad_generator()
{
    return std::unique_ptr< i_generator >(new bad_generator());
}

std::unique_ptr< i_generator > make_fixed_generator( unsigned base1, unsigned base2 )
{
    switch(base1)
    {
#define IMPL_b2(z, BASE2, BASE1) case BASE2 : return std::unique_ptr< i_generator >(new fixed_generator<BASE1, BASE2>());
#define IMPL_b1(z, BASE1, unused ) \
        case BASE1: \
                switch(base2) { \
                    BOOST_PP_REPEAT_FROM_TO( 2, 17, IMPL_b2, BASE1 ); \
                    default: throw std::runtime_error("Base value out of limit;"); \
                };
    BOOST_PP_REPEAT_FROM_TO( 2, 17, IMPL_b1, unused_text )
#undef IMPL_b2
#undef IMPL_b1
        default: throw std::runtime_error("Base value out of limit;");
    };

}

std::unique_ptr< i_generator > make_cross_generator( unsigned base1, unsigned base2 )
{
    return std::unique_ptr< i_generator >( new cross_generator( make_fixed_generator(base1, base2),
                                                                make_fixed_generator(base2, base1)));
}

int main(int argc, char** argv)
{
    assert( table_pow[10][1] == 10 );

    boost::timer::cpu_timer timer;

    try
    {
        unsigned base1=2 ;
        unsigned base2=10;
        if( argc > 1 ) base1 = boost::lexical_cast<used_uint>(argv[1]);
        if( argc > 2 ) base2 = boost::lexical_cast<used_uint>(argv[2]);

        std::cout<<"bits   : " << std::numeric_limits<used_uint>::digits <<std::endl
                 <<"base 1 : " << base1 << std::endl
                 <<"base 2 : " << base2 << std::endl
                 ;

        std::unique_ptr< i_generator > h = make_cross_generator(base1, base2);

        timer.start();

        size_t counter=1;
        for(auto * g=h.get();; g=g->next() )
        {
            std::cout<< std::setw(3) << (counter++)  <<" : "
                     << std::setw(std::numeric_limits<used_uint>::digits10+1) <<  (used_uint) *g   << " "
                     // << g->to_str() <<" "
                     // << std::setprecision(2) << std::scientific << pow( (*g) *(1./limit), .5 ) <<" "
                     << std::setw(10) << timer.format(6, "%w") <<" "
                     << std::endl
                     ;
        }
    }
    catch(std::exception& err)
    {
        std::cout << std::endl << err.what() <<std::endl;
    }
    std::cout<<" Total CPU time: " << timer.format(6, "%w") << " (s)" << std::endl;

    return 0;
}
Re[2]: двоично-десятичные палиндромы
От: Буравчик Россия  
Дата: 23.12.15 18:26
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>(4)

C>Если одно основание кратно другому основанию, (например, основания 2 и 10 , то для группы палиндромов начинающихся с одних и тех-же n цифр (в представлении
C>по большему основанию), содержат одни и те же последнии n фцифр (в представлении по основанию 2).
C>Поэтому когда обновилась n-я цифра: смотрим сколько первых цифр (в двоичном представлении) не будут меняться, для группы палиндромов, начинающихся с этих
C>n десятичных цифр. Для этого строим двоичное представление палиндрома, в котором n-я цифра на 1 больше, и далее битовые операции...
C>Если есть несколько сохраняющихся цифр в двоичном представлении, и они не спадают переставленными цифрами в хвосте, то пропускам серию палиндромов.

Можно подробнее про эту оптимизацию? Сходу что-то не понял как работает
Best regards, Буравчик
Re[3]: двоично-десятичные палиндромы
От: Chorkov Россия  
Дата: 24.12.15 06:48
Оценка: 31 (3)
Здравствуйте, Буравчик, Вы писали:

Б>Здравствуйте, Chorkov, Вы писали:


C>>(4)

C>>Если одно основание кратно другому основанию, (например, основания 2 и 10 , то для группы палиндромов начинающихся с одних и тех-же n цифр (в представлении
C>>по большему основанию), содержат одни и те же последнии n фцифр (в представлении по основанию 2).
C>>Поэтому когда обновилась n-я цифра: смотрим сколько первых цифр (в двоичном представлении) не будут меняться, для группы палиндромов, начинающихся с этих
C>>n десятичных цифр. Для этого строим двоичное представление палиндрома, в котором n-я цифра на 1 больше, и далее битовые операции...
C>>Если есть несколько сохраняющихся цифр в двоичном представлении, и они не спадают переставленными цифрами в хвосте, то пропускам серию палиндромов.

Б>Можно подробнее про эту оптимизацию? Сходу что-то не понял как работает


Рассмотрим серию палиндромов начиная с 12340000004321 по 12349999994321 включительно.
Они все заканчиваются на 4321, поэтому двоичные палиндромы этой серии будут заканчиваться на 4321%(2^4) = 0001(bin).
(Более старшие разряды не будут оказывать влияния на младшие разряды двоичного представления, поскольку (10^4)%(2^4)==0. )

С другой стороны
12340000004321 = 10110011100100100001010100010001100011100001 (bin)
12349999994321 = 10110011101101110101010111001101010111010001 (bin)
Т.е. все палиндромы серии начинаются с 1011001110.....
(Сколько именно цифр двоичного представления будет совпадать заранее сказать нельзя. Проще посчитать двоичное представление начала и конца серии.)
Но двоичные палиндромы не могут начинаться на 1011... и заканчиваться на ...0001.
Значит в серии с 12340000004321 по 12349999994321 нет двоично-десятичных палиндромов.

Можно сразу переходить к 12350000005321.

Рассмотрим серию с 12350000005321 по 12369999996321 ...
Re[2]: двоично-десятичные палиндромы
От: Chorkov Россия  
Дата: 24.12.15 12:25
Оценка:
Здравствуйте, В дополнение к предыдущему письму:

1) исправил условие проверки возможности пропустить серию.
2) исправил функцию переворачивающею последовательность бит (в двоичном представлении).
Раньше проверялось только 7 бит, потому что длинна таблицы была ограничена.
Новое время перебора всех 64-битных палиндромов 11.371s.
Наблюдаемый порядок O( P ^ 0.32 ).

  Исправленный код
// http://rsdn.ru/forum/etude/6281286.1
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <numeric>
#include <memory>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/cstdint.hpp>
#include <boost/timer/timer.hpp>
#include <boost/multiprecision/cpp_int.hpp>

//typedef unsigned long long used_uint;
//typedef std::uint_fast64_t used_uint;
typedef boost::uint_fast64_t used_uint;
//typedef boost::uint_fast32_t used_uint;
//typedef boost::uint_least16_t used_uint;
//typedef boost::uint_least8_t used_uint;
//typedef boost::multiprecision::uint128_t used_uint;

//const used_uint limit = std::numeric_limits<used_uint>::max();
//const used_uint limit = 1000000000000000UL;
const used_uint limit = ~((used_uint)0);

#define LIMIT_OF_BASE    32
#define LIMIT_OF_POWER   65
#define LIMIT_OF_SWAP_BITS 7
#define LIMIT_OF_SWAP_BITS_NUMBER 128

static_assert( LIMIT_OF_POWER  > std::numeric_limits<used_uint>::digits, "Need to update LIMIT_OF_POWER" );
static_assert( (1<<LIMIT_OF_SWAP_BITS)==LIMIT_OF_SWAP_BITS_NUMBER , "Need to update LIMIT_OF_SWAP_BITS_NUMBER" );

static const size_t bad_index = ~((size_t)0);

namespace compiletime
{
    template<unsigned base, unsigned p>
    struct pow
    {
        static const used_uint value = pow<base, p-1>::value * base;
    };
    template<unsigned base>
    struct pow<base, 0>
    {
        static const used_uint value = 1;
    };


    template<unsigned base, used_uint number = limit >
    struct digits_count;

    template<unsigned base>
    struct digits_count<base, 0>
    {
    public:
        enum { value = 0 };
    };

    template<unsigned base, used_uint number >
    struct digits_count
    {
        typedef digits_count<base, (number/base) > pref;
    public:
        enum { value = pref::value +1 };
    };

    // return low n-bites of number at reversed order
    template<unsigned bits_count, used_uint number >
    struct swap_bits;

    template<used_uint number >
    struct swap_bits<1, number>
    {
        enum { value= (number & 1) };
    };

    template<used_uint number >
    struct swap_bits<0, number>
    {
        enum { value= 0 };
    };

    template< unsigned bits_count, used_uint number >
    struct swap_bits // <bits_count, number>
    {
        enum { value= ( swap_bits<bits_count-1, number/2 >::value )
                    + (number&1) * (1<<(bits_count-1))          };
    };
}

const used_uint table_pow[LIMIT_OF_BASE][LIMIT_OF_POWER] = // table_pow[ base ][ power ] == compiletime::pow< base, power >::value
{
    #define IMPL_p(z, POWER, BASE) (compiletime::pow< BASE, POWER >::value ),
    #define IMPL_b(z, BASE, unused ) { BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_POWER, IMPL_p, BASE ) },
    BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_BASE, IMPL_b, unused_text )
    #undef IMPL_p
    #undef IMPL_b
};

const used_uint table_swap_bits[LIMIT_OF_SWAP_BITS][LIMIT_OF_SWAP_BITS_NUMBER] =
{
    #define IMPL_p(z, NUMBER, BITS) (compiletime::swap_bits< BITS, NUMBER >::value ),
    #define IMPL_b(z, BITS, unused ) { BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_SWAP_BITS_NUMBER, IMPL_p, BITS ) },
    BOOST_PP_REPEAT_FROM_TO( 0, LIMIT_OF_SWAP_BITS, IMPL_b, unused_text )
    #undef IMPL_p
    #undef IMPL_b
};


used_uint uint_pow( used_uint base, unsigned power )
{
    if( base  < LIMIT_OF_BASE
     && power < LIMIT_OF_POWER )
        return table_pow[base][power];


    switch(power)
    {
    case 0 : return 1;
    case 1 : return base;
    case 2 : return base*base;
    case 3 : return base*base*base;
    case 4 : return uint_pow(uint_pow(base,2),2);
    default:
        if(power>16)
            return  uint_pow( uint_pow(base, 16), power/16 ) * uint_pow(base, power%16 );
        else
            return  uint_pow( uint_pow(base, 4), power/4 ) * uint_pow(base, power%4 );
    }
}

template<unsigned base>
size_t digits_count(used_uint number)
{
    static_assert( base< LIMIT_OF_BASE, "Bad value of base" );
    const used_uint* begin = table_pow[base];
    const used_uint* end = begin + compiletime::digits_count<base>::value +1;
    const used_uint* it=std::upper_bound(begin, end, number);
    // *it > number
    return (it-begin)-1;
}

// return number with same last bints, but at revers order of bits.
used_uint swap_bits( unsigned bits_count, used_uint number )
{
    // assert( number < (1<<bits_count) );
    if( bits_count < LIMIT_OF_SWAP_BITS )
        return table_swap_bits[bits_count][number];

    #define IMPL(SHIFT) \
        if( bits_count>SHIFT ) \
            return swap_bits( bits_count-SHIFT, number>>SHIFT ) \
               | ( swap_bits( SHIFT, number & ( ((used_uint)1<<SHIFT) -1) ) << (bits_count-SHIFT) )
    IMPL(32);
    IMPL(16);
    IMPL(8);
    IMPL(4);
    IMPL(2);
    IMPL(1);
    // assert (bits_count==1)
    return number;
}



// Return position on higest updated digit.
// Return bad_index on 'out of digits number'
template<unsigned base>
size_t lexical_next(used_uint* begin, used_uint*end )
{
    for(;  begin != end ; )
    {
        used_uint& back = end[-1];
        ++back;
        if(back<base)
            return end-begin -1;

        back=0;
        --end;
    }
    return bad_index;
}


struct i_generator
{
    virtual i_generator* next() =0; // return pointer to generetor with value of next palindrome (next from value of current generator value.)
    virtual i_generator* next(used_uint v) =0; // return pointer to generator with palindromee great or equal given value.
    virtual void reset()=0;
    virtual std::string to_str() const =0;

    bool operator< ( const i_generator& r ) const
    {
        return (used_uint)(*this) < (used_uint)(r);
    }

    used_uint value;
    operator used_uint() const { return value; }
};

void on_overload()
{
    throw std::runtime_error("Overload!");
}

struct bad_generator : i_generator
{
    bad_generator()                     { value =0; }
    operator used_uint() const          { on_overload(); return 0; }
    i_generator* next()                 { on_overload(); return this; }
    i_generator* next(used_uint)        { on_overload(); return this; }
    void reset()                        {}
    virtual std::string to_str() const  { return "Overload!"; }
};


template< unsigned base     ,
          unsigned base2    , //< base of alternate generator. Used at skip condition.
          unsigned n_digits = 1,
          bool overload     = ( n_digits > compiletime::digits_count<base>::value ) >
struct fixed_generator;

template< unsigned b, unsigned b2, unsigned n        > struct fixed_generator<b, b2, n, true> : bad_generator {};
template<             unsigned b2, unsigned n, bool o> struct fixed_generator<0, b2, n, o   > : bad_generator {};
template<             unsigned b2, unsigned n, bool o> struct fixed_generator<1, b2, n, o   > : bad_generator {};

template< unsigned base, unsigned base2, unsigned n_digits >
struct fixed_generator<base, base2, n_digits, false> : i_generator
{
    static_assert( base  < LIMIT_OF_BASE , "Update LIMIT_OF_BASE!" );
    static_assert( base2 < LIMIT_OF_BASE , "Update LIMIT_OF_BASE!" );
private:
    enum { even = ( n_digits%2 == 0 ) };
    enum { half_size = (n_digits/2) + ( even ? 0 : 1 ) };

    used_uint digits_max [half_size];
    used_uint multiplers [half_size];
    used_uint digits     [half_size];

    fixed_generator< base, base2, n_digits+1 > next_instance;

    void fill_multiplers()
    {
        if(even)
        {
            for(size_t i=0; i<half_size; ++i )
                multiplers[i] = uint_pow(base,  n_digits-1 -i) + uint_pow(base,  i);
        }
        else
        {
            for(size_t i=0; i<half_size-1; ++i )
                multiplers[i] = uint_pow(base,  n_digits-1 -i) + uint_pow(base,  i);
            multiplers[half_size-1] = uint_pow(base, n_digits/2);
        }

        used_uint max = limit;
        for(size_t i=0; i<half_size; ++i )
        {
            used_uint m = uint_pow(base, n_digits-1 -i );
            //used_uint m = multiplers[i];
            digits_max[i] = max / m;
            max -= digits_max[i] *m;
        }
        if( ::lexical_next<base>(std::begin(digits_max),std::end(digits_max)) == bad_index )
            for(size_t i=0; i<half_size; ++i )
                digits_max[i] = base+1;
    }
    void calc_value()
    {
        value = std::inner_product(std::begin(digits), std::end(digits),
                                   std::begin(multiplers), (used_uint)0 );
    }

    bool skip_condition( size_t update_digit_index ) const
    {
        // for( size_t i=update_digit_index+1; i<half_size; ++i )
        //    assert( digits[i] ==0 );

        if( ( base % base2) ==0 )
        {
            if(update_digit_index==0)
                if( digits[0] % base2 == 0 )
                    return true;

        }
#if 1
        if( base2==2 && (( base % base2) ==0 ) )
        {
            enum { stored_bits_per_digit = ( base % (1<<4) ==0 ) ? 4
                                         : ( base % (1<<3) ==0 ) ? 3
                                         : ( base % (1<<2) ==0 ) ? 2
                                         : ( base % (1<<1) ==0 ) ? 1
                                         : ( base % (1<<0) ==0 ) ? 0
                                         : 0 };
            //assert( stored_bits_per_digit>=1 );

            enum { size_of_series_to_skip_test = 10 }; // optimization. No reason to skip short step series. 'step by step' is faster then test.
            enum { skip_digits_to_half_size = ::compiletime::digits_count<base,size_of_series_to_skip_test>::value  };

            if( half_size > skip_digits_to_half_size
            &&  update_digit_index>=1
            //&&  update_digit_index*stored_bits_per_digit< LIMIT_OF_SWAP_BITS
            &&  update_digit_index < half_size - skip_digits_to_half_size
            &&  value <= (~((used_uint)0)) - multiplers[update_digit_index] // protection from oveload  at 'value + multiplers[update_digit_index]'
            )
            {
                size_t value_bits = ::digits_count<2>(value);
                used_uint updated = value ^ ( value + multiplers[update_digit_index] );
                size_t updated_bits = ::digits_count<2>(updated);
                if( updated_bits < value_bits-1 )
                {
                    size_t bits_to_test = value_bits - updated_bits;
                    bits_to_test  = std::min( bits_to_test , stored_bits_per_digit*(update_digit_index+1)  );
                    //bits_to_test  = std::min( bits_to_test , (size_t) LIMIT_OF_SWAP_BITS  ); // tabulated
                    {
                        used_uint mask =  ( used_uint(1) << bits_to_test ) -1;
                        used_uint top_digits= (value >>(value_bits - bits_to_test+1) ) & mask;
                        used_uint low_digits= value & mask;
                        used_uint reversed_low_bits=swap_bits(bits_to_test,low_digits);

                        //if(table_swap_bits[bits_to_test][low_digits] != top_digits )
                        if( reversed_low_bits != top_digits )
                        {
                            return true;
                        }
                    }
                }
            }
        }
#endif
        if( base%2!=0 && base2==2 && n_digits%2!=0 )
            return true;

        return false;
    }

    // return value:
    //      true  - navigated to correct next palindrome
    //      fales - need to increment numbre of digits.
    bool lexical_next_with_skip_bad_values()
    {
        size_t i=::lexical_next<base>( std::begin(digits), std::end(digits) );
        calc_value();
        if(i==bad_index)
            return false;

        for(;;)
        {
            while( i<half_size && !skip_condition(i) )
                ++i;

            if(i>=half_size)
                return true;

            // assert( skip_condition(i) );

            i=::lexical_next<base>( std::begin(digits), std::begin(digits)+i+1 );
            calc_value();
            if(i==bad_index)
                return false;
        }
    }

    bool value_out_of_limit() const
    {
        if( n_digits >= compiletime::digits_count<base>::value ) // compile time condition
            if( ! std::lexicographical_compare(std::begin(digits), std::end(digits),
                                               std::begin(digits_max), std::end(digits_max)
                                               ) )
                return true;

        return false;
    }

public:

    void reset()
    {
        digits[0]=1;
        for(size_t i=1; i<half_size; ++i )
            digits[i]=0;
        calc_value();
        next_instance.reset();
    }

    fixed_generator()
    {
        digits[0]=1;
        for(size_t i=1; i<half_size; ++i )
            digits[i]=0;
        fill_multiplers();
        calc_value();
    }


    static std::string char_to_str(used_uint char_)
    {
        if(char_<10)
            return std::string(1, (char)'0'+char_ );
        if(char_ < 10 + ('Z'-'A') )
            return std::string(1, (char)'A'+char_-10 );
        return "(" + std::to_string(char_) +")";
    }
    std::string to_str() const
    {
        std::string ret;
        ret.reserve(n_digits*( base<40 ? 1 : 5));
        for(size_t i=0; i<half_size; ++i )
            ret += char_to_str(digits[i]);
        for(size_t i=( even ? half_size-1 : half_size-2 ) ; i!= std::numeric_limits<size_t>::max() ; --i )
            ret += char_to_str(digits[i]);
        return ret;
    }

    i_generator* next();
    i_generator* next(used_uint v);
};

template< unsigned base, unsigned base2, unsigned n_digits >
i_generator* fixed_generator<base, base2, n_digits, false>::next()
{
    if(!lexical_next_with_skip_bad_values())
        return &next_instance;

    //calc_value();

    if( value_out_of_limit() )
            return &next_instance;

    return this;
}

template< unsigned base, unsigned base2, unsigned n_digits >
i_generator* fixed_generator<base, base2, n_digits, false>::next(used_uint v)
{
    if(v==value)
        return this;

    if(base==2)
    {
        if( v >= (((used_uint)1)<<n_digits) )
            return next_instance.next(v);

        for(size_t i=0; i<half_size; ++i )
            digits[i]= (v >>(n_digits-i-1) ) & 1 ;
        calc_value();

        if( value_out_of_limit() )
                return next_instance.next(v);

        if(value>=v)
            return this;
    }

    if(!lexical_next_with_skip_bad_values())
        return next_instance.next(v);

    //calc_value();
    if( value_out_of_limit() )
            return next_instance.next(v);

    return this;
}

struct cross_generator : i_generator
{
    std::unique_ptr< i_generator > holder1;
    std::unique_ptr< i_generator > holder2;
    i_generator* g1;
    i_generator* g2;
    cross_generator(std::unique_ptr< i_generator >&& h1,
                    std::unique_ptr< i_generator >&& h2)
        : holder1( std::move(h1) ), holder2(std::move(h2))
    {
        reset();
    }
    void reset()
    {
        g1=holder1.get();
        g2=holder2.get();
        g1->reset();
        g2->reset();
        value=1;
    }

    i_generator* next()
    {
        g1=g1->next();
        g2=g2->next();
        while( *g1 != *g2  )
        {
            if( *g1 < *g2 )
                g1=g1->next(*g2);
            if( *g2 < *g1 )
                g2=g2->next(*g1);
        }
        value=g1->value;
        return this;
    }

    i_generator* next(used_uint v)
    {
        g1=g1->next(v);
        g2=g2->next(v);
        while( *g1 != *g2  )
        {
            if( *g1 < *g2 )
                g1=g1->next(*g2);
            if( *g2 < *g1 )
                g2=g2->next(*g1);
        }
        value=g1->value;
        return this;
    }

    std::string to_str() const
    {
        return g1->to_str() +" "+g2->to_str();
    }
};

// Comment next line on compilation error 'fatal error C1128: number of sections exceeded object file format limit : compile with /bigobj'
#define SELECTABLE 1
#ifdef SELECTABLE
std::unique_ptr< i_generator > make_bad_generator()
{
    return std::unique_ptr< i_generator >(new bad_generator());
}

std::unique_ptr< i_generator > make_fixed_generator( unsigned base1, unsigned base2 )
{
    switch(base1)
    {
#define IMPL_b2(z, BASE2, BASE1) case BASE2 : return std::unique_ptr< i_generator >(new fixed_generator<BASE1, BASE2>());
#define IMPL_b1(z, BASE1, unused ) \
        case BASE1: \
                switch(base2) { \
                    BOOST_PP_REPEAT_FROM_TO( 2, 17, IMPL_b2, BASE1 ); \
                    default: throw std::runtime_error("Base value out of limit;"); \
                };
    BOOST_PP_REPEAT_FROM_TO( 2, 17, IMPL_b1, unused_text )
#undef IMPL_b2
#undef IMPL_b1
        default: throw std::runtime_error("Base value out of limit;");
    };

}

std::unique_ptr< i_generator > make_cross_generator( unsigned base1, unsigned base2 )
{
    return std::unique_ptr< i_generator >( new cross_generator( make_fixed_generator(base1, base2),
                                                                make_fixed_generator(base2, base1)));
}
#else

template<unsigned base1, unsigned base2 >
std::unique_ptr< i_generator > make_fixed_generator()
{
    return std::unique_ptr< i_generator >(new fixed_generator<base1, base2>());
}


template<unsigned base1, unsigned base2 >
std::unique_ptr< i_generator > make_cross_generator()
{
    return std::unique_ptr< i_generator >( new cross_generator( make_fixed_generator<base1, base2>(),
                                                                make_fixed_generator<base2, base1>()));
}

#endif

#if 1
int main(int argc, char** argv)
{
    assert( table_pow[10][1] == 10 );

    boost::timer::cpu_timer timer;

    try
    {
        #ifdef SELECTABLE
        unsigned base1=2 ;
        unsigned base2=10;
        if( argc > 1 ) base1 = boost::lexical_cast<used_uint>(argv[1]);
        if( argc > 2 ) base2 = boost::lexical_cast<used_uint>(argv[2]);

        std::unique_ptr< i_generator > h = make_cross_generator(base1, base2);
        #else
        enum { base1=2, base2=10 };
        std::unique_ptr< i_generator > h = make_cross_generator<base1, base2>();
        #endif

        std::cout<<"bits   : " << std::numeric_limits<used_uint>::digits <<std::endl
                 <<"base 1 : " << base1 << std::endl
                 <<"base 2 : " << base2 << std::endl
                 ;

        timer.start();

        size_t counter=1;
        for(auto * g=h.get();; g=g->next() )
        {
            std::cout<< std::setw(3) << (counter++)  <<" : "
                     << std::setw(std::numeric_limits<used_uint>::digits10+1) <<  (used_uint) *g   << " "
                     // << g->to_str() <<" "
                     // << std::setprecision(2) << std::scientific << pow( (*g) *(1./limit), .5 ) <<" "
                     << std::setw(10) << timer.format(6, "%w") <<" "
                     << std::endl
                     ;
        }
    }
    catch(std::exception& err)
    {
        std::cout << std::endl << err.what() <<std::endl;
    }
    std::cout<<" Total CPU time: " << timer.format(6, "%w") << " (s)" << std::endl;

    return 0;
}
#endif
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.