Le period
d'une chaîne est le décalage non nul le plus court afin que la chaîne corresponde à elle-même, en ignorant toutes les parties qui dépassent. Ainsi, par exemple, abcabcab
a des règles 3
. Par convention, nous disons que s'il n'y a pas un tel décalage, une chaîne a une période égale à sa longueur. Donc la période de abcde
est 5
et la période de a
est 1
.
En termes plus formels, la période d'une chaîne S
est le minimum i > 0
pour que S[1,n-i] == S[i+1,n]
(indexation à partir de 1
).
Pour une chaîne S donnée de puissance de deux longueurs, nous calculerons la période de tous ses préfixes de puissance de deux longueurs. Par exemple, réfléchissez S = abcabcab
. Les périodes que nous calculerons sont:
'a', 1
'ab', 2
'abca', 3
'abcabcab', 3
Nous allons en fait simplement sortir le tableau des périodes, c'est-à-dire [1, 2, 3, 3]
.
Pour une puissance positive donnée de deux n
, considérez toutes les chaînes binaires possibles S
. Rappelons qu'une chaîne binaire est simplement une chaîne de 1
s et 0
s donc il y a exactement de 2^n
telles chaînes (c'est à dire 2
à la puissance n
). Pour chacun, nous pouvons calculer ce tableau de périodes.
Le défi consiste à écrire du code qui prend
n
(une puissance de deux) en entrée et calcule le nombre de tableaux distincts.
Les réponses pour n = 1, 2, 4, 8, 16, 32, 64, 128
sont:
1, 2, 6, 32, 320, 6025, 216854, 15128807
L'ensemble complet de tableaux de périodes distincts pour n = 4
est:
1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4
But
Je vais exécuter votre code sur mon ordinateur exécutant Ubuntu pendant 10 minutes. Votre score est le plus élevé n
pour lequel votre code se termine pendant cette période. Dans le cas d'une égalité, la réponse qui complète le plus grand n
gain conjoint le plus rapide. Dans le cas où il y a égalité en moins d'une seconde sur le chronométrage, la première réponse publiée l'emporte.
Langues et bibliothèques
Vous pouvez utiliser toutes les langues et bibliothèques disponibles que vous aimez. Veuillez inclure une explication complète sur la façon d'exécuter / compiler votre code sous Linux si possible. »
Votre code doit en fait calculer les réponses et non pas, par exemple, simplement afficher des valeurs précalculées.
Entrées principales
- 2 minutes et 21 secondes pour n = 128 en C # par Peter Taylor
- 9 secondes pour n = 32 en rouille par isaacg
n
, l'accepteriez-vous? Il n'est pas bien défini où se situe la frontière entre le codage en dur et l'informatique réelle.abcab
. Tout sauf les 3 dernières lettres estabcab
. Celles-ci correspondent et la suppression d'un plus petit nombre de lettres ne correspond pas.Réponses:
C #, n = 128 dans environ 2:40
L'extension à n = 256 nécessiterait de passer à
BigInteger
pour les masques, ce qui tue probablement trop les performances pour que n = 128 passe sans nouvelles idées, encore moins n = 256.Sous Linux, compilez avec
mono-csc
et exécutez avecmono
.Explication de base
Je ne vais pas faire une dissection ligne par ligne, juste un aperçu des concepts.
En règle générale, je suis heureux de parcourir de l'ordre de 2 50 éléments dans un programme combinatoire à force brute. Pour arriver à n = 128, il est donc nécessaire d'utiliser une approche qui n'analyse pas chaque chaîne de bits. Donc, plutôt que de travailler en avant des chaînes de bits aux séquences de périodes, je travaille en arrière: étant donné une séquence de périodes, y a-t-il une chaîne de bits qui la réalise? Pour n = 2 x, il existe une borne supérieure facile de 2 x (x + 1) / 2 séquences de périodes (vs 2 2 x chaînes de bits).
Certains des arguments utilisent le lemme de périodicité des chaînes :
Wlog Je suppose que toutes les chaînes de bits considérées commencent par
0
.Étant donné une séquence de périodes où est la période du préfixe de longueur 2 i ( toujours), il y a quelques observations faciles sur les valeurs possibles de :
[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
car une période d'une chaîneS
est également une période de n'importe quel préfixe deS
.pk+1 = pk
est toujours une extension possible: il suffit de répéter la même chaîne primitive pour deux fois plus de caractères.2k < pk+1 ≤ 2k+1
est toujours une extension possible. Il suffit de le montrer car une chaîne apériodique de longueur peut être étendue à une chaîne apériodique de longueur en ajoutant n'importe quelle lettre qui n'est pas sa première lettre.pk+1 = 2k+1
L
L+1
Prenez une chaîne
Sx
de longueur 2 k dont la période est et considérez la chaîne de longueur 2 k + 1 . Il est clair a une période de 2 k 1. Supposons que sa période soit plus petite.pk
SxyS
SxyS
q
Alors donc selon la périodicité, le lemme est aussi une période de , et comme le plus grand diviseur est inférieur ou égal à ses arguments et est la plus petite période, nous devons être un facteur propre de 2 k +1. Puisque son quotient ne peut pas être 2, nous l'avons .
2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
Maintenant, puisque c'est une période, cela doit être une période de . Mais la période de l' est . Nous avons deux cas:
q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
, ou se divise de manière équivalente exactement en .pk
q
pk + q > 2k + gcd(pk, q)
de sorte que le lemme de périodicité ne force pas une période plus courte.Considérons d'abord le deuxième cas. , contredisant la définition de comme période de . Par conséquent, nous sommes obligés de conclure que c'est un facteur .
pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
Mais comme
q
c'est une période deSx
et est la période de , le préfixe de longueur n'est que des copies du préfixe de longueur , donc nous voyons que c'est aussi une période de .pk
Sx
q
q/pk
pk
pk
SxyS
La période de
SxyS
est donc soit ou 2 k +1. Mais nous avons deux options pour ! Au plus, un choix donnera la période , donc au moins un donnera la période 2 k +1. QED.pk
y
y
pk
Le lemme de périodicité nous permet de rejeter certaines des extensions possibles restantes.
Toute extension qui n'a pas réussi un test d'acceptation rapide ou de rejet rapide doit être testée de manière constructive.
La construction d'une chaîne de bits étant donné une séquence de périodes est essentiellement un problème de satisifiabilité, mais elle a beaucoup de structure. Il y a des contraintes d'égalité simples impliquées par chaque période de préfixe, donc j'utilise une structure de données d' ensemble d'unions pour combiner des bits en grappes indépendantes. C'était suffisant pour s'attaquer à n = 64, mais pour n = 128, il fallait aller plus loin. J'emploie deux arguments utiles:
2k - pk
M
est et que le préfixe de longueur a une période, le préfixe de longueur doit se terminer par . Ceci est plus puissant précisément dans les cas qui auraient autrement la plupart des clusters indépendants, ce qui est pratique.01M-1
L > M
L
L
1M
M
est et que le préfixe de longueur a une période avec et se termine par, il doit en fait se terminer par . C'est le plus puissant à l'extrême opposé, lorsque la séquence de périodes commence par beaucoup.0M
L > M
L - d
d < M
0d
10d
Si nous n'obtenons pas de contradiction immédiate en forçant le cluster avec le premier bit (supposé à zéro) à un, alors nous force brute (avec quelques micro-optimisations) sur les valeurs possibles pour les clusters non forcés. Notez que l'ordre est en nombre décroissant de car parce que si le
i
e bit est un alors la période ne peut pas êtrei
et nous voulons éviter les périodes qui sont plus courtes que celles qui sont déjà appliquées par le clustering. Descendre augmente les chances de trouver tôt une affectation valide.la source
Rust, 32, 10s
11s29ssur mon ordinateur portableAppelez-le avec la taille de bit comme argument de ligne de commande.
Techniques intelligentes: représentez les chaînes de bits directement sous forme de nombres, utilisez bittwiddling pour vérifier les cycles. Recherchez uniquement la première moitié des chaînes de bits, celles commençant par 0, car le tableau de périodes d'une chaîne de bits et son inverse (0 échangés contre 1) sont identiques. Si chaque possibilité pour la position finale s'est déjà produite, je ne la recherche pas.
Des trucs plus intelligents:
Pour dédupliquer chaque bloc (chaînes où la première moitié des bits est la même), j'utilise un bitvector, qui est beaucoup plus rapide qu'un hachage, car les longueurs de cycle finales n'ont pas besoin d'être hachées.
De plus, je saute les premières étapes de la vérification du cycle, car je sais que le dernier cycle ne peut pas être plus court que l'avant-dernier cycle.
Après beaucoup de profilage, je peux maintenant dire que presque tout le temps est utilisé de manière productive, donc des améliorations algorithmiques seront nécessaires pour s'améliorer à partir d'ici, je pense. Je suis également passé à des entiers 32 bits pour gagner un peu plus de temps.
Mettez
bit-vec = "0.4.4"
votre Cargo.tomlSi vous souhaitez exécuter ceci, clonez ceci: github.com/isaacg1/cycle puis
Cargo build --release
pour construire, puisCargo run --release 32
pour exécuter.la source
time
lui donne 27 secondes utilisateur sur mon ordinateur portable.Rouille , 16
Essayez-le en ligne!
Compilation:
rustc -O <name>.rs
La chaîne est implémentée en tant que vecteur booléen.
La
next
fonction parcourt les combinaisons;Le
find_period
prend une tranche Bool et renvoie la période;Le
compute_count_array
fait le travail pour chaque sous-séquence "puissance de deux" de chaque combinaison de booléens.Théoriquement, aucun débordement n'est prévu jusqu'à ce qu'il
2^n
dépasse la valeur maximale u64, c'est-à-diren > 64
. Cette limite pourrait être dépassée avec un test coûteux sur s = [vrai, vrai, ..., vrai].La mauvaise nouvelle est: elle renvoie 317 pour n = 16, mais je ne sais pas pourquoi. Je ne sais pas non plus s'il le fera en dix minutes pour n = 32, car le
Vec<bool>
n'est pas optimisé pour ce type de calcul.ÉDITER
J'ai réussi à supprimer la limite de 64 pour
n
. Maintenant, il ne se bloquera pas tant qu'iln
n'est pas supérieur à l'entier max usize.J'ai trouvé pourquoi le code précédent renvoyait 317 pour
n=32
. Je comptais des ensembles de périodes et non des tableaux de périodes. Il y avait trois tableaux avec les mêmes éléments:Maintenant ça marche. C'est encore lent mais ça marche.
la source
C - 16
Il échoue sur des valeurs supérieures à 16 cuz de débordement.
Je n'ai aucune idée de la vitesse à laquelle cela s'exécute cuz im sur un Chromebook l'exécutant sur repl.it.
Implémente simplement la question pendant sa lecture, passe en revue toutes les chaînes de bits, calcule les tableaux de périodes et vérifie s'ils ont déjà été comptés.
Compilez-le simplement avec gcc, etc.
la source
16
moment où le code a été modifié de sorte que les deuxmalloc
s étaientmalloc(...int*))
et...**
respectivement16
imprimésAnswer: 320
comme prévu, mais32
imprimésAnswer: 0
(et assez rapidement).n = 8
mais après l'impression du résultat, ce qui signifie que la pile est corrompue. Vous êtes probablement quelque part en train d'écrire au-delà des blocs de mémoire alloués.Haskell
Compilez avec
ghc -O2
. Essayez-le en ligne!Fonctionne en moins de 0,1 seconde sur mon matériel portable de 6 ans
n=16
.n=32
prend9992min, donc je suis facteur 9 ou 10. J'ai essayé de mettre en cache les périodes dans une table de recherche, donc je n'ai pas à les recalculer encore et encore, mais cela manque rapidement de mémoire sur ma machine de 4 Go.la source
Python 2 (PyPy), 16
la source
osboxes@osboxes:~/python$ python ascii_user.py 16 None
[C ++], 32, 4 minutes
la source