Plaques d'immatriculation parfaites
Depuis quelques années, je me suis fait un petit jeu en conduisant: vérifier si les plaques d'immatriculation à proximité sont "parfaites". C'est relativement rare, mais excitant quand vous en trouvez un.
Pour vérifier si une plaque d'immatriculation est parfaite:
- Résumer les caractères, avec A = 1, B = 2, ... Z = 26.
- Prenez chaque morceau consécutif de chiffres et additionnez-les; multipliez chacune de ces sommes ensemble.
Si les valeurs des parties 1 et 2 sont égales, félicitations! Vous avez trouvé une plaque d'immatriculation parfaite!
Exemples
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!
Le défi
J'ai utilisé des plaques d'immatriculation de longueur 5 et 6 à titre d'exemple, mais cette procédure est valable pour toute longueur de plaque. Votre défi est, pour une longueur donnée N, de retourner le nombre de plaques d'immatriculation parfaites de cette longueur. Pour les besoins du défi, une plaque d'immatriculation valide est une combinaison des chiffres 0 à 9 et des caractères AZ. La plaque doit contenir à la fois un caractère et un chiffre pour être considérée comme potentiellement parfaite. À des fins de vérification, voici les valeurs que j'ai obtenues (bien que je ne puisse pas être à 100% sur leur exactitude, hahaha)
N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012
Remarques
Si, dans votre langue, le problème est simplifié, vous pouvez générer la proportion de plaques d'immatriculation parfaites pour un N donné, avec au moins deux chiffres significatifs.
N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...
OU vous pouvez sortir la valeur équivalente mod 256
N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76
Le plus court gagne!
N
.Réponses:
Python 3.6, 150 octets
résultats:
Version non-golfée avec explication:
Le problème revient à rechercher un arbre dans lequel chaque niveau de l’arbre correspond à une position dans un numéro de plaque d’immatriculation et chaque nœud a 36 enfants (10 chiffres et 26 lettres). La fonction effectue une recherche récursive de l’arbre, en accumulant les valeurs des chiffres et des lettres au fur et à mesure.
Golf inclus, convertissant les boucles for en sommes de générateurs:
Puis en combinant les générateurs. Encodez les lettres, de A à Z, de -1 à -26 et les chiffres de 0 à 9. La somme devient:
où args est:
Le reste du jeu consiste à convertir la fonction en lambda, à raccourcir les noms de variables et à simplifier les expressions.
la source
n*n*log(n)
ou quelque chose de similaire?Dyalog APL,
5756 octets(suppose
⎕io←0
)a
matrice de toutes les plaques d'immatriculation valides (sauf00...0
) codée avec: 0-9 pour les chiffres, 10-35 pour les lettresb
masque de bits pour indiquer où se trouvent les chiffresc
masque de bits pour le dernier chiffre de chaque groupe de chiffres consécutifsla source
Python 2,
359295 octetsAssez long; c'est la solution triviale. Je suis convaincu que c'est correct, bien que cela ne corresponde pas aux cas de test du challenge. Les solutions que je trouve correspondent aux réponses de Dada.
-64 octets grâce aux suggestions de @numbermaniac
la source
for
; entremap(ord,x)
etif
; et dans la dernière ligne, entre.join(x)
etfor
. Je pense que vous pouvez également économiser encore plus si vous redéfinissez les fonctions en lambdas.Python 2 ,
291287276273 octetsEssayez-le en ligne!
Résultats:
la source
Perl 5 , 117 octets
116 octets de code +
-p
drapeau.Essayez-le en ligne!
Cela semble assez sous-optimal, mais je suis à court d'idées pour le moment.
Le code lui-même est très inefficace car il calcule chaque permutation de
a..z,0..9
longueurn
(cela prend environ 1 seconde pourn=3
, environ 15 secondes pourn=4
et environ 7 minutes pourn=5
).L'algorithme est assez simple: pour chaque plaque de taille possible
n
(générée avecglob"{@F}"x$_
- l'glob
opérateur est assez magique),$r*=eval s/./+$&/gr for/\d+/g;
calcule le produit de chaque bloc de chiffres et lui$r+=64-ord for/\pl/g
soustrait du poids des lettres. Ensuite, nous incrémentons le compteur$\
si$r
is0
(!$r
) et si la plaque contient des chiffres et des lettres (/\pl/*/\d/
).$\
est implicitement imprimé à la fin grâce à-p
flag.Notez que les chiffres que j'obtiens sont
n=2 -> 18
,n=3 -> 355
,n=4 -> 8012
,n=5 -> 218153
. Je suis à peu près sûr que ce sont les bons, mais je peux me tromper, dans ce cas, faites-le moi savoir et je supprimerai cette réponse.la source
APL (Dyalog) , 71 octets
Corps du programme complet. Les invites pour N. N≥4 nécessitent d'énormes quantités de mémoire et de calculs.
Essayez-le en ligne!
la source
Scala, 265 octets
Explications:
Remarques :
-64
et-48
sont utilisés pour transformer unChar
son (respectivement lettre et chiffres)Int
valeur ('A' - 64 = 1
,'B' - 64 = 2
, ...,'9' - 48 = 9
)l.split("[A-Z]").filter(""<)
est utilisé pour éliminer les""
valeurs sil
commence par une lettre (exemple:)"A1".split("[A-Z]") => Array("", 1)
. Il pourrait y avoir une solution meilleure et plus courteCas de test:
Résultats :
La fonction est assez lente
n > 4
car toutes les combinaisons doivent être générées.la source
Java,
382365 octetsGolfé
Détaillé
la source
n
qu'en entrée.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 octets ) Vous pouvez comparer votre version actuelle à celle-ci pour voir les modifications que j'ai apportées (trop pour tenir dans le reste de ce commentaire). :)GAP , 416 octets
Ne gagnera pas sur la taille du code, et loin du temps constant, mais utilise les mathématiques pour accélérer beaucoup!
Pour réduire les espaces inutiles et obtenir une ligne de 416 octets, dirigez-le:
Mon ancien ordinateur portable "conçu pour Windows XP" peut calculer
f(10)
en moins d'une minute et aller beaucoup plus loin en moins d'une heure:Comment ça marche
Supposons que nous ne voulions d'abord connaître que le nombre de plaques d'immatriculation parfaites correspondant au motif
LDDLLDL
, où correspond àL
une lettre et àD
un chiffre. Supposons que nous ayons une listel
de nombresl[i]
donnant le nombre de façons dont les lettres peuvent donner la valeuri
et une liste similaired
pour les valeurs que nous obtenons des chiffres. Ensuite, le nombre de plaques d'immatriculation parfaites ayant une valeur communei
est justel[i]*d[i]
, et nous obtenons le nombre de toutes les plaques d'immatriculation parfaites avec notre modèle en additionnant l'ensemblei
. Notons l'opération consistant à obtenir cette somme parl@d
.Maintenant, même si le meilleur moyen d’obtenir ces listes était d’essayer toutes les combinaisons et de compter, nous pouvons le faire indépendamment pour les lettres et les chiffres, en regardant les
26^4+10^3
cas au lieu des26^4*10^3
cas où nous passons simplement en revue toutes les plaques correspondant au modèle. Mais nous pouvons faire beaucoup mieux:l
est juste la liste des coefficients de(x+x^2+...+x^26)^k
oùk
est le nombre de lettres, ici4
.De même, nous obtenons le nombre de façons d'obtenir une somme de chiffres dans une suite de
k
chiffres en tant que coefficients de(1+x+...+x^9)^k
. S'il y a plus d'une suite de chiffres, nous devons combiner les listes correspondantes avec une opérationd1#d2
dont positioni
a la valeur la somme de tousd1[i1]*d2[i2]
oùi1*i2=i
. Il s'agit de la convolution de Dirichlet, qui est simplement le produit si nous interprétons les listes comme des coefficients de la série de Dirchlet. Mais nous les avons déjà utilisés en tant que polynômes (séries de puissance finie) et il n’existe aucun moyen d’interpréter l’opération pour eux. Je pense que cette inadéquation fait partie de ce qui rend difficile de trouver une formule simple. Utilisons-le quand même sur les polynômes et utilisons la même notation#
. Il est facile de calculer quand un opérande est un monôme: on ap(x) # x^k = p(x^k)
. Avec le fait qu'il soit bilinéaire, cela donne un moyen agréable (mais pas très efficace) de le calculer.Notez que les
k
lettres donnent une valeur d'au plus26k
, alors quek
les chiffres simples peuvent donner une valeur de9^k
. Nous aurons donc souvent des pouvoirs élevés inutiles dans led
polynôme. Pour nous en débarrasser, nous pouvons calculer modulox^(maxlettervalue+1)
. Cela donne une grande vitesse et, bien que je ne l'aie pas remarqué tout de suite, aide même au golf, car nous savons maintenant que le degré ded
n'est pas plus grand que celui del
, ce qui simplifie la limite supérieure en finaleSum
. Nous obtenons une vitesse encore meilleure en faisant unmod
calcul dans le premier argument deValue
(voir les commentaires), et en effectuant tout le#
calcul à un niveau inférieur, cela donne une vitesse incroyable. Mais nous essayons toujours d'être une réponse légitime à un problème de golf.Nous avons donc nos
l
et nousd
pouvons les utiliser pour calculer le nombre de plaques d'immatriculation parfaites avec un motifLDDLLDL
. C'est le même nombre que pour le motifLDLLDDL
. En général, nous pouvons modifier l'ordre des séquences de chiffres de longueur différente selonNrArrangements
le nombre de possibilités. Et s'il doit y avoir une lettre entre les séries de chiffres, les autres lettres ne sont pas fixes. LeBinomial
compte ces possibilités.Il reste maintenant à parcourir tous les moyens possibles pour avoir des longueurs de chiffres de courses.
r
passe à travers tous les numéros de pistes, àc
travers tous le nombre total de chiffres, et àp
travers toutes les partitions dec
avecr
summands.Le nombre total de partitions que nous examinons est deux de moins que le nombre de partitions
n+1
, et les fonctions de partition se développent comme suitexp(sqrt(n))
. Il est donc toujours facile d'améliorer le temps d'exécution en réutilisant les résultats (en parcourant les partitions dans un ordre différent), mais pour une amélioration fondamentale, nous devons éviter de regarder chaque partition séparément.Le calcul rapide
Notez que
(p+q)@r = p@r + q@r
. En soi, cela permet d'éviter certaines multiplications. Mais(p+q)#r = p#r + q#r
cela signifie que nous pouvons combiner par simple addition des polynômes correspondant à différentes partitions. Nous ne pouvons pas simplement les ajouter tous, car nous avons encore besoin de savoir avec lequell
nous devons@
combiner, quel facteur nous devons utiliser et quelles#
extensions sont encore possibles.Associons tous les polynômes correspondant aux partitions ayant la même somme et la même longueur, et prenons déjà en compte les multiples façons de répartir les longueurs des séries de chiffres. Contrairement à ce que j'ai spéculé dans les commentaires, je n'ai pas besoin de me soucier de la plus petite valeur utilisée ou de la fréquence d'utilisation, si je m'assure que je ne vais pas prolonger cette valeur.
Voici mon code C ++:
Ceci utilise la bibliothèque GNU MP. Sur Debian, installez
libgmp-dev
. Compiler avecg++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx
. Le programme tire son argument de stdin. Pour le timing, utilisezecho 100 | time ./pl
.À la fin,
a[sum][length][i]
donne le nombre de façons dont lessum
chiffres dans leslength
exécutions peuvent donner le nombrei
. Lors du calcul, au début de lam
boucle, il indique le nombre de façons de procéder avec des nombres supérieurs àm
. Tout commence para[0][0][1]=1
. Notez que ceci est un sur-ensemble des nombres dont nous avons besoin pour calculer la fonction pour des valeurs plus petites. Donc, presque au même moment, nous pourrions calculer toutes les valeurs jusqu’àn
.Il n'y a pas de récursivité, nous avons donc un nombre fixe de boucles imbriquées. (Le niveau d'imbrication le plus profond est 6.) Chaque boucle passe par un nombre de valeurs qui est linéaire
n
dans le pire des cas. Nous avons donc besoin que de temps polynomial. Si nous regardons de plus près l'imbricationi
et laj
boucleextend
, nous trouvons une limite supérieure pourj
la formeN/i
. Cela ne devrait donner qu'un facteur logarithmique pour laj
boucle. La boucle la plus internef
(avecsumn
etc.) est similaire. Gardez également à l'esprit que nous calculons avec des nombres qui croissent rapidement.Notez également que nous stockons
O(n^3)
ces nombres.Expérimentalement, j'obtiens ces résultats sur un matériel raisonnable (i5-4590S):
f(50)
nécessite une seconde et 23 Mo,f(100)
21 secondes et 166 Mo,f(200)
10 minutes et 1,5 Go, etf(300)
une heure et 5,6 Go. Cela suggère une complexité temporelle meilleure queO(n^5)
.la source
n=5
, il n'y a pas cas avec une suite de deux chiffres et deux chiffres simples, car alors nous n'avons pas assez de lettres pour séparer les chiffres. C’est ce que font les troisfor
boucles externes : parcourir toutes les partitions utiles de nombres<n
. (Et je viens de me rendre compte que j'autorise aussi lesn
chiffres. Par pure chance, une autre optimisation compte pour 0).<n/2
, toutes les partitions sont utiles. Et les calculs restants prennent toujours leur temps non constant. Pour voir ce qui se passe, vous pouvez ajouterPrint(p,"\n");
au début du corps de lafor p...
boucle. - J'ai eu l'idée d'utiliser une boucle de moins, mais cela ne fera qu'aider la taille du code.mod
(qui a déjà beaucoup aidé) dansValue
, en le changeant enValue(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1))
. Cela seul permet de calculerf(15)
en 80 secondes.Pyth, 55 octets
la source