Ідеальні номерні знаки


33

Ідеальні номерні знаки

Починаючи кілька років тому, я зробив собі невеличку гру під час руху: перевірити, чи знаходяться поблизу номерні знаки "ідеальними". Це порівняно рідко, але захоплююче, коли його знайдеш.

Щоб перевірити, чи відповідає номерний знак:

  1. Підсумуйте символи, причому A = 1, B = 2, ... Z = 26.
  2. Візьміть кожен наступний відрізок числівників і підсумовуйте їх; помножте кожну з цих сум разом.

Якщо значення в частині 1 та частині 2 рівні, вітаємо! Ви знайшли ідеальний номерний знак!

Приклади

License plate: AB3C4F

Digits -> 3 * 4 
        = 12
Chars  -> A + B + C + F 
        = 1 + 2 + 3 + 6 
        = 12
12 == 12 -> perfect!


License plate: G34Z7T

Digits -> (3 + 4) * 7 
        = 49
Chars  -> G + Z + T 
        = 7 + 26 + 20 
        = 53
49 != 53 -> not perfect!


License plate: 10G61

Digits -> (1 + 0) * (6 + 1)
        = 7
Chars  -> G
        = 7
7 == 7 -> perfect!

Змагання

Я використовував номерні знаки довжиною 5 і 6 в якості прикладів, але ця процедура справедлива для будь-якої довжини таблички. Ваше завдання полягає в тому, щоб для заданої довжини N повернути кількість досконалих номерних знаків такої довжини. Для цілей виклику дійсним номерним знаком є ​​будь-яке поєднання цифр 0-9 та символів AZ. Пластина повинна містити як символ, так і цифру, щоб вважатись потенційно досконалою. Для перевірки, ось значення, які я отримав (хоча я не можу бути на 100% щодо їх коректності, ха-ха)

N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012 

Примітки

Якщо це якимось чином спрощує проблему на вашій мові, ви можете вивести частку досконалих номерних знаків для даного N, принаймні, до двох значущих цифр.

N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...

АБО ви можете вивести еквівалентне значення mod 256

N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76

Найкоротші виграші!


2
Ласкаво просимо на сайт! Я думаю, що це хороший виклик, але додаткові дозволені результати ускладнюють фактичну оцінку відповідей. PPCG шукає об'єктивні критерії виграшу, і це важко зробити, коли існує стільки свобод; це не тільки змінює формат виводу, це фактично змінює те, що вам дозволяється виводити. Я рекомендую відредагувати інші параметри і просто зробити так, щоб було потрібно вивести кількість ідеальних номерних знаків для N.
HyperNeutrino

11
Мені особисто сподобалося б це завдання ще більше, якби він просто перевіряв, чи є даний номерний знак ідеальним чи ні (тим більше, що у вас немає остаточних номерів для тестових випадків. Це, мабуть, нормально, доки потенційно підраховані результати Не знаю, як почуваються інші люди. Хоча
приємна

4
Я згоден з Містом Фіггінсом; Я відчуваю, що таким чином, це більше стосується пошуку викрійки, яка все ще є цікавою проблемою, але я думаю, що це могло б отримати більше відповідей, якби це була лише перевірка перевірки. Хороший виклик, хоча!
HyperNeutrino

1
Я розмістив тісно пов'язаний виклик , сподіваючись, що він приверне увагу до цього чудового питання, а також трохи спростить це, лише перевіривши (майже) ідеальний номерний знак.
Містер Xcoder

1
@carusocomputing Я постарався, але прийшов порожній. Я надіслав це своєму вчителю математики, і він поки що порожній
Крістофер

Відповіді:


9

Python 3,6, 150 байт

f=lambda n,t=-1,p=-1,a=0:sum(f(n-1,*((t,c+p*(p>=0),a),((t<0 or t)*(p<0 or p),-1,a-c))[c<0])for c in range(-26,10))if n else 0<(t<0 or t)*(p<0 or p)==a

результати:

f(2) = 18
f(3) = 355
f(4) = 8012
f(5) = 218153

Безгольова версія з поясненням:

digits=[*range(10)]
letters=[*range(1,27)]

def f(n, dt=-1, dp=-1, lt=0):
    if n:
        for d in digits:
            yield from f(n - 1,
                         dt,
                         d if dp < 0 else dp + d,
                         lt
                         )

        for l in letters:
            yield from f(n - 1,
                         dp if dt < 0 else dt if dp < 0 else dt*dp,
                         -1,
                         lt + l
                         )
    else:
        yield 0 < lt == (dt<0 or dt)*(dp<0 or dp)

Проблема зводиться до пошуку дерева, в якому кожен рівень дерева відповідає позиції в номері номерного знака, і кожен вузол має 36 дітей (10 цифр і 26 літер). Функція здійснює рекурсивний пошук дерева, накопичуючи значення для цифр і літер у міру його проходження.

n is the number of levels to search. 
dp accumulates the sum of a group of digits.
dt accumulates the product of the digit sums.
lt accumulates the sum of the letter values.

For dp and dt, a value < 0 indicates it is not initialized.

Гольф включений, перетворюючи петлі для сум генераторів:

sum(f(n-1, 
      dt,
      d if dp < 0 else dp + d,
      lt) for d in digits)
+
sum(f(n-1,
      dp if dt<0 else dt if dp<0 else dt*dp,
      -1,
      lt+l) for l in letters)

Потім комбінуючи генератори. Кодуйте літери від A до Z, як від -1 до -26, а цифри - від 0 до 9. Отже, сума стає такою:

sum(f(n-1, *args) for c in range(-26, 10)),

де args:

((dp if dt<0 else dt if dp<0 else dt*dp, -1, lt-l) if c <0 else
 (dt, d if dp<0 else dp+d, lt))

Інша частина гольфу - це перетворення функції на лямбда, скорочення назв змінних та спрощення виразів.


Це красномовне рішення, яким би був час виконання? n*n*log(n)чи щось подібне?
Чарівний восьминіг Урна

@carusocomputing Спасибі Рішення все ще генерує всі можливі перестановки заданої довжини, тому воно має таку ж складність, як і інші рішення. Щось на кшталт k ** n, де k - кількість символів в алфавіті (наприклад, 10 цифр + 26 літер = 36), а n - кількість символів на номерному знаку. Для його виконання n = 5 потрібно перевірити 36 ^ 5 = 60,466,176 перестановок і зайняти хвилину-дві (запам'ятовування може прискорити її, але коштуватиме багато байт ;-)).
RootTwo

6

Діялог APL, 57 56 байт

+/(+/0⌈a-9)=×/c*⍨-2-/0,⌈\(+\a×b)×c←2>/0,⍨b←9≥a←↑1↓,⍳⎕⍴36

(передбачається ⎕io←0)

aматриця всіх дійсних номерних знаків (крім 00...0), закодована: 0-9 для цифр, 10-35 для літер

b бітмаска, де розміщуються цифри

c бітова маска для останньої цифри в кожній групі послідовних цифр



4

Python 2, 359 295 байт

Досить довгий; це тривіальне рішення. Я впевнений, що це правильно, хоча це не відповідає тестовим випадкам у виклику. Рішення, які я отримую, відповідають відповідям Дади.

import itertools as i,re as r,string as s
print len([''.join(x)for x in i.product(s.lowercase+s.digits,repeat=input())if(lambda t:r.search('\\D',t)and r.search('\\d',t)and reduce(int.__mul__,[sum(map(int,k))for k in r.split('\\D+',t)if k])==sum([k-96 for k in map(ord,t) if k>96]))(''.join(x))])

-64 байти завдяки пропозиціям від @numbermaniac


1
Ви можете зберегти близько трьох байтів у c (x) та останньому рядку; видаліть пробіл між 96 і for; між map(ord,x)і if; і в останньому рядку, між .join(x)і for. Я думаю, ви також можете заощадити ще більше, якщо перезначити функції лямбда.
numbermaniac

@numbermaniac Дякую! (Всього 64 байти)
HyperNeutrino

4

Python 2 , 291 287 276 273 байт

lambda n:sum(1for x in s.product(y+z,repeat=n)if(lambda p,s=set:reduce(int.__mul__,[sum(map(int,i))for i in re.findall(r"\d+",p)],1)==sum(ord(i)-64for i in p if ord(i)>64)and s(p)&s(y)and s(p)&s(z))(''.join(x)))
import re,itertools as s,string as t
y=t.uppercase
z=t.digits

Спробуйте в Інтернеті!


Результати:

0 0
1 0
2 18
3 355
4 8012

3

Perl 5 , 117 байт

116 байт коду + -pпрапор.

$"=",";@F=(A..Z,0..9);map{$r=1;$r*=eval s/./+$&/gr for/\d+/g;$r+=64-ord for/\pl/g;$\+=!$r*/\pl/*/\d/}glob"{@F}"x$_}{

Спробуйте в Інтернеті!

Це відчувається досить неоптимально, але я зараз поза ідеями.
Сам код дуже неефективний, оскільки він обчислює кожну перестановку a..z,0..9довжини n(це займає приблизно 1 секунду n=3, ~ 15 секунд n=4і ~ 7 хвилин для n=5).
Алгоритм є досить прямим вперед: для кожної можливої ​​розміру пластини n(генерованої з glob"{@F}"x$_- globоператор досить магічний), $r*=eval s/./+$&/gr for/\d+/g;обчислюється добуток кожного куска цифр і $r+=64-ord for/\pl/gвіднімає до нього вагу літер. Потім збільшуємо лічильник, $\якщо $rє 0( !$r) і якщо на табличці є цифри та букви ( /\pl/*/\d/). $\неявно друкується наприкінці завдяки -pпрапору.

Зверніть увагу , що номер я отримую є n=2 -> 18, n=3 -> 355, n=4 -> 8012, n=5 -> 218153. Я впевнений, що це правильні, але я можу помилитися, і в цьому випадку повідомте мені, і я видалю цю відповідь.


3

APL (Dyalog) , 71 байт

Повний орган програми. Підказки для N. N≥4 вимагають величезної кількості пам'яті та обчислень.

+/((+/⊢⍳∩)∘⎕A=(×/'\d+'S{+/⍎¨⍵.Match}))¨l/⍨∧⌿∨/¨c∘.∊l←,(∊c←⎕DA)∘.,⍣⎕⊂⍬

Спробуйте в Інтернеті!


2

Scala, 265 байт

(n:Int)=>{val i=('A'to'Z')++('0'to'9');Seq.fill(n)(i).flatten.combinations(n).flatMap(_.permutations).map(_.mkString).count(l=>"(?=.*[A-Z])(?=.*\\d)".r.findAllIn(l).size>0&&l.map(_-64).filter(_>0).sum==l.split("[A-Z]").filter(""<).map(_.map(_-48).sum).reduce(_*_))}

Пояснення:

(n:Int) => {
    val i = ('A' to 'Z') ++ ('0' to '9');                       // All license plates available characters.
    Seq.fill(n)(i).flatten                                      // Simulate combination with repetition (each character is present n times)
        .combinations(n)                                        // and generate all combinations of size n (all license plates).
        .flatMap(_.permutations)                                // For each combination, generate all permutations (ex. : Seq('A', '1') => Seq('A', '1') and Seq('1', 'A')), and
        .map(_.mkString)                                        // convert each permutation to String (Seq('A', '1') => "A1").
        .count ( l =>                                           // Then count all strings having :
            "(?=.*[A-Z])(?=.*\\d)".r.findAllIn(l).size > 0 &&   // at least 1 character and 1 digit and
            l.map(_ - 64).filter(_ > 0).sum ==                  // a sum of characters (> 'A' or > 64) equals to
            l.split("[A-Z]").filter(""<)
                .map(_.map(_ - 48).sum)
                .reduce(_*_)                                    // the product of sum of digits groups (split String by letters to get digits groups)
        )
}

Примітки:

  • -64і -48використовуються для перетворення аChar (відповідно літери і цифри) , його Intзначення ( 'A' - 64 = 1, 'B' - 64 = 2..., '9' - 48 = 9)
  • Фільтр в l.split("[A-Z]").filter(""<) використовується для усунення ""значень, якщо lпочинається з літери (приклад "A1".split("[A-Z]") => Array("", 1):). Можливо, буде краще і коротше рішення

Тестові приклади:

val f = (n:Int) => ...  // assign function
(1 to 5).foreach ( i =>
    println(s"N = $i: ${f(i)}")
)

Результати:

N = 1: 0
N = 2: 18
N = 3: 355
N = 4: 8012
N = 5: 218153

Ця функція є досить повільною, n > 4оскільки необхідно створити всі комбінації.


2

Java, 382 365 байт

  • Збережено 17 байт, завдяки Кевіну Крейсейну

Гольф

int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}
int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}
int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}
int s(int n){return f("",n);}

Детально

// return sum of adjecent digits
int h(String s)
{
    int sum = 0;
    for(char c : s.toCharArray()) sum += c-'0';
    return sum;
}

// check if perfect
int g(String t)
{
    int d = 1;
    int c = 0;

    for(String s : t.split("[^0-9]")) d *= h(s);
    for(String s : t.split("[^A-Z]")) c += s.charAt(0)-'A';

    return d == c ? 1 : 0;
}

// tree of enumerations
int f(String t, int n)
{
    // base case
    if(t.length() == n)
    {
        return g(t);
    }

    // enumeration
    int sum = 0;
    for(char d='0'; d<='9'; d++) sum += f(t+d, n);
    for(char c='A'; c<='Z'; c++) sum += f(t+c, n);

    return sum;
}

int s(int n){ return f("",n); }

Я думаю, вам потрібна функція, яка бере лише nвхід.
Крістіан Сіверс

@ChristianSievers виправлено
Khaled.K

1
Деякі речі для гольфу для вашого поточного коду: int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}int s(int n){return f("",n);}( 365 байт ) Ви можете порівняти свою поточну версію з цією, щоб побачити зміни, які я зробив (занадто багато, щоб увійти до залишку цього коментаря). :)
Кевін Кройсейсен

@KevinCruijssen thx, 17 байт зараз
Khaled.K

2

GAP , 416 байт

Не виграє розмір коду та далеко не постійний час, але використовує математику, щоб значно прискорити!

x:=X(Integers);
z:=CoefficientsOfUnivariatePolynomial;
s:=Size;

f:=function(n)
 local r,c,p,d,l,u,t;
 t:=0;
 for r in [1..Int((n+1)/2)] do
  for c in [r..n-r+1] do
   l:=z(Sum([1..26],i->x^i)^(n-c));
   for p in Partitions(c,r) do
    d:=x;
    for u in List(p,k->z(Sum([0..9],i->x^i)^k)) do
     d:=Sum([2..s(u)],i->u[i]*Value(d,x^(i-1))mod x^s(l));
    od;
    d:=z(d);
    t:=t+Binomial(n-c+1,r)*NrArrangements(p,r)*
         Sum([2..s(d)],i->d[i]*l[i]);
   od;
  od;
 od;
 return t;
end;

Щоб вичавити зайвий пробіл і отримати один рядок з 416 байтами, пропустіть це:

sed -e 's/^ *//' -e 's/in \[/in[/' -e 's/ do/do /' | tr -d \\n

Мій старий ноутбук "призначений для Windows XP" може обчислити f(10)менше ніж за одну хвилину і піти набагато далі за менше години:

gap> for i in [2..15] do Print(i,": ",f(i),"\n");od;
2: 18
3: 355
4: 8012
5: 218153
6: 6580075
7: 203255386
8: 6264526999
9: 194290723825
10: 6116413503390
11: 194934846864269
12: 6243848646446924
13: 199935073535438637
14: 6388304296115023687
15: 203727592114009839797

Як це працює

Припустимо, що спочатку ми хочемо лише дізнатися кількість досконалих номерних знаків, що відповідають шаблону LDDLLDL, де Lпозначається літера та Dпозначається цифра. Припустимо, у нас є список lтаких чисел, який l[i]дає кількість способів, якими букви можуть дати значення i, і подібний перелік dзначень, які ми отримуємо з цифр. Тоді кількість досконалих номерних знаків із загальним значенням iсправедлива l[i]*d[i], і ми отримуємо кількість всіх досконалих номерних знаків з нашим шаблоном, підсумовуючи це по всіх i. Позначимо операцію отримання цієї суми через l@d.

Тепер навіть якщо найкращим способом отримати ці списки було спробувати всі комбінації та підрахунок, ми можемо це зробити незалежно для букв та цифр, дивлячись на 26^4+10^3випадки замість 26^4*10^3 випадків, коли ми просто проходимо через усі таблички, що відповідають шаблону. Але ми можемо зробити набагато краще: lось лише список коефіцієнтів, (x+x^2+...+x^26)^kде kзнаходиться кількість літер, ось тут4 .

Аналогічно ми отримуємо числа способів отримати суму цифр у пробігу kцифр як коефіцієнти (1+x+...+x^9)^k. Якщо є кілька пробігів цифр, нам потрібно поєднати відповідні списки з операцією, d1#d2яка в позиції iмає значення суми всіх, d1[i1]*d2[i2]де . Разом з тим, що він є білінеарним, це дає приємний (але не дуже ефективний) спосіб його обчислити.i1*i2=i . Це згортання Діріхле, яке є просто добутокм, якщо трактувати списки як коефіцієнти ряду Дірхле. Але ми вже використовували їх як поліноми (кінцеві ряди потужностей), і немає хорошого способу інтерпретувати операцію для них. Я думаю, що ця невідповідність є частиною того, що ускладнює пошук простої формули. Давайте все-таки будемо використовувати його на многочленах і використовуємо те саме позначення #. Легко обчислити, коли один операнд є одночленним: маємоp(x) # x^k = p(x^k)

Зауважте, що kлітери дають значення максимум 26k, а k однозначні - значення9^k . Тому ми часто отримуємо непотрібні високі сили в dполіномі. Щоб позбутися від них, ми можемо обчислити модуль x^(maxlettervalue+1). Це дає велику швидкість і, хоча я не відразу помітив, навіть допомагає гольфу, тому що зараз ми знаємо, що ступінь dне більша за ступінь l, що спрощує верхню межу у фіналі Sum. Ми отримуємо ще кращу швидкість, роблячи modобчислення в першому аргументі Value (див. Коментарі), а все #обчислення на нижчому рівні дає неймовірне прискорення. Але ми все ще намагаємось бути законною відповіддю на проблему з гольфом.

Таким чином, ми отримали наші lі dможемо використовувати їх для обчислення кількості досконалих номерних знаків з малюнком LDDLLDL. Це те саме число, що і для візерунка LDLLDDL. Взагалі ми можемо змінювати порядок пробігів цифр різної довжини, як нам подобається, NrArrangementsдає кількість можливостей. І хоча між пробілами цифр повинна бути одна літера, інші літери не фіксуються. Розраховує Binomialці можливості.

Тепер залишається пройти всі можливі способи мати довжину цифр прогонів. rпроходить через усі числа прогонів, cчерез усі загальні числа цифр і pчерез усі розділи cз rпідсумками.

Загальна кількість розділів, на які ми дивимось, на два менше, ніж кількість розділів n+1, і функція розбиття зростає як exp(sqrt(n)). Тож, хоча існують ще прості способи поліпшити час роботи шляхом повторного використання результатів (проходження через розділи в іншому порядку), для принципового вдосконалення нам потрібно уникати огляду кожного розділу.

Обчислити це швидко

Зауважте, що (p+q)@r = p@r + q@r. Це самостійно допомагає уникнути деяких примножень. Але разом з (p+q)#r = p#r + q#rцим це означає, що ми можемо об'єднати простими доданими поліномами, що відповідають різним перегородкам. Ми не можемо просто їх додати, тому що нам ще потрібно знати, з чимl нам потрібно @поєднати, який фактор ми маємо використовувати, а який - #розширення ще можливі.

Давайте поєднаємо всі многочлени, що відповідають розділам, з однаковою сумою та довжиною, і вже враховуємо кілька способів розподілу довжин прогонів цифр. На відміну від того, про що я міркував у коментарях, мені не потрібно дбати про найменше використане значення чи про те, як часто воно використовується, якщо я переконуюсь, що я не поширюватимуся на це значення.

Ось мій код C ++:

#include<vector>
#include<algorithm>
#include<iostream>
#include<gmpxx.h>

using bignum = mpz_class;
using poly = std::vector<bignum>;

poly mult(const poly &a, const poly &b){
  poly res ( a.size()+b.size()-1 );
  for(int i=0; i<a.size(); ++i)
    for(int j=0; j<b.size(); ++j)
      res[i+j]+=a[i]*b[j];
  return res;
}

poly extend(const poly &d, const poly &e, int ml, poly &a, int l, int m){
  poly res ( 26*ml+1 );
  for(int i=1; i<std::min<int>(1+26*ml,e.size()); ++i)
    for(int j=1; j<std::min<int>(1+26*ml/i,d.size()); ++j)
      res[i*j] += e[i]*d[j];
  for(int i=1; i<res.size(); ++i)
    res[i]=res[i]*l/m;
  if(a.empty())
    a = poly { res };
  else
    for(int i=1; i<a.size(); ++i)
      a[i]+=res[i];
  return res;
}

bignum f(int n){
  std::vector<poly> dp;
  poly digits (10,1);
  poly dd { 1 };
  dp.push_back( dd );
  for(int i=1; i<n; ++i){
    dd=mult(dd,digits);
    int l=1+26*(n-i);
    if(dd.size()>l)
      dd.resize(l);
    dp.push_back(dd);
  }

  std::vector<std::vector<poly>> a;
  a.reserve(n);

  a.push_back( std::vector<poly> { poly { 0, 1 } } );
  for(int i=1; i<n; ++i)
    a.push_back( std::vector<poly> (1+std::min(i,n+i-i)));
  for(int m=n-1; m>0; --m){
    //    std::cout << "m=" << m << "\n";
    for(int sum=n-m; sum>=0; --sum)
      for(int len=0; len<=std::min(sum,n+1-sum); ++len){
        poly d {a[sum][len]} ;
        if(!d.empty())
          for(int sumn=sum+m, lenn=len+1, e=1;
              sumn+lenn-1<=n;
              sumn+=m, ++lenn, ++e)
            d=extend(d,dp[m],n-sumn,a[sumn][lenn],lenn,e);
      }
  }
  poly let (27,1);
  let[0]=0;
  poly lp { 1 };
  bignum t { 0 };
  for(int sum=n-1; sum>0; --sum){
    lp=mult(lp,let);
    for(int len=1; len<=std::min(sum,n+1-sum); ++len){
      poly &a0 = a[sum][len];
      bignum s {0};
      for(int i=1; i<std::min(a0.size(),lp.size()); ++i)
        s+=a0[i]*lp[i];
      bignum bin;
      mpz_bin_uiui( bin.get_mpz_t(), n-sum+1, len );
      t+=bin*s;
    }
  }
  return t;
}

int main(){
  int n;
  std::cin >> n;
  std::cout << f(n) << "\n" ;
}

Для цього використовується бібліотека MP GNU. На debian встановіть libgmp-dev. Компілювати з g++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx. Програма бере аргументи від stdin. Для встановлення часу використовуйтеecho 100 | time ./pl .

Наприкінці a[sum][length][i]наводиться кількість способів, за допомогою яких sum цифр у lengthпрогонах може дати число i. Під час обчислення, на початку mциклу, він дає кількість способів, які можна виконати з числами більше, ніж m. Все починається з a[0][0][1]=1. Зауважте, що це надмножина чисел, які нам потрібні для обчислення функції для менших значень. Таким чином, майже в той же час ми могли обчислити всі величини, що до них n.

Рекурсії немає, тому у нас фіксована кількість вкладених петель. (Найглибший рівень гніздування - 6.) Кожна петля проходить через ряд значень, лінійних у nгіршому випадку. Тому нам потрібен лише полином час. Якщо придивитись уважніше до вкладених iі jциклів extend, знайдемо верхню межу jформи N/i. Це має дати лише логарифмічний коефіцієнт для jциклу. Найбільш внутрішня петля в fsumnтощо) схожа. Також майте на увазі, що ми обчислюємо числа, які швидко ростуть.

Зауважте також, що ми зберігаємо O(n^3)ці номери.

Експериментально я отримую ці результати на розумному апаратному забезпеченні (i5-4590S): f(50)потрібна одна секунда та 23 Мб, f(100)21 секунди та 166 МБ, f(200)10 хвилин та 1,5 ГБ, f(300)потрібна година та 5,6 ГБ. Це говорить про складність у часі краще, ніж O(n^5).


Оскільки це проблема з кодовим гольфом, ця відповідь потребує гольфу. Вибачте.
Rɪᴋᴇʀ

1
@Riker Хоча я не думаю, що мій код був надто багатослівним для початку, я пограв ще трохи і взяв на себе тягар визначення його розміру, коли пробіл видаляється.
Крістіан Сіверс

1
@carusocomputing Боюся, що це набагато гірше. Я обробляю окремо кожен випадок розподілу цифр між циклами цифр, як, наприклад, один пробір з трьох цифр, або один пробіг з двох цифр і одна цифра, або три одноцифрові цифри, але для n=5цього немає з двома цифрами та двома однозначними цифрами, оскільки тоді нам не вистачає літер для розділення чисел. Так forроблять три зовнішні петлі: пробігайте всі корисні розділи чисел <n. (І я щойно зрозумів, що я також допускаю nцифри. За великої долі інша оптимізація вважає це 0).
Крістіан Сіверс

1
@carusocomputing Зауважте, що для чисел <n/2 , всі розділи корисні. А решта обчислень все ще займають свій непостійний час. Щоб побачити, що відбувається, ви можете додати Print(p,"\n");на початку корпусу for p...петлі. - У мене з’явилася ідея використовувати одну петлю менше, але це допоможе лише розміру коду.
Крістіан Сіверс

2
Я отримую дивовижну швидкість, переміщуючи mod(що вже дуже допомогло) Value, змінюючи його на Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1)). Це одне лише дозволяє проводити обчисленняf(15) за 80 секунд.
Крістіан Сіверс

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.