Placas de licencia perfectas

33

Placas de licencia perfectas

Comenzando hace unos años, hice un pequeño juego mientras conducía: comprobar si las placas cercanas son "perfectas". Es relativamente raro, pero emocionante cuando encuentras uno.

Para verificar si una placa es perfecta:

  1. Resume los caracteres, con A = 1, B = 2, ... Z = 26.
  2. Tome cada trozo consecutivo de números y sume; multiplique cada una de estas sumas juntas.

Si los valores en la parte 1 y la parte 2 son iguales, ¡felicidades! ¡Has encontrado una matrícula perfecta!

Ejemplos

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!

El reto

Usé placas de longitud 5 y 6 como ejemplos, pero este procedimiento es válido para cualquier longitud de placa. Su desafío es, para una longitud N dada, devolver el número de placas perfectas de esa longitud. Para los propósitos del desafío, una placa válida es cualquier combinación de dígitos 0-9 y caracteres AZ. La placa debe contener tanto un carácter como un número para que se considere potencialmente perfecta. Para fines de verificación, aquí están los valores que obtuve (aunque no puedo estar al 100% sobre su corrección, jajaja)

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

Notas

Si de alguna manera el problema se simplifica en su idioma, puede generar la proporción de matrículas perfectas para un N dado, por lo menos a 2 dígitos significativos.

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

O, puede generar el valor equivalente mod 256

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

¡Las victorias más cortas!

Comenzara
fuente
2
Bienvenido al sitio! Creo que este es un buen desafío, pero los resultados adicionales permitidos hacen que sea difícil calificar las respuestas. PPCG busca criterios objetivos ganadores, y es difícil hacerlo cuando hay tantas libertades; esto no solo cambia el formato de salida, sino que también cambia lo que se le permite emitir. Recomendaría editar las otras opciones y solo hacer que sea necesario generar el número de placas perfectas para N.
HyperNeutrino
11
Personalmente, disfrutaría mucho más este desafío si simplemente validara si una placa dada es perfecta o no (especialmente porque no tienes números definitivos para los casos de prueba. Sin embargo, probablemente esté bien, siempre y cuando los resultados potenciales calculados se reducen. No estoy seguro acerca de cómo se sienten otras personas. Sin embargo, ¡es una buena idea!
MildlyMilquetoast
44
Estoy de acuerdo con Mistah Figgins; Siento que de esta manera se trata más de encontrar un patrón, que sigue siendo un desafío interesante, pero creo que podría atraer más respuestas si solo fuera una verificación de validación. ¡Buen desafío sin embargo!
HyperNeutrino
1
He publicado un desafío estrechamente relacionado , con la esperanza de que llame la atención sobre esta maravillosa pregunta, así como de simplificarlo un poco, solo buscando la placa (casi) perfecta.
Sr. Xcoder
1
@carusocomputing Hice mi mejor esfuerzo pero llegué vacío. Se lo envié a mi maestro de matemáticas y él está vacío hasta ahora
Christopher

Respuestas:

9

Python 3.6, 150 bytes

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

resultados:

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

Versión sin golf con explicación:

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)

El problema se reduce a buscar un árbol en el que cada nivel del árbol corresponde a una posición en un número de placa y cada nodo tiene 36 hijos (10 dígitos y 26 letras). La función realiza una búsqueda recursiva del árbol, acumulando los valores de los dígitos y letras a medida que avanza.

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 incluido, convirtiendo los bucles for en sumas de generadores:

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)

Luego combinando los generadores. Codifique las letras, de la A a la Z, como -1 a -26 y los dígitos como 0 a 9. Entonces la suma se convierte en:

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

donde args es:

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

El resto del golf está convirtiendo la función en una lambda, acortando nombres de variables y simplificando expresiones.

RootTwo
fuente
Esta es una solución elocuente, ¿cuál sería el tiempo de ejecución? n*n*log(n)¿o algo similar?
Urna mágica de pulpo
@carusocomputing Gracias. La solución aún genera todas las permutaciones posibles de la longitud dada, por lo que tiene la misma complejidad que las otras soluciones. Algo así como k ** n, donde k es el número de símbolos en el alfabeto (por ejemplo, 10 dígitos + 26 letras = 36) yn es el número de símbolos en una placa de matrícula. Ejecutarlo para n = 5 requiere verificar 36 ^ 5 = 60,466,176 permutaciones y tomó un minuto o dos (la memorización podría acelerarlo, pero costaría muchos bytes ;-)).
RootTwo
6

Dyalog APL, 57 56 bytes

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

(asume ⎕io←0)

amatriz de todas las placas válidas (excepto 00...0) codificadas con: 0-9 para dígitos, 10-35 para letras

b máscara de bits para donde ocurren los dígitos

c máscara de bits para el último dígito en cada grupo de dígitos consecutivos

ngn
fuente
Pruébelo en línea para 1-4 necesita más memoria para 4, ¡pero también hay formas de evitarlo!
Adám
4

Python 2, 359295 bytes

Más bien largo; Esta es la solución trivial. Estoy seguro de que esto es correcto, aunque no coincide con los casos de prueba en el desafío. Las soluciones que estoy obteniendo coinciden con las respuestas de 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 bytes gracias a las sugerencias de @numbermaniac

Hiperneutrino
fuente
1
Puede guardar aproximadamente tres bytes en c (x) y la última línea; eliminar un espacio entre 96 y for; entre map(ord,x)y if; y en la última línea, entre .join(x)y for. Creo que también puede ahorrar aún más si redefine las funciones a lambdas.
numbermaniac
@numbermaniac ¡Gracias! (64 bytes en total)
HyperNeutrino
4

Python 2 , 291 287 276 273 bytes

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

Pruébalo en línea!


Resultados:

0 0
1 0
2 18
3 355
4 8012
ovs
fuente
3

Perl 5 , 117 bytes

116 bytes de código + -pbandera.

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

Pruébalo en línea!

Se siente bastante subóptimo, pero estoy sin ideas en este momento.
El código en sí es muy ineficiente, ya que calcula cada permutación de a..z,0..9longitud n(tarda aproximadamente 1 segundo durante n=3, ~ 15 segundos n=4y ~ 7 minutos n=5).
El algoritmo es bastante sencillo: para cada placa de tamaño posible n(generada con glob"{@F}"x$_- el globoperador es bastante mágico), $r*=eval s/./+$&/gr for/\d+/g;calcula el producto de cada trozo de dígitos y le $r+=64-ord for/\pl/gresta el peso de las letras. Luego, incrementamos el contador $\si el $res 0( !$r) y si la placa contiene números y letras ( /\pl/*/\d/). $\se imprime implícitamente al final gracias a -pflag.

Tenga en cuenta que los números obtengo son n=2 -> 18, n=3 -> 355, n=4 -> 8012, n=5 -> 218153. Estoy bastante seguro de que estos son los correctos, pero podría estar equivocado, en cuyo caso avíseme y eliminaré esta respuesta.

Dada
fuente
3

APL (Dyalog) , 71 bytes

Programa completo del cuerpo. Las solicitudes de N. N≥4 requieren grandes cantidades de memoria y cálculo.

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

Pruébalo en línea!

Adán
fuente
2

Scala, 265 bytes

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

Explicaciones:

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

Notas:

  • -64y -48se utilizan para transformar una Char(respectivamente letra y dígitos) a su Intvalor ( 'A' - 64 = 1, 'B' - 64 = 2, ..., '9' - 48 = 9)
  • El filtro en l.split("[A-Z]").filter(""<)se usa para eliminar ""valores si lcomienza con una letra (ejemplo:) "A1".split("[A-Z]") => Array("", 1). Puede haber una solución mejor y más corta

Casos de prueba :

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

Resultados:

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

La función es bastante lenta n > 4ya que se deben generar todas las combinaciones.

norbjd
fuente
2

Java, 382 365 bytes

  • Guardado 17 bytes, gracias 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);}

Detallado

// 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); }
Khaled.K
fuente
Creo que necesitas una función que solo tome ncomo entrada.
Christian Sievers
@ChristianSievers solucionado
Khaled.K
1
Algunas cosas para jugar golf para su código actual: 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 ) Puede comparar su versión actual con esta para ver los cambios que hice (demasiado para el resto de este comentario). :)
Kevin Cruijssen
@KevinCruijssen thx, 17 bytes de descuento ahora
Khaled.K
2

GAP , 416 bytes

No ganará en tamaño de código, y lejos del tiempo constante, ¡pero usa las matemáticas para acelerar mucho!

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;

Para exprimir el espacio en blanco innecesario y obtener una línea con 416 bytes, canalice esto:

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

Mi vieja computadora portátil "diseñada para Windows XP" puede calcular f(10)en menos de un minuto e ir mucho más lejos en menos de una hora:

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

Cómo funciona

Supongamos que primero solo queremos saber el número de placas perfectas que se ajustan al patrón LDDLLDL, donde Ldenota una letra y Ddenota un dígito. Supongamos que tenemos una lista lde números que l[i]da la cantidad de formas en que las letras pueden dar el valor i, y una lista similar dpara los valores que obtenemos de los dígitos. Entonces, el número de placas perfectas con valor común ies justo l[i]*d[i], y obtenemos el número de todas las placas perfectas con nuestro patrón sumando esto sobre todo i. Denotemos la operación de obtener esta suma l@d.

Ahora, incluso si la mejor manera de obtener estas listas era probar todas las combinaciones y contar, podemos hacer esto independientemente para las letras y los dígitos, mirando los 26^4+10^3casos en lugar de los 26^4*10^3 casos cuando simplemente pasamos por todas las placas que se ajustan al patrón. Pero podemos hacerlo mucho mejor: aquí les solo la lista de coeficientes de (x+x^2+...+x^26)^kdónde kestá el número de letras 4.

Del mismo modo, obtenemos el número de formas de obtener una suma de dígitos en una serie de kdígitos como los coeficientes de (1+x+...+x^9)^k. Si hay más de una serie de dígitos, necesitamos combinar las listas correspondientes con una operación d1#d2que en la posición itenga como valor la suma de todos los d1[i1]*d2[i2]lugares i1*i2=i. Esta es la convolución de Dirichlet, que es solo el producto si interpretamos las listas como coeficientes de las series de Dirchlet. Pero ya los hemos usado como polinomios (series de potencia finita), y no hay una buena manera de interpretar la operación para ellos. Creo que este desajuste es parte de lo que hace que sea difícil encontrar una fórmula simple. Usémoslo en polinomios de todos modos y usemos la misma notación #. Es fácil de calcular cuando un operando es un monomio: tenemosp(x) # x^k = p(x^k). Junto con el hecho de que es bilineal, esto proporciona una forma agradable (pero no muy eficiente) de calcularlo.

Tenga en cuenta que las kletras dan un valor de como máximo 26k, mientras quek dígitos individuales pueden dar un valor de 9^k. Por lo tanto, a menudo obtendremos altos poderes innecesarios en el dpolinomio. Para deshacernos de ellos, podemos calcular el módulo x^(maxlettervalue+1). Esto da una gran velocidad y, aunque no me di cuenta de inmediato, incluso ayuda al golf, porque ahora sabemos que el grado ded no es mayor que el de l, lo que simplifica el límite superior en la final Sum. Obtenemos una velocidad aún mejor al hacer un modcálculo en el primer argumento de Value (ver comentarios), y hacer todo el #cálculo a un nivel inferior da una velocidad increíble. Pero todavía estamos tratando de ser una respuesta legítima a un problema de golf.

Así que tenemos nuestro lyd podemos usarlos para calcular el número de matrículas perfectas con patrón LDDLLDL. Ese es el mismo número que para el patrón LDLLDDL. En general, podemos cambiar el orden de las corridas de dígitos de diferente longitud como queramos, NrArrangementsda el número de posibilidades. Y aunque debe haber una letra entre las series de dígitos, las otras letras no son fijas. El Binomialcuenta estas posibilidades.

Ahora queda por recorrer todas las formas posibles de tener longitudes de dígitos de ejecución. rrecorre todos los números de carreras,c todos los números totales de dígitos y ptodas las particiones de ccon rsumandos.

El número total de particiones que vemos es dos menos que el número de particiones n+1, y las funciones de partición crecen como exp(sqrt(n)). Por lo tanto, aunque todavía hay formas fáciles de mejorar el tiempo de ejecución reutilizando los resultados (ejecutando las particiones en un orden diferente), para una mejora fundamental, debemos evitar mirar cada partición por separado.

Calcularlo rápido

Tenga en cuenta que (p+q)@r = p@r + q@r. Por sí solo, esto solo ayuda a evitar algunas multiplicaciones. Pero junto con (p+q)#r = p#r + q#resto significa que podemos combinar mediante polinomios de suma simple correspondientes a diferentes particiones. No podemos simplemente agregarlos a todos, porque aún necesitamos saber con quél tenemos que @combinar, qué factor tenemos que usar y qué #extensiones aún son posibles.

Combinemos todos los polinomios correspondientes a particiones con la misma suma y longitud, y ya tenga en cuenta las múltiples formas de distribuir las longitudes de las series de dígitos. A diferencia de lo que especulé en los comentarios, no necesito preocuparme por el valor usado más pequeño o con qué frecuencia se usa, si me aseguro de no extenderme con ese valor.

Aquí está mi código 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" ;
}

Esto usa la biblioteca GNU MP. En debian, instale libgmp-dev. Compilar con g++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx. El programa toma su argumento de stdin. Para el tiempo, useecho 100 | time ./pl .

Al final a[sum][length][i]da el número de formas en que los sum dígitos en las lengthcorridas pueden dar el número i. Durante el cálculo, al comienzo del mciclo, proporciona la cantidad de formas que se pueden hacer con números mayores que m. Todo comienza con a[0][0][1]=1. Tenga en cuenta que este es un superconjunto de los números que necesitamos para calcular la función para valores más pequeños. Entonces, casi al mismo tiempo, podríamos calcular todos los valores hasta n.

No hay recursión, por lo que tenemos un número fijo de bucles anidados. (El nivel de anidamiento más profundo es 6.) Cada ciclo pasa por una serie de valores que son lineales nen el peor de los casos. Entonces solo necesitamos tiempo polinomial. Si miramos más de cerca los anidados iy los jbucles extend, encontramos un límite superior para jel formulario N/i. Eso solo debería dar un factor logarítmico para el jbucle. El bucle más interno en f (consumn etc.) es similar. También tenga en cuenta que calculamos con números que crecen rápidamente.

Tenga en cuenta también que almacenamos O(n^3) estos números.

Experimentalmente, obtengo estos resultados en hardware razonable (i5-4590S): f(50)necesita un segundo y 23 MB, f(100)necesita 21 segundos y 166 MB, f(200)necesita 10 minutos y 1.5 GB, y f(300)necesita una hora y 5.6 GB. Esto sugiere una complejidad temporal mejor que O(n^5).

Christian Sievers
fuente
Como este es un desafío de código de golf, esta respuesta debe ser desarrollada. Lo siento.
Rɪᴋᴇʀ
1
@Riker Si bien no creo que mi código fuera demasiado detallado para empezar, jugué un poco más y tomé la carga de determinar su tamaño cuando se elimina el espacio en blanco.
Christian Sievers
1
@carusocomputing Me temo que es mucho peor. Estoy manejando por separado cada caso de distribución de dígitos entre series de dígitos, como si hay una serie de tres dígitos, o si hay una serie de dos dígitos y un solo dígito, o si hay tres dígitos únicos, pero para n=5, no hay caso con una corrida de dos dígitos y dos dígitos simples, porque entonces no tenemos suficientes letras para separar los números. Esto es lo que hacen los tres forbucles externos : ejecutar todas las particiones útiles de números <n. (Y me acabo de dar cuenta de que también permito ndígitos. Por pura suerte, otra optimización cuenta esto como 0).
Christian Sievers
1
@carusocomputing Tenga en cuenta que para los números <n/2, todas las particiones son útiles. Y los cálculos restantes aún toman su tiempo no constante. Para ver qué está sucediendo, puede agregar Print(p,"\n");al comienzo del cuerpo del for p...bucle. - Tengo una idea para usar un bucle menos, pero solo ayudará al tamaño del código.
Christian Sievers
2
Obtengo una velocidad increíble moviendo el mod(que ya ayudó mucho) Value, cambiándolo a Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1)). Eso solo permite calcular f(15)en 80 segundos.
Christian Sievers
0

Pyth, 55 bytes

FNQI<xrG1N0=+ZsN).?=+YZ=Z0;qu*G?HH1Y1sm?<xrG1d00hxrG1dQ
Karan Elangovan
fuente