완벽한 번호판


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에 대한 완벽한 번호판 의 비율 을 2 자리 이상의 유효 숫자로 출력 할 수 있습니다 .

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
주어진 번호판이 완벽한 지 여부를 단순히 검증하는 경우 (특히 테스트 사례에 대한 명확한 숫자가 없기 때문에 개인적 으로이 도전을 훨씬 더 많이 누릴 것입니다. 다른 사람들이 어떻게 느끼는지 잘 모르겠지만 좋은 생각이지만
MildlyMilquetoast

4
나는 Mistah Figgins에 동의합니다. 이런 식으로 생각하면 패턴을 찾는 것이 더 흥미로운 문제이지만 여전히 확인 검사일 경우 더 많은 답변을 얻을 수 있다고 생각합니다. 그래도 좋은 도전입니다!
HyperNeutrino

1
나는 이 훌륭한 질문에주의를 끌고이를 단순화하고 완벽한 번호판 (거의) 만 확인하기를 기대하면서 밀접하게 관련된 도전 을 게시 했습니다 .
Mr. Xcoder

1
@ carusocomputing 최선을 다했지만 비어있었습니다. 수학 선생님에게 보냈는데 지금까지 비어 있습니다
Christopher

답변:


9

파이썬 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.

골프가 포함되어 for 루프를 발전기의 합으로 변환합니다.

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)아니면 비슷한?
매직 문어 Urn

@carusocomputing 감사합니다. 솔루션은 여전히 ​​주어진 길이의 가능한 모든 순열을 생성하므로 다른 솔루션과 동일한 복잡성을 갖습니다. k ** n과 같은 것, 여기서 k는 알파벳의 기호 수 (예 : 10 자리 + 26 자 = 36)이고 n은 번호판의 기호 수입니다. n = 5 동안 실행하면 36 ^ 5 = 60,466,176 순열을 확인해야하며 1 ~ 2 분이 걸렸습니다 (메모리 속도가 빨라지지만 많은 바이트가 필요합니다 ;-)).
RootTwo

6

Dyalog APL, 57 56 바이트

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

(가정 ⎕io←0)

a00...0숫자로 0-9, 문자로 10-35로 인코딩 된 모든 유효한 번호판 (제외 )의 매트릭스

b 숫자가 발생하는 비트 마스크

c 모든 연속 자릿수 그룹의 마지막 자릿수에 대한 비트 마스크


1-4 온라인 4를 위해 더 많은 메모리가 필요 하지만 온라인으로 시도 하지만 그 주위에도 방법이 있습니다!
Adám

4

파이썬 2, 359 295 바이트

오히려 길다. 이것은 사소한 해결책입니다. 도전 과제의 테스트 사례와 일치하지 않지만 이것이 정확하다고 확신합니다. 내가 얻는 솔루션은 Dada의 답변과 일치합니다.

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))])

@numbermaniac의 제안 덕분에 -64 바이트


1
c (x)와 마지막 줄에 약 3 바이트를 저장할 수 있습니다. 96과 for~ 사이의 공백을 제거하십시오 . 사이 map(ord,x)if; 마지막 줄에서와 사이 .join(x)for있습니다. 함수를 람다로 재정의하면 더 많은 비용을 절약 할 수 있다고 생각합니다.
numbermaniac

@numbermaniac 감사합니다! (총 64 바이트)
HyperNeutrino

4

파이썬 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

펄 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

스칼라, 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

자바, 382365 바이트

  • Kevin Cruijssen 덕분에 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입력으로 만 사용할 수있는 기능이 필요하다고 생각합니다 .
Christian Sievers

@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 bytes ) 현재 버전을이 버전과 비교하여 내가 한 변경 사항을 볼 수 있습니다 (이 의견의 나머지 부분에 너무 맞지 않음). :)
Kevin Cruijssen

@KevinCruijssen thx, 17 바이트 할인
Khaled.K

2

간격 , 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)1 분 안에 계산할 수 있고 1 시간 안에 훨씬 더 나아갈 수 있습니다 .

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]idil[i]*d[i]il@d

이 목록을 얻는 가장 좋은 방법은 모든 조합과 계산을 시도하는 것이었지만 문자와 숫자에 대해 독립적 으로이 작업을 수행 할 수 있습니다 . 패턴에 맞는 모든 판을 통과하는 26^4+10^3경우 대신 26^4*10^3케이스를 봅니다. 그러나 우리는 훨씬 더 잘 할 수 있습니다 : 문자 수가 어디에 있는지 에 대한 l계수 목록 일뿐 입니다.(x+x^2+...+x^26)^kk4

마찬가지로, 우리 k는 계수로 자릿수 의 자릿수를 얻는 방법의 수를 얻습니다 (1+x+...+x^9)^k. 하나 이상의 자릿수가있는 경우 해당 목록 d1#d2을 위치 i에있는 모든 d1[i1]*d2[i2]위치 의 합이 값인 연산과 결합해야합니다 i1*i2=i. 이것은 Dirichlet 컨볼 루션입니다. 목록을 Dirchlet 시리즈의 계수로 해석하면 제품입니다. 그러나 우리는 이미 그것들을 다항식 (유한 한 파워 시리즈)으로 사용했으며, 그것들을위한 연산을 해석하는 좋은 방법은 없습니다. 이 불일치가 간단한 수식을 찾기 어려운 부분이라고 생각합니다. 어쨌든 다항식에 사용하고 같은 표기법을 사용합시다 #. 하나의 피연산자가 단일 일 때 쉽게 계산할 수 있습니다.p(x) # x^k = p(x^k). 그것이 쌍 선형이라는 사실과 함께, 이것은 그것을 계산하는 좋은 (매우 효율적이지 않은) 방법을 제공합니다.

참고 k편지를 최대에서의 값을 제공 26k하면서, k 한 자리의 값을 제공 할 수 있습니다 9^k. 따라서 우리는 종종 d다항식 에서 불필요한 높은 힘을 얻습니다 . 그것들을 제거하기 위해 모듈로를 계산할 수 있습니다 x^(maxlettervalue+1). 이것은 빠른 속도를 제공하지만, 즉시 눈치 채지 못했지만 골프를 돕습니다. 우리는 이제 학위가 학위 d보다 크지 않다는 것을 알기 때문에 l결승전의 상한을 단순화합니다 Sum. 우리 mod는 첫 번째 논점에서 계산을 함으로써 더 빠른 속도를 얻습니다 Value (주석 참조). 그리고 #더 낮은 수준에서 전체 계산을 수행 하면 놀라운 속도를 얻 습니다. 그러나 우리는 여전히 골프 문제에 대한 합법적 인 답변을 찾고 있습니다.

그래서 우리는 우리를 가지고 ld패턴으로 완벽한 번호판의 수를 계산하는 데 사용할 수 있습니다 LDDLLDL. 패턴과 같은 숫자입니다 LDLLDDL. 일반적으로 원하는 길이가 다른 숫자의 런 순서를 변경할 NrArrangements수 있으며 가능한 수를 제공합니다. 숫자 사이에 하나의 문자가 있어야하지만 다른 문자는 고정되어 있지 않습니다. 는 Binomial이러한 가능성을 계산합니다.

이제 길이가 런 자릿수를 갖는 가능한 모든 방법을 통해 실행됩니다. r, 실행의 모든 번호를 통해 실행 c모든 숫자 총 숫자를 통해, 그리고 p모든 파티션을 통해 cr피가수.

우리가 보는 총 파티션 수는 파티션 수보다 두 개가 적으며 n+1파티션 기능은 다음과 같이 커 exp(sqrt(n))집니다. 따라서 파티션을 통해 실행되는 결과를 다른 순서로 다시 실행하여 실행 시간을 개선하는 쉬운 방법이 여전히 있지만 기본 개선을 위해서는 각 파티션을 따로 보지 않아야합니다.

빠른 컴퓨팅

참고하십시오 (p+q)@r = p@r + q@r. 그 자체만으로도 곱셈을 피할 수 있습니다. 그러나 (p+q)#r = p#r + q#r그것 과 함께 우리는 다른 파티션에 해당하는 간단한 추가 다항식으로 결합 할 수 있습니다. 우리가 아직있는 알 필요가 있기 때문에 우리는 그들 모두를 추가 할 수 없습니다 l우리가해야 @우리가 사용해야하고, 어떤 어떤 요인, -combine #-Extensions은 여전히 가능합니다.

파티션에 해당하는 모든 다항식을 동일한 합과 길이로 결합하고 이미 자릿수 길이를 분산시키는 여러 가지 방법을 설명합시다. 내가 의견에서 추측 한 것과는 달리, 내가 그 값으로 확장되지 않도록하기 위해 사용 된 가장 작은 값이나 사용 빈도에 대해 신경 쓸 필요가 없습니다.

내 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" ;
}

이것은 GNU MP 라이브러리를 사용합니다. 데비안에서는을 설치하십시오 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최악의 경우 선형 인 여러 값을 통과합니다 . 따라서 다항식 시간 만 필요합니다. 우리가 가까이 중첩에서 보면 ij의 루프 extend, 우리에 대한 상한 찾을 j양식을 N/i. j루프에 대한 로그 팩터 만 제공해야합니다 . 가장 안쪽의 루프 f (with sumn등)는 비슷합니다. 또한 빠르게 증가하는 숫자로 계산합니다.

우리는 또한 O(n^3)이 숫자 들을 저장 합니다.

실험적으로 합리적인 하드웨어 (i5-4590S)에서이 결과를 얻습니다. f(50)1 초 및 23MB, f(100)21 초 및 166MB , f(200)10 분 및 1.5GB, f(300)1 시간 5.6GB 가 필요합니다. 이는보다 복잡한 시간을 제안합니다 O(n^5).


이것은 코드 골프 챌린지 이므로이 답변 골퍼되어야합니다. 죄송합니다.
Rɪᴋᴇʀ

1
@Riker 내 코드가 너무 장황하다고 생각하지는 않지만 공백을 더 많이 넣을 때 더 많은 골프를 치고 크기를 결정하는 부담을 가졌습니다.
Christian Sievers

1
@ carusocomputing 훨씬 나 빠질까 걱정됩니다. 나는 개별적으로 세 자리 중 하나 실행이, 또는 3 개 개의 단일 숫자가이 두 자리 중 하나 개를 실행하고 하나 개의 자리가, 나처럼, 숫자의 실행 사이에 자리를 배포 각각의 경우를 처리하지 않지만있어 n=5더있다, 두 자리 숫자와 두 자리 숫자로 구성된 경우, 숫자를 구분하기에 문자가 충분하지 않기 때문입니다. 이것은 세 개의 외부 for루프가하는 것입니다 : 유용한 모든 숫자 파티션을 통과하십시오 <n. (그리고 방금 n숫자 도 허용한다는 것을 깨달았습니다 . 운 좋게도 다른 최적화는 이것을 0으로 계산합니다.)
Christian Sievers

1
@carusocomputing numbers의 <n/2경우 모든 파티션이 유용합니다. 나머지 계산에는 여전히 일정하지 않은 시간이 걸립니다. 무슨 일이 일어나고 있는지 확인하기 Print(p,"\n");위해 for p...루프 본문의 시작 부분에 추가 할 수 있습니다 . -하나의 루프를 적게 사용한다는 아이디어가 있지만 코드 크기에만 도움이됩니다.
Christian Sievers

2
나는 mod(많은 도움이 된)로 이동하여 로 Value변경 하여 놀라운 속도를 얻습니다 Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1)). 그것만으로도 f(15)80 초 안에 계산할 수 있습니다 .
Christian Sievers

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.