|
Последний раз редактировалось wrest 29.10.2025, 20:26, всего редактировалось 2 раз(а).
Дописал, при помощи ИИ, алгоритм который считает за O(log(n)). (Оффтоп)
- comb_fast(n)={
- /* функция возвращает вектор кратностей цифр n-го замечательного числа */
- /* входной контроль */
- if(n<1,error("n must be positive, got n=",n));
- /* нерегулярные случаи */
- if(n<10,return([[n,1]]));
- if(n==10,return([[1,1],[0,1]])); /* тут по неубыванию не выходит */
-
- /* начало основного вычисления */
- my(len=floor((sqrt(8*sqrt(n-2)+1)-1)/2)); /* вычисляем длину числа -- количество цифр в нём */
- my(npos=n-len^2*(len+1)^2/4-1); /* вычисляем относительную позицию числа где единица -- начало ряда чисел нашей длины */
-
- /* Определяем фиксированные цифры */
- my(fixed_digits = vector(7));
- fixed_digits[1] = [[2, 1]]; /* тип 1 */
- fixed_digits[2] = [[2, 1], [6, 1]]; /* тип 2 */
- fixed_digits[3] = [[3, 1]]; /* тип 3 */
- fixed_digits[4] = [[4, 1]]; /* тип 4 */
- fixed_digits[5] = []; /* тип 5 */
- fixed_digits[6] = [[6, 1]]; /* тип 6 */
-
- /* Вычисляем размеры диапазонов для каждого из 6 типов*/
- my(type_len=vector(6,i,len-#fixed_digits[i])); /* длина сочетания равна длине числа минус количество фиксированных цифр */
- my(type_range=vector(6,i,(type_len[i]+1)*(type_len[i]+2)*(type_len[i]+3)/6)); /* тетраэдральные числа */
- my(type_finish=vector(6,i,sum(j=1,i,type_range[j]))); /* концы диапазонов шаблонов */
-
- /* Определяем тип числа */
- my(type_num, type_pos);
- if(npos <= type_finish[2],
- type_num = 2; type_pos = npos;
- , npos <= type_finish[3],
- type_num = 3; type_pos = npos - type_finish[2];
- , npos <= type_finish[4],
- type_num = 4; type_pos = npos - type_finish[3];
- , npos <= type_finish[6],
- type_num = 6; type_pos = npos - type_finish[4];
- ,
- error("npos too big: ", npos)
- );
-
- /* Обрабатываем составные типы 12 и 56, которые идут вперемешку */
-
- if(type_num == 2 || type_num == 6,
- my(m = floor((3*type_pos)^(1/3))); /* примерно куб, иначе пришлось бы по формуле Кардано */
- if(m*(m+1)*(m+2)/3 >= type_pos, m--); /* если не попали, корректируемся */
-
- my(Sm = m*(m+1)*(m+2)/6);
- my(offset = type_pos - 2*Sm);
- my(L = (m+1)*(m+2)/2);
-
- my(subtype = if(offset <= L, 1, 0));
- type_pos = Sm + if(subtype == 1, offset, offset - L);
-
- /* Преобразуем в окончательный тип 1 2 или 5 6*/
- type_num=type_num-subtype;
- );
-
- /* начинаем вычислять кратности цифр 5789 */
- my(elts=[5,7,8,9]); /* вектор элементов для генерации их сочетания */
- my(v_mult=vector(#elts,i,vector(2))); /* заготавливаем вектор кратностей цифр 5789 */
-
- /*
- Вычисляем вектор кратностей для сочетания с повторениями
- номер type_pos, из #elts элементов по type_len[type_num], в лексикографическом порядке
- по неубывающим последовательностям (т.е. сначала все минимальные элементы).
- */
- my(n_elts=#elts);
- my(m = n_elts + type_len[type_num] - 1, t = n_elts - 1, total = binomial(m, t));
-
-
- /* приводим type_pos к 0-based */
- type_pos = type_pos - 1;
-
- /* инвертируем позицию, чтобы порядок стал лексикографическим по неубыванию --- */
- type_pos = total - 1 - type_pos;
-
- /* выбираем позиции перегородок (1..m) */
- my(seps = vector(t), prev = 0, bincount=0);
- for (j = 1, t,
- my(low = prev + 1, high = m - (t - j), Rtj = t - j);
-
- /* бинарный поиск: ищем минимальный s в [low..high], такой что
- prefix(low..s) > r, где
- prefix(low..mid) = C(m-low+1, Rtj+1) - C(m-mid, Rtj+1) */
- my(A = m - low + 1, pref_high, pref_mid);
- while (low < high,
- my(mid = (low + high) \ 2);
- pref_mid = binomial(A, Rtj+1) - binomial(m - mid, Rtj+1); bincount+=2;
- if (pref_mid > type_pos,
- high = mid; /* возможно ответ слева включительно */
- ,
- low = mid + 1; /* префикс до mid <= r, идём правее */
- );
- );
- seps[j] = low;
-
- /* вычитаем из r все варианты, которые мы прошли (skip = prefix(low-1)) */
- if (low > prev + 1,
- my(skip = ( binomial(m - (prev + 1) + 1, Rtj + 1) - binomial(m - (low - 1), Rtj + 1) ));bincount+=2;
- type_pos = type_pos-skip;
- ); /* иначе skip = 0 */
-
- prev = low;
- );
-
- /* переводим позиции перегородок в кратности (число звёзд между ними) */
-
- for(i=1,n_elts,v_mult[i][1]=elts[i]);
- v_mult[1][2] = seps[1] - 1;
- for (j = 2, t, v_mult[j][2] = seps[j] - seps[j-1] - 1);
- v_mult[n_elts][2] = m - seps[t];
- /* добавляем фиксированные цифры и сортируем результат по неубыванию */
- v_mult = vecsort(concat(v_mult, fixed_digits[type_num])); /* в принципе, готовои ещё чуть: */
-
- /* удаляем нулевые кратности */
- my(res=[]);
- for(i=1,#v_mult,if(v_mult[i][2]>0,res=concat(res,[v_mult[i]])));
-
- return(res);
- }
Запуск: Код: ? comb_fast(20^25) time = 4 ms. %191 = [[3, 1], [5, 1420700], [7, 16335714], [8, 136434330], [9, 37213898]] ? comb_fast(10^1025); time = 17 ms. ? В общем решил повысить ставки до  17 миллисекунд вычисляется замечательное число номер  , если подавить печать (напечатанное не помещается на поля этого форума). В общем - всё. Алгоритм асимптотически идеальный. Улучшить нельзя теоретически.
|