Perfect License Plates
Começando há alguns anos, fiz um pequeno jogo enquanto dirigia: verificando se as placas próximas são "perfeitas". É relativamente raro, mas emocionante quando você encontra um.
Para verificar se uma placa é perfeita:
- Resuma os caracteres, com A = 1, B = 2, ... Z = 26.
- Pegue cada pedaço consecutivo de numerais e some-os; multiplique cada uma dessas somas.
Se os valores da parte 1 e da parte 2 forem iguais, parabéns! Você encontrou uma placa perfeita!
Exemplos
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!
O desafio
Eu usei placas de comprimento 5 e 6 como exemplos, mas esse procedimento é válido para qualquer comprimento de placa. Seu desafio é, para um determinado comprimento N, retornar o número de placas perfeitas desse comprimento. Para os propósitos do desafio, uma placa válida é qualquer combinação de dígitos de 0 a 9 e caracteres de AZ. A placa deve conter um caractere e um numeral para ser considerado potencialmente perfeito. Para fins de verificação, aqui estão os valores que obtive (embora eu não possa ter 100% de exatidão, hahaha)
N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012
Notas
Se, de alguma forma, isso simplificar o problema no seu idioma, você poderá gerar a proporção de placas perfeitas para um determinado N, com pelo menos 2 dígitos significativos.
N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...
OU, você pode emitir o valor equivalente mod 256
N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76
Vitórias mais curtas!
N
.Respostas:
Python 3.6, 150 bytes
resultados:
Versão ungolfed com explicação:
O problema se resume a procurar uma árvore na qual cada nível da árvore corresponde a uma posição no número de uma placa e cada nó tem 36 filhos (10 dígitos e 26 letras). A função faz uma pesquisa recursiva da árvore, acumulando os valores dos dígitos e letras à medida que avançam.
Golf incluído, convertendo os loops for em somas de geradores:
Em seguida, combinando os geradores. Codifique as letras A a Z como -1 a -26 e os dígitos como 0 a 9. Portanto, a soma se torna:
onde args é:
O restante do golfe está convertendo a função em um lambda, encurtando nomes de variáveis e simplificando expressões.
fonte
n*n*log(n)
ou algo parecido?Dyalog APL,
5756 bytes(assume
⎕io←0
)a
matriz de todas as matrículas válidas (exceto00...0
) codificadas com: 0-9 para dígitos, 10-35 para letrasb
máscara de bits para onde ocorrem os dígitosc
máscara de bits para o último dígito em todos os grupos de dígitos consecutivosfonte
Python 2,
359295 bytesBastante longo; Esta é a solução trivial. Estou confiante de que isso está correto, embora não corresponda aos casos de teste do desafio. As soluções que estou obtendo correspondem às respostas de Dada.
-64 bytes graças a sugestões de @numbermaniac
fonte
for
; entremap(ord,x)
eif
; e na última linha, entre.join(x)
efor
. Eu acho que você também pode economizar ainda mais se redefinir as funções para lambdas.Python 2 ,
291287276273 bytesExperimente online!
Resultados:
fonte
Perl 5 , 117 bytes
116 bytes de código +
-p
sinalizador.Experimente online!
Parece bastante abaixo do ideal, mas estou sem ideias no momento.
O código em si é muito ineficiente, pois calcula toda permutação de
a..z,0..9
comprimenton
(leva aproximadamente 1 segundo porn=3
, ~ 15 segundosn=4
e ~ 7 minutosn=5
).O algoritmo é bastante direto: para cada placa de tamanho possível
n
(gerada comglob"{@F}"x$_
- oglob
operador é bastante mágico),$r*=eval s/./+$&/gr for/\d+/g;
calcula o produto de cada parte dos dígitos e$r+=64-ord for/\pl/g
subtrai a ela o peso das letras. Em seguida, incrementamos o contador$\
se$r
is0
(!$r
) e se a placa contém números e letras (/\pl/*/\d/
).$\
é implicitamente impresso no final graças à-p
bandeira.Note que os números que eu obter são
n=2 -> 18
,n=3 -> 355
,n=4 -> 8012
,n=5 -> 218153
. Tenho certeza de que esses são os corretos, mas posso estar enganado; nesse caso, avise-me e excluirei esta resposta.fonte
APL (Dyalog) , 71 bytes
Corpo do programa completo. As solicitações de N. N≥4 requerem grandes quantidades de memória e computação.
Experimente online!
fonte
Scala, 265 bytes
Explicações:
Notas :
-64
e-48
são usados para transformar umChar
(respectivamente letra e dígitos) ao seuInt
valor ('A' - 64 = 1
,'B' - 64 = 2
...,'9' - 48 = 9
)l.split("[A-Z]").filter(""<)
é usado para eliminar""
valores sel
começar com uma letra (exemplo"A1".split("[A-Z]") => Array("", 1)
:). Pode haver uma solução melhor e mais curtaCasos de teste :
Resultados :
A função é bastante lenta
n > 4
porque todas as combinações devem ser geradas.fonte
Java,
382365 bytesGolfe
Detalhado
fonte
n
como entrada.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 ) Você pode comparar sua versão atual com esta para ver as alterações que fiz (muito para caber no restante deste comentário). :)GAP , 416 bytes
Não vencerá no tamanho do código e está longe do tempo constante, mas usa a matemática para acelerar muito!
Para espremer o espaço em branco desnecessário e obter uma linha com 416 bytes, passe por isso:
Meu antigo laptop "projetado para Windows XP" pode calcular
f(10)
em menos de um minuto e ir além em menos de uma hora:Como funciona
Suponha que primeiro queremos saber apenas o número de placas perfeitas que se encaixam no padrão
LDDLLDL
, ondeL
indica uma letra eD
indica um dígito. Suponha que temos uma listal
de números quel[i]
fornece o número de maneiras pelas quais as letras podem fornecer o valori
e uma lista semelhanted
para os valores que obtemos dos dígitos. Então, o número de placas perfeitas com valor comumi
é justol[i]*d[i]
, e obtemos o número de todas as placas perfeitas com nosso padrão, somando isso em gerali
. Vamos denotar a operação de obter essa soma porl@d
.Agora, mesmo que a melhor maneira de obter essas listas seja tentar todas as combinações e contar, podemos fazer isso de forma independente para as letras e os dígitos, observando
26^4+10^3
casos em vez de26^4*10^3
casos quando apenas percorremos todas as placas que se encaixam no padrão. Mas podemos fazer muito melhor: aquil
está apenas a lista de coeficientes de(x+x^2+...+x^26)^k
ondek
está o número de letras4
.Da mesma forma, obtemos o número de maneiras de obter uma soma de dígitos em uma sequência de
k
dígitos como coeficientes de(1+x+...+x^9)^k
. Se houver mais de uma sequência de dígitos, precisamos combinar as listas correspondentes com uma operaçãod1#d2
que na posiçãoi
tenha como valor a soma de tudo emd1[i1]*d2[i2]
quei1*i2=i
. Essa é a convolução de Dirichlet, que é apenas o produto se interpretarmos as listas como coeficientes da série Dirchlet. Mas nós já os usamos como polinômios (séries finitas de potência) e não há uma boa maneira de interpretar a operação para eles. Eu acho que essa incompatibilidade faz parte do que torna difícil encontrar uma fórmula simples. Vamos usá-lo em polinômios de qualquer maneira e usar a mesma notação#
. É fácil calcular quando um operando é um monômio: temosp(x) # x^k = p(x^k)
. Juntamente com o fato de ser bilinear, isso fornece uma maneira agradável (mas não muito eficiente) de computá-lo.Observe que as
k
letras dão um valor de no máximo26k
, enquantok
os dígitos únicos podem dar um valor de9^k
. Portanto, frequentemente obteremos altas potências desnecessárias nod
polinômio. Para se livrar deles, podemos calcular o módulox^(maxlettervalue+1)
. Isso dá uma grande velocidade e, embora eu não tenha notado imediatamente, até ajuda no golfe, porque agora sabemos que o grau ded
não é maior que o del
, o que simplifica o limite superior na finalSum
. Nós obtemos uma velocidade ainda melhor ao fazer ummod
cálculo no primeiro argumento deValue
(ver comentários), e fazer todo o#
cálculo em um nível mais baixo fornece uma velocidade incrível. Mas ainda estamos tentando ser uma resposta legítima para um problema no golfe.Portanto, temos o nosso
l
ed
podemos usá-lo para calcular o número de placas perfeitas com padrãoLDDLLDL
. Esse é o mesmo número do padrãoLDLLDDL
. Em geral, podemos alterar a ordem das séries de dígitos de diferentes comprimentos, conforme desejamos,NrArrangements
fornece o número de possibilidades. E enquanto deve haver uma letra entre as execuções de dígitos, as outras letras não são fixas. OBinomial
conta essas possibilidades.Agora, resta analisar todas as formas possíveis de ter comprimentos de dígitos de execuções.
r
atravessa todos os números de pistas,c
através de todos os números totais de dígitos, ep
através de todas as partições dec
comr
summands.O número total de partições que observamos é dois a menos do que o número de partições
n+1
, e as funções da partição aumentam comoexp(sqrt(n))
. Portanto, embora ainda haja maneiras fáceis de melhorar o tempo de execução reutilizando resultados (executando as partições em uma ordem diferente), para uma melhoria fundamental, precisamos evitar olhar cada partição separadamente.Computando rápido
Note isso
(p+q)@r = p@r + q@r
. Por si só, isso apenas ajuda a evitar algumas multiplicações. Mas junto com(p+q)#r = p#r + q#r
isso significa que podemos combinar por polinômios de adição simples correspondentes a diferentes partições. Não podemos simplesmente adicioná-los todos, porque ainda precisamos saber com os quaisl
devemos@
combinar, qual fator devemos usar e quais#
extensões ainda são possíveis.Vamos combinar todos os polinômios correspondentes às partições com a mesma soma e comprimento, e já consideramos as várias maneiras de distribuir os comprimentos das execuções de dígitos. Diferente do que especulei nos comentários, não preciso me preocupar com o menor valor usado ou com que frequência ele é usado, se tiver certeza de que não estenderei esse valor.
Aqui está o meu código C ++:
Isso usa a biblioteca GNU MP. No debian, instale
libgmp-dev
. Compile comg++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx
. O programa leva seu argumento de stdin. Para cronometrar, useecho 100 | time ./pl
.No final,
a[sum][length][i]
fornece o número de maneiras pelas quais ossum
dígitos naslength
execuções podem fornecer o númeroi
. Durante a computação, no início dom
loop, fornece o número de maneiras que podem ser feitas com números maiores quem
. Tudo começa coma[0][0][1]=1
. Observe que este é um superconjunto dos números que precisamos para calcular a função para valores menores. Assim, quase ao mesmo tempo, poderíamos calcular todos os valores atén
.Como não há recursão, temos um número fixo de loops aninhados. (O nível de aninhamento mais profundo é 6.) Cada loop passa por vários valores lineares no
n
pior dos casos. Então, precisamos apenas de tempo polinomial. Se observarmos mais de perto o aninhadoi
e oj
loopextend
, encontraremos um limite superior paraj
o formulárioN/i
. Isso deve fornecer apenas um fator logarítmico para oj
loop. O loop mais internof
(comsumn
etc) é semelhante. Lembre-se também de que computamos com números que crescem rapidamente.Observe também que armazenamos
O(n^3)
esses números.Experimentalmente, obtenho esses resultados em hardware razoável (i5-4590S):
f(50)
precisa de um segundo e 23 MB,f(100)
precisa de 21 segundos e 166 MB,f(200)
precisa de 10 minutos e 1,5 GB ef(300)
precisa de uma hora e 5,6 GB. Isso sugere uma complexidade de tempo melhor queO(n^5)
.fonte
n=5
, para , não há maiúsculas e minúsculas com uma sequência de dois dígitos e dois dígitos únicos, porque não temos letras suficientes para separar os números. É isso que os trêsfor
loops externos fazem: percorrem todas as partições úteis de números<n
. (E acabei de perceber que também permiton
dígitos. Por pura sorte, outra otimização conta isso como 0).<n/2
, todas as partições são úteis. E os cálculos restantes ainda levam um tempo não constante. Para ver o que está acontecendo, você pode adicionarPrint(p,"\n");
no início do corpo dofor p...
loop. - Tive uma idéia para usar um loop a menos, mas isso ajudará apenas no tamanho do código.mod
(que já ajudou bastante)Value
, mudando paraValue(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1))
. Só isso permite calcularf(15)
em 80 segundos.Pitão, 55 bytes
fonte