Mükemmel Plakalar


33

Mükemmel Plakalar

Birkaç yıl önce başlayarak, etrafta dolaşırken kendime küçük bir oyun yaptım: yakındaki plakaların "mükemmel" olup olmadığını kontrol ettim. Nispeten nadir görülür, ancak bir tane bulduğunuzda heyecan verici.

Bir plakanın mükemmel olup olmadığını kontrol etmek için:

  1. A = 1, B = 2, ... Z = 26 ile karakterleri toplayın.
  2. Her ardışık sayı yığınını al ve topla; bu toplamların her birini birlikte çarpın.

Bölüm 1 ve bölüm 2'deki değerler eşitse, tebrikler! Mükemmel bir plaka buldun!

Örnekler

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!

Meydan okuma

Örnek olarak 5 ve 6 uzunluklarındaki plakaları kullandım, ancak bu prosedür herhangi bir plaka uzunluğu için geçerlidir. Buradaki zorluk, verilen bir uzunluk için N, bu uzunluktaki mükemmel plaka sayısını geri döndürmektir. Meydan okuma için, geçerli bir plaka 0-9 rakamlarının ve AZ karakterlerinin herhangi bir birleşimidir. Plaka, potansiyel olarak mükemmel sayılabilecek hem karakter hem de sayı içermelidir . Kontrol amacıyla, işte aldığım değerler (doğruluklarının% 100 olamamasına rağmen, hahaha)

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

notlar

Bir şekilde sorunu kendi dilinizde daha basit hale getirirse, belirli bir N için mükemmel araç plakası oranını en az 2 anlamlı basamağa çıkarabilirsiniz .

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

VEYA, mod 256 eşdeğer değerini yazdırabilirsiniz

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

En kısa kazanır!


2
Siteye Hoşgeldiniz! Bunun iyi bir meydan okuma olduğunu düşünüyorum, ancak izin verilen ek çıktılar aslında yanıtları puanlamayı zorlaştırıyor. PPCG nesnel kazanma kriterleri arar ve bu kadar çok özgürlük olduğunda bunu yapmak zordur; bu yalnızca çıktı biçimini değiştirmez, gerçekte çıktı almanıza izin vereni değiştirir. Diğer seçenekleri düzenlemenizi ve sadece için mükemmel plaka sayısının çıkmasını gerekli kılmanızı öneririm N.
HyperNeutrino,

11
Belirli bir plakanın mükemmel olup olmadığını doğrulamak isteseydim şahsen bu mücadeleden daha çok zevk alırdım (özellikle test durumları için kesin sayılarınız olmadığı için. Potansiyel hesaplanan sonuçlar olduğu sürece muhtemelen tamam azalır emin değil insanlar olsa nasıl hissettiğini diğeri hakkında Güzel fikir ama..!
MildlyMilquetoast

4
Mistah Figgins’le aynı fikirdeyim; Bu şekilde daha ilginç bir meydan okuma olan bir model bulma konusunda daha fazla şey hissediyorum, ancak sadece bir doğrulama kontrolü olsaydı daha fazla cevap alabileceğini düşünüyorum. Yine de güzel bir meydan okuma!
HyperNeutrino,

1
Bu harika soruya dikkat çekeceğini ve bunun yanı sıra biraz basitleştiğinden, sadece (neredeyse) mükemmel plakayı kontrol edeceğimi umarak yakından ilgili bir sorun çıkardım .
Bay Xcoder

1
@ carusocomputing Elimden geleni yaptım ama boş geldi. Onu matematik öğretmenime gönderdim ve şimdiye kadar boş
Christopher

Yanıtlar:


9

Python 3.6, 150 bayt

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

Sonuçlar:

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

Açıklama ile Ungolfed versiyonu:

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)

Sorun, ağacın her seviyesinin bir plaka numarasındaki bir pozisyona tekabül ettiği ve her düğümün 36 çocuğu (10 hane ve 26 harf) olduğu bir ağacı aramakla sonuçlanır. Bu fonksiyon ağacın özyinelemeli bir taramasını yapar ve sayı ve harflerin değerlerini toplar.

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.

Golf, for loop'ları jeneratör toplamlarına dönüştürür:

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)

Sonra jeneratörleri birleştiriyor. Harfleri A'dan Z'ye, -1'den -26'ya ve rakamları 0'dan 9'a kodlayın. Böylece toplam:

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

Args nerede:

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

Golfün geri kalanı, işlevi bir lambdaya dönüştürmek, değişken isimlerini kısaltmak ve ifadeleri sadeleştirmektir.


Bu önemli bir çözüm, çalışma zamanı ne olurdu? n*n*log(n)Veya benzeri?
Magic Octopus Urn

@carusocomputing Teşekkürler. Çözüm hala verilen uzunluktaki tüm olası permütasyonları üretmektedir, bu nedenle diğer çözeltilerle aynı karmaşıklığa sahiptir. K ** n gibi bir şey, burada k, alfabedeki sembollerin sayısıdır (örneğin, 10 rakam + 26 harf = 36) ve n, bir plakadaki sembollerin sayısıdır. N = 5 için çalıştırılması 36 ^ 5 = 60,466,176 permütasyonun kontrol edilmesini gerektirir ve bir veya iki dakika sürdü (notlandırma işlemi hızlandırabilir, ancak bir sürü bayta mal olabilir ;-)).
RootTwo

6

Dyalog APL, 57 56 bayt

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

(varsayar ⎕io←0)

a00...0ile kodlanmış tüm geçerli plaka plakalarının matrisi (hariç ): rakamlar için 0-9, harfler için 10-35

b basamakların gerçekleştiği yer için bit maskesi

c ardışık basamakların her grubunda son basamak için bit maskesi


1-4 için çevrimiçi deneyin 4 için daha fazla hafızaya ihtiyacı var
Adám

4

Python 2,359 295 bayt

Oldukça uzun; bu önemsiz bir çözümdür. Bunun doğru olduğuna eminim, ancak meydan okumadaki test durumlarıyla eşleşmiyor. Alıyorum çözümleri Dada'nın cevaplarına uyuyor.

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'in önerileri sayesinde -64 bayt


1
C (x) ve son satırda yaklaşık üç bayt kaydedebilirsiniz; 96 ile 96 arasındaki bir boşluğu kaldırın for; arasında map(ord,x)ve if; ve son satırda, .join(x)ve arasında for. İşlevleri lambda olarak yeniden tanımladığınızda daha da fazla tasarruf edebileceğinizi düşünüyorum.
numbermaniac

@numbermaniac Teşekkürler! (Toplam 64 bayt)
HyperNeutrino

4

Python 2 , 291 287 276 273 bayt

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

Çevrimiçi deneyin!


Sonuçlar:

0 0
1 0
2 18
3 355
4 8012

3

Perl 5 , 117 bayt

116 bayt kodu + -pbayrak.

$"=",";@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$_}{

Çevrimiçi deneyin!

Oldukça yetersiz görünüyor, ama şu anda fikirlerden uzakım.
Kodun kendisi, a..z,0..9uzunluktaki her permütasyonu hesapladığı için çok verimsizdir n(kabaca 1 saniye n=3, ~ 15 saniye n=4ve ~ 7 dakika sürer n=5).
Algoritma oldukça basittir: her büyüklük plakası için n(üretilen glob"{@F}"x$_- globoperatör oldukça sihirdir), $r*=eval s/./+$&/gr for/\d+/g;her basamak hanesinin ürününü hesaplar $r+=64-ord for/\pl/gve harflerin ağırlığını çıkarır. Daha sonra, sayaç artırmak $\halinde $rolan 0( !$r) ve plaka, rakam ve harf içeriyorsa ( /\pl/*/\d/). bayrak $\sayesinde dolaylı olarak basılmıştır -p.

Not I elde sayılar olduğunu n=2 -> 18, n=3 -> 355, n=4 -> 8012, n=5 -> 218153. Bunların doğru olanlar olduğuna eminim, ama hatalı olabilirim, bu durumda bana bildirin ve bu cevabı sildim.


3

APL (Dyalog) , 71 bayt

Tam program gövdesi. N için bilgi istemi N≥4 büyük miktarda bellek ve hesaplama gerektirir.

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

Çevrimiçi deneyin!


2

Scala, 265 bayt

(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(_*_))}

Açıklamalar:

(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)
        )
}

Notlar:

  • -64ve -48bir dönüştürmek için kullanılır Charonun için (sırasıyla mektup ve rakamı) Intdeğeri ( 'A' - 64 = 1, 'B' - 64 = 2, ..., '9' - 48 = 9)
  • Filtre, bir harfle başlarsa değerleri l.split("[A-Z]").filter(""<)elemek için kullanılır (örnek:) . Daha iyi ve daha kısa bir çözüm olabilir""l"A1".split("[A-Z]") => Array("", 1)

Test durumları:

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

Sonuçlar :

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

İşlev, n > 4tüm kombinasyonların oluşturulması gerektiğinden oldukça yavaştır .


2

Java, 382 365 bayt

  • Kevin Cruijssen sayesinde 17 bayt kaydedildi

golfed

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);}

Detaylı

// 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); }

Sadece ngirdi olarak alan bir işleve ihtiyacınız olduğunu düşünüyorum .
Christian Sievers,

@ ChristianSievers sabit
Khaled.K

1
Geçerli kodunuz için golf oynamak için bazı şeyler: 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 bayt ) Yaptığım değişiklikleri görmek için mevcut sürümünüzü bu sürümle karşılaştırabilirsiniz (bu yorumun geri kalanına sığmayacak kadar çok). :)
Kevin Cruijssen

@KevinCruijssen thx, 17 bayt kapalı
Khaled.K

2

GAP , 416 bayt

Kod boyutunda kazanamayacaksınız ve sabit zamandan uzak duracaksınız, ama çok hızlandırmak için matematiği kullanıyor!

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;

Gereksiz boşlukları sıkıştırmak ve 416 byte'lık bir satır almak için aşağıdakileri işaretleyin:

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

Eski "Windows XP için tasarlanmış" dizüstü bilgisayarım f(10)bir dakikadan daha az bir sürede hesaplayabilir ve bir saat içinde daha da ileri gidebilir:

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

Nasıl çalışır

Biz ilk sadece desen uydurma mükemmel plakalı sayısını bilmek istediğinizi varsayalım LDDLLDL, Lbir mektup gösterir ve Dbir rakamı ifade eder. Biz bir listesi var varsayalım lböyle sayıların l[i]harfler değer vermek yolları sayısını verir ive benzer bir liste dbiz basamağı aldığım değerler için. Ardından ortak değere sahip mükemmel plakalı sayısı iadildir l[i]*d[i]ve hepimiz üzerinden bu toplayarak bizim desenli tüm mükemmel plakalı sayısını almak i. Bu toplamı elde etme işlemini gösterelim l@d.

Şimdi bu listeleri elde etmenin en iyi yolu tüm kombinasyonları denemek ve saymak olsa bile , bunu sadece desene uyan tüm plakaları incelediğimizde 26^4+10^3durumlara bakmak yerine harflere ve rakamlara bağımsız olarak yapabiliriz 26^4*10^3. Ama biz çok daha iyisini yapabiliriz: lkatsayıları sadece listesidir (x+x^2+...+x^26)^knerede kharflerin sayısıdır, burada 4.

Benzer şekilde, bir basamak basamağında basamakların toplamının kkatsayıları olarak alınmasının yollarının sayısını alırız (1+x+...+x^9)^k. Birden fazla basamak koşusu varsa, karşılık gelen listeleri d1#d2, konumdaki iher d1[i1]*d2[i2]yerin toplamının değeri kadar olan bir işlemle birleştirmemiz gerekir i1*i2=i. Bu, listeleri Dirchlet serisinin katsayıları olarak yorumladığımız takdirde yalnızca ürün olan Dirichlet evrimidir. Fakat biz bunları zaten polinomlar (sonlu güç serileri) olarak kullandık ve bu operasyonu onlar için yorumlamanın iyi bir yolu yok. Bu uyumsuzluğun basit bir formül bulmayı zorlaştıran şeyin bir parçası olduğunu düşünüyorum. Hadi yine de polinomlarda kullanalım ve aynı notasyonu kullanalım.# . Bir işlenenin monomial olduğu zaman hesaplamak kolaydır:p(x) # x^k = p(x^k). Bilinear olduğu gerçeğiyle birlikte, bu onu hesaplamak için güzel (ama çok verimli değil) bir yol sunar.

Not kharfler en az bir değer vermek 26kederken, k tek basamaklı bir değer verebilir 9^k. Bu yüzden dpolinomda sıklıkla gereksiz yüksek güçlere sahip olacağız . Onlardan kurtulmak için moduloyu hesaplayabiliriz x^(maxlettervalue+1). Bu büyük bir hız kazandırıyor ve hemen farketmeme rağmen golfe bile yardımcı oluyor, çünkü artık derecenin dbundan daha büyük olmadığını biliyoruz.l finalin üst sınırını basitleştirenSum . modİlk argümanında bir hesaplama yaparak daha da hızlanırız Value (yorumlara bakın) ve tüm #hesaplamanın daha düşük bir seviyede yapılması inanılmaz bir hızlanma sağlar. Ama hala bir golf problemine meşru bir cevap olmaya çalışıyoruz.

Bu yüzden biz elimizde lve donları mükemmel plaka sayısını desenle hesaplamak için kullanabilirizLDDLLDL . Bu, kalıpla aynı sayıdır LDLLDDL. Genel olarak, farklı uzunluktaki rakam dizilerinin sırasını istediğimiz gibi değiştirebiliriz, NrArrangementsolasılık sayısını veririz . Ve rakamların arasında bir harf olması gerekirken, diğer harfler sabit değildir. BinomialBu olasılıkları sayar.

Artık, uzunlukları kaç basamak hane sahip olabileceği tüm yollardan geçmeye devam ediyor. rTüm koşu sayıları boyunca, ctüm toplam sayı sayıları ile yapılan vep toplamlarla cbirlikte tüm bölümleri boyunca rçalıştırır.

Baktığımız toplam bölüm sayısı, bölüm sayısından iki kat daha az n+1 ve bölümleme işlevleri de büyüyor exp(sqrt(n)). Bu nedenle, sonuçları tekrar kullanarak (bölümleri farklı bir sırada geçirerek) çalışma süresini iyileştirmenin kolay yolları olsa da, temel bir iyileştirme için her bölüme ayrı ayrı bakmaktan kaçınmamız gerekir.

Hızlı hesaplamak

Bunu not al (p+q)@r = p@r + q@r. Kendi başına, bu sadece bazı çarpmalardan kaçınmaya yardımcı olur. Ancak (p+q)#r = p#r + q#rbununla birlikte, farklı bölümlere karşılık gelen basit ek polinomları ile birleşebileceğimiz anlamına gelir. Hepsini ekleyemiyoruz, çünkü hangisini lyapmamız gerektiğini bilmemiz gerekiyor .@ biz kullanıma zorunda ve hangi faktör, -combine #-extensions hala mümkündür.

Bölümlere tekabül eden tüm polinomları aynı toplam ve uzunluk ile birleştirelim ve halihazırda basamak koşularının uzunluğunu dağıtmanın birçok yolunu hesaba katalım. Yorumlarda söylediklerimden farklı olarak, en küçük kullanılan değeri veya ne sıklıkta kullanıldığını umursamama gerek yok, eğer bu değerle uzatacağımdan emin olmazsam.

İşte benim C ++ kodum:

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

Bu GNU MP kütüphanesini kullanır. Debian'da yükleyin libgmp-dev. İle derleyin g++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx. Program argümanını stdin'den alıyor. Zamanlama için kullanın echo 100 | time ./pl.

Sonunda, koşulardaki basamakların sayıyı verebileceği a[sum][length][i]yolların sayısını verir . Hesaplama sırasında, döngünün başlangıcında, büyük sayılarla yapılabilecek yol sayısını verir . Her şey ile başlar . Bunun, daha küçük değerler için fonksiyonu hesaplamamız için gereken sayıların üst kümesi olduğunu unutmayın. Neredeyse aynı anda, tüm değerleri hesaplayabiliriz .sumlengthimma[0][0][1]=1n

Özyineleme yok, bu nedenle sabit sayıda iç içe geçmiş döngümüz var. (En derin yerleştirme seviyesi 6'dır.) Her döngü nen kötü durumda doğrusal olan bir dizi değerden geçer . Bu yüzden sadece polinom zamana ihtiyacımız var. Eğer iç içe geçmişe bakar ive içine jgirersek , form extendiçin bir üst sınır buluruz . Bu sadece döngü için logaritmik bir faktör vermelidir . İçindeki en içteki döngü ( etc ile) benzerdir. Ayrıca hızlı büyüyen sayılarla hesapladığımızı da unutmayın.jN/ijfsumn

Ayrıca O(n^3)bu numaraları sakladığımızı da unutmayın .

Deneysel olarak, bu sonuçları makul donanım (i5-4590S) ile elde ediyorum: f(50)bir saniye ve 23 MB f(100)gerekiyor, 21 saniye ve 166 MB f(200)gerekiyor, 10 dakika ve 1,5 GB f(300)gerekiyor ve bir saat 5,6 GB gerekiyor. Bu, zaman karmaşıklığından daha iyi olduğunu gösteriyor O(n^5).


Bu bir kod golf mücadeledir gibi bu cevabı ihtiyacı golfed edilecek. Üzgünüm.
Rɪᴋᴇʀ

1
@Riker Kodumun başlamak için aşırı derecede ayrıntılı olduğunu düşünmeme rağmen, biraz daha golf oynadım ve boşluklar sıkıldığında boyutunu belirleme yükünü aldım.
Christian Sievers,

1
@ carusocomputing Korkarım çok daha kötü. Üç basamaktan oluşan bir basamak veya bir basamaklı iki basamak ve bir tek basamaklı basamak gibi, basamak basamaklarının arasına rakam dağıtma durumlarını ayrı ayrı ele alıyorum, ya da üç tane basamak n=5var iki rakam ve iki rakamdan oluşan bir rakam olması durumunda, rakamları ayırmak için yeterli harfimiz yoktur. Bu, üç dış fordöngünün yaptığı şeydir : sayıların tüm yararlı bölümlerini geç <n. (Ve sadece nrakamlara izin verdiğimi de farkettim . Şans eseri başka bir optimizasyon bunu 0 olarak sayar).
Christian Sievers,

1
sayılar için o Not @carusocomputing <n/2, tüm bölümleri faydalıdır. Ve kalan hesaplamalar hala sabit olmayan zamanlarını alıyor. Neler olup bittiğini görmek için, döngünün Print(p,"\n");gövdesinin başına ekleyebilirsiniz for p.... - Bir döngüyü daha az kullanmakla ilgili bir fikrim var, ancak yalnızca kod boyutuna yardımcı olacak.
Christian Sievers,

2
mod(Zaten çok yardımcı oldu) içine alarak Value, onu değiştirerek inanılmaz bir hız kazanıyorum Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1)). Bu tek başına f(15)80 saniye içinde hesaplama yapmanıza izin veriyor .
Christian Sievers

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.