Targhe perfette


33

Targhe perfette

A partire da qualche anno, mi sono fatto un piccolo gioco mentre guidavo: controllare se le targhe vicine sono "perfette". È relativamente raro, ma emozionante quando ne trovi uno.

Per verificare se una targa è perfetta:

  1. Riassumi i caratteri, con A = 1, B = 2, ... Z = 26.
  2. Prendi ogni pezzo consecutivo di numeri e sommali; moltiplicare ciascuna di queste somme insieme.

Se i valori nella parte 1 e nella parte 2 sono uguali, congratulazioni! Hai trovato una targa perfetta!

Esempi

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!

La sfida

Ho usato targhe di lunghezza 5 e 6 come esempi, ma questa procedura è valida per qualsiasi lunghezza di targa. La tua sfida è, per una data lunghezza N, restituire il numero di targhe perfette di quella lunghezza. Ai fini della sfida, una targa valida è qualsiasi combinazione di cifre 0-9 e caratteri AZ. La piastra deve contenere sia un carattere che un numero per essere considerata potenzialmente perfetta. Ai fini del controllo, ecco i valori che ho ottenuto (anche se non posso essere al 100% sulla loro correttezza, ahahah)

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

Gli appunti

Se in qualche modo semplifica il problema nella tua lingua, potresti ottenere la proporzione di targhe perfette per una data N, ad almeno 2 cifre significative.

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

OPPURE, è possibile emettere il valore equivalente mod 256

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

Vittorie più brevi!


2
Benvenuti nel sito! Penso che questa sia una buona sfida, ma gli output consentiti aggiuntivi rendono difficile il punteggio effettivo delle risposte. PPCG cerca criteri obiettivi vincenti, ed è difficile farlo quando ci sono così tante libertà; questo non cambia solo il formato di output, ma cambia anche ciò che ti è permesso di produrre. Consiglierei di modificare le altre opzioni e di renderle necessarie per produrre il numero di targhe perfette per N.
HyperNeutrino,

11
Personalmente mi piacerebbe molto di più questa sfida se si stesse semplicemente confermando se una determinata targa è perfetta o meno (soprattutto perché non si hanno numeri definitivi per i casi di test. Probabilmente va bene, purché i potenziali risultati calcolati sono ridotti. Non sono sicuro di come si sentano le altre persone. Bella idea però!
MildlyMilquetoast

4
Sono d'accordo con Mistah Figgins; Penso che in questo modo si tratti più di trovare un modello, che è ancora una sfida interessante, ma penso che potrebbe attirare più risposte se fosse solo un controllo di validazione. Bella sfida però!
HyperNeutrino,

1
Ho pubblicato una sfida strettamente correlata , sperando che attiri l'attenzione su questa meravigliosa domanda, oltre a semplificarla un po ', controllando solo la (quasi) perfetta targa.
Mr. Xcoder

1
@carusocomputing Ho fatto del mio meglio ma mi sono ritrovato vuoto. L'ho inviato al mio insegnante di matematica ed è ancora vuoto
Christopher,

Risposte:


9

Python 3.6, 150 byte

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

i risultati:

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

Versione non golfata con spiegazione:

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)

Il problema si riduce alla ricerca di un albero in cui ogni livello dell'albero corrisponde a una posizione in un numero di targa e ogni nodo ha 36 figli (10 cifre e 26 lettere). La funzione esegue una ricerca ricorsiva dell'albero, accumulando i valori delle cifre e delle lettere mentre procede.

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 incluso, convertendo i loop for in somme di generatori:

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)

Quindi combinando i generatori. Codifica le lettere, dalla A alla Z, da -1 a -26 e le cifre da 0 a 9. Quindi la somma diventa:

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

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

Il resto del golf sta convertendo la funzione in lambda, accorciando i nomi delle variabili e semplificando le espressioni.


Questa è una soluzione eloquente, quale sarebbe il tempo di esecuzione? n*n*log(n)o qualcosa di simile?
Magic Octopus Urn,

@carusocomputing Grazie. La soluzione genera ancora tutte le possibili permutazioni della lunghezza data, quindi ha la stessa complessità delle altre soluzioni. Qualcosa come k ** n, dove k è il numero di simboli dell'alfabeto (ad es. 10 cifre + 26 lettere = 36) e n è il numero di simboli su una targa. Eseguirlo per n = 5 richiede il controllo di 36 ^ 5 = 60.466.176 permutazioni e richiede un minuto o due (la memoizzazione potrebbe accelerare, ma costerebbe molti byte ;-)).
Root Due

6

Dyalog APL, 57 56 byte

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

(presume ⎕io←0)

amatrice di tutte le targhe (tranne 00...0) valide codificate con: 0-9 per le cifre, 10-35 per le lettere

b maschera di bit per dove si verificano le cifre

c maschera di bit per l'ultima cifra in ogni gruppo di cifre consecutive


Provalo online per 1-4 ha bisogno di più memoria per 4, ma ci sono anche modi per aggirare questo!
Adám,

4

Python 2, 359 295 byte

Piuttosto lungo; questa è la soluzione banale. Sono sicuro che questo sia corretto, anche se non corrisponde ai casi di test nella sfida. Le soluzioni che sto ottenendo corrispondono alle risposte di 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))])

-64 byte grazie ai suggerimenti di @numbermaniac


1
È possibile salvare circa tre byte in c (x) e l'ultima riga; rimuovere uno spazio tra 96 ​​e for; tra map(ord,x)e if; e nell'ultima riga, tra .join(x)e for. Penso che puoi anche risparmiare ancora di più se ridefinisci le funzioni in lambdas.
numbermaniac

@numbermaniac Grazie! (64 byte in totale)
HyperNeutrino

4

Python 2 , 291 287 276 273 byte

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

Provalo online!


risultati:

0 0
1 0
2 18
3 355
4 8012

3

Perl 5 , 117 byte

116 byte di codice + -pflag.

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

Provalo online!

Sembra abbastanza subottimale, ma sono senza idee in questo momento.
Il codice stesso è molto inefficiente in quanto calcola ogni permutazione di a..z,0..9lunghezza n(richiede circa 1 secondo per n=3, ~ 15 secondi per n=4e ~ 7 minuti per n=5).
L'algoritmo è piuttosto semplice: per ogni possibile piatto di dimensioni n(generato con glob"{@F}"x$_- l' globoperatore è piuttosto magico), $r*=eval s/./+$&/gr for/\d+/g;calcola il prodotto di ogni blocco di cifre e $r+=64-ord for/\pl/gsottrargli il peso delle lettere. Quindi, incrementiamo il contatore $\se $ris 0( !$r) e se la piastra contiene numeri e lettere ( /\pl/*/\d/). $\viene stampato implicitamente alla fine grazie alla -pbandiera.

Si noti che i numeri ottengo sono n=2 -> 18, n=3 -> 355, n=4 -> 8012, n=5 -> 218153. Sono abbastanza sicuro che siano quelli giusti, ma potrei sbagliarmi, nel qual caso fammi sapere e cancellerò questa risposta.


3

APL (Dyalog) , 71 byte

Corpo del programma completo. I prompt per N. N≥4 richiedono enormi quantità di memoria e calcolo.

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

Provalo online!


2

Scala, 265 byte

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

Spiegazioni:

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

Gli appunti :

  • -64e -48sono utilizzati per trasformare un Char(rispettivamente lettera e cifra) al suo Intvalore ( 'A' - 64 = 1, 'B' - 64 = 2, ..., '9' - 48 = 9)
  • Il filtro in l.split("[A-Z]").filter(""<)viene utilizzato per eliminare i ""valori se linizia con una lettera (esempio:) "A1".split("[A-Z]") => Array("", 1). Potrebbe esserci una soluzione migliore e più breve

Casi test :

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

Risultati:

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

La funzione è piuttosto lenta n > 4poiché tutte le combinazioni devono essere generate.


2

Java, 382 365 byte

  • Salvato 17 byte, grazie a Kevin Cruijssen

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

dettagliata

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

Penso che tu abbia bisogno di una funzione che accetta solo ncome input.
Christian Sievers,

@ChristianSievers fixed
Khaled.K

1
Alcune cose da golf per il tuo codice attuale: 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 byte ) Puoi confrontare la tua versione attuale con questa per vedere le modifiche che ho fatto (troppo per adattarsi al resto di questo commento). :)
Kevin Cruijssen,

@KevinCruijssen grazie, 17 byte di sconto ora
Khaled.K

2

GAP , 416 byte

Non vincerà sulla dimensione del codice, e lungi dall'essere un tempo costante, ma usa la matematica per accelerare molto!

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;

Per eliminare lo spazio bianco non necessario e ottenere una riga con 416 byte, passare attraverso questo:

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

Il mio vecchio laptop "progettato per Windows XP" può calcolare f(10)in meno di un minuto e andare molto più avanti in meno di un'ora:

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

Come funziona

Supponiamo di voler prima conoscere solo il numero di targhe perfette che si adattano al modello LDDLLDL, dove Lindica una lettera e Dindica una cifra. Supponiamo di avere un elenco ldi numeri tale l[i]da fornire il numero di modi in cui le lettere possono dare il valore ie un elenco simile dper i valori che otteniamo dalle cifre. Quindi il numero di targhe perfette con un valore comune iè giusto l[i]*d[i]e otteniamo il numero di tutte le targhe perfette con il nostro modello sommando questo su tutto i. Indichiamo l'operazione per ottenere questa somma l@d.

Ora, anche se il modo migliore per ottenere questi elenchi fosse provare tutte le combinazioni e contare, possiamo farlo in modo indipendente per le lettere e le cifre, osservando i 26^4+10^3casi anziché i 26^4*10^3 casi quando passiamo attraverso tutte le piastre che si adattano al modello. Ma possiamo fare molto meglio: lè solo l'elenco dei coefficienti di (x+x^2+...+x^26)^kdove kè il numero di lettere, qui 4.

Allo stesso modo, otteniamo il numero di modi per ottenere una somma di cifre in una serie di kcifre come coefficienti di (1+x+...+x^9)^k. Se v'è più di una corsa di cifre, è necessario combinare gli elenchi corrispondenti con un'operazione d1#d2che nella posizione iha come valore la somma di tutti d1[i1]*d2[i2]dove i1*i2=i. Questa è la convoluzione di Dirichlet, che è solo il prodotto se interpretiamo le liste come coefficienti delle serie di Dirchlet. Ma li abbiamo già usati come polinomi (serie di potenze finite) e non esiste un buon modo per interpretare l'operazione per loro. Penso che questa mancata corrispondenza faccia parte di ciò che rende difficile trovare una formula semplice. Usiamolo comunque sui polinomi e usiamo la stessa notazione #. È facile calcolare quando un operando è un monomio: abbiamop(x) # x^k = p(x^k). Insieme al fatto che è bilineare, questo fornisce un modo piacevole (ma non molto efficiente) per calcolarlo.

Nota che le klettere danno un valore al massimo 26k, mentre le k singole cifre possono dare un valore di 9^k. Quindi spesso otterremo alti poteri non necessari nel dpolinomio. Per sbarazzarci di loro, possiamo calcolare il modulo x^(maxlettervalue+1). Questo dà una grande velocità e, anche se non l'ho notato immediatamente, aiuta anche a giocare a golf, perché ora sappiamo che il grado di dnon è maggiore di quello dil , il che semplifica il limite superiore in finale Sum. Otteniamo un'accelerazione ancora migliore facendo un modcalcolo nel primo argomento di Value (vedi commenti) e facendo l'intero #calcolo a un livello inferiore si ottiene un'incredibile velocità. Ma stiamo ancora cercando di essere una risposta legittima a un problema di golf.

Quindi abbiamo il nostro le dpossiamo usarli per calcolare il numero di targhe perfette con motivoLDDLLDL . Questo è lo stesso numero del modello LDLLDDL. In generale, possiamo cambiare l'ordine delle serie di cifre di diversa lunghezza a nostro piacimento, NrArrangementsdando il numero di possibilità. E mentre ci deve essere una lettera tra le serie di cifre, le altre lettere non sono fisse. Il Binomialconta queste possibilità.

Ora resta da percorrere tutti i modi possibili per avere lunghezze di cifre di esecuzione. rcorre attraverso tutti i numeri di corse, cattraverso tutti i numeri totali di cifre ep attraverso tutte le partizioni di ccon rsomme.

Il numero totale di partizioni che osserviamo è due in meno rispetto al numero di partizioni di n+1 , e le funzioni di partizione crescono come exp(sqrt(n)). Quindi, mentre ci sono ancora modi semplici per migliorare il tempo di esecuzione riutilizzando i risultati (scorrendo le partizioni in un ordine diverso), per un miglioramento fondamentale dobbiamo evitare di guardare ciascuna partizione separatamente.

Calcolo veloce

Si noti che (p+q)@r = p@r + q@r. Da solo, questo aiuta solo ad evitare alcune moltiplicazioni. Ma insieme ad (p+q)#r = p#r + q#resso significa che possiamo combinare con semplici polinomi di aggiunta corrispondenti a diverse partizioni. Non possiamo semplicemente aggiungerli tutti, perché dobbiamo ancora sapere con chi ldobbiamo@ combinare, quale fattore dobbiamo usare e quali #estensioni sono ancora possibili.

Combiniamo tutti i polinomi corrispondenti alle partizioni con la stessa somma e lunghezza e già spieghiamo i molteplici modi di distribuire le lunghezze delle serie di cifre. Diversamente da quanto ho ipotizzato nei commenti, non ho bisogno di preoccuparmi del valore usato più piccolo o della frequenza con cui viene utilizzato, se mi assicuro di non estenderlo con quel valore.

Ecco il mio codice 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" ;
}

Questo utilizza la libreria GNU MP. Su debian, installa libgmp-dev. Compila con g++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx. Il programma prende le sue argomentazioni da stdin. Per i tempi, utilizzare echo 100 | time ./pl.

Alla fine a[sum][length][i]indica il numero di modi in cui le sum cifre nelle lengthcorse possono indicare il numero i. Durante il calcolo, all'inizio del mciclo, fornisce il numero di modi che è possibile eseguire con numeri maggiori di m. Tutto inizia con a[0][0][1]=1. Si noti che questo è un superset dei numeri di cui abbiamo bisogno per calcolare la funzione per valori più piccoli. Quindi quasi allo stesso tempo, potremmo calcolare tutti i valori fino a n.

Non c'è ricorsione, quindi abbiamo un numero fisso di loop nidificati. (Il livello di annidamento più profondo è 6.) Ogni ciclo passa attraverso un numero di valori che è lineare nel ncaso peggiore. Quindi abbiamo bisogno solo del tempo polinomiale. Se osserviamo più da vicino i nidificati ie gli janelli extend, troviamo un limite superiore per jil modulo N/i. Ciò dovrebbe solo dare un fattore logaritmico per il jciclo. Il loop più interno in f (con sumnetc) è simile. Tieni anche presente che calcoliamo con numeri che crescono rapidamente.

Si noti inoltre che memorizziamo O(n^3)questi numeri.

Sperimentalmente, ottengo questi risultati su hardware ragionevole (i5-4590S): ha f(50)bisogno di un secondo e 23 MB, f(100)richiede 21 secondi e 166 MB, ha f(200)bisogno di 10 minuti e 1,5 GB e f(300)richiede un'ora e 5,6 GB. Ciò suggerisce una complessità temporale migliore di O(n^5).


Poiché si tratta di una sfida del codice golf, questa risposta deve essere giocata a golf. Scusate.
Rɪᴋᴇʀ

1
@Riker Anche se non credo che il mio codice fosse troppo prolisso all'inizio, ho giocato ancora un po 'a golf e ho preso l'onere di determinarne le dimensioni quando gli spazi bianchi sono spremuti.
Christian Sievers,

1
@carusocomputing Temo che sia molto peggio. Sto gestendo separatamente ogni caso di distribuzione di cifre tra serie di cifre, come se ci fosse una sequenza di tre cifre, oppure ci sia una sequenza di due cifre e una singola cifra, oppure ci sono tre cifre singole, ma per n=5, non c'è caso con una sequenza di due cifre e due cifre singole, perché quindi non abbiamo abbastanza lettere per separare i numeri. Questo è ciò che fanno i tre forloop esterni : scorrere tutte le utili partizioni di numeri <n. (E ho appena realizzato che consento anche le ncifre. Per pura fortuna, un'altra ottimizzazione conta questo come 0).
Christian Sievers,

1
@carusocomputing Nota che per i numeri <n/2, tutte le partizioni sono utili. E i calcoli rimanenti richiedono ancora un tempo non costante. Per vedere cosa sta succedendo, potresti aggiungere Print(p,"\n");all'inizio del corpo del for p...loop. - Mi è venuta l'idea di usare un loop in meno, ma aiuterà solo la dimensione del codice.
Christian Sievers,

2
Ottengo una velocità incredibile spostando mod(il che ha già aiutato molto) Value, cambiandolo in Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1)). Questo da solo consente di calcolare f(15)in 80 secondi.
Christian Sievers,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.