C'est la séquence A054261
Le ème nombre de confinement premier est le plus petit nombre qui contient les premiers nombres premiers comme sous-chaînes. Par exemple, le nombre est le nombre le plus bas qui contient les 3 premiers nombres premiers comme sous-chaînes, ce qui en fait le 3ème nombre de confinement principal.
Il est trivial de comprendre que les quatre premiers nombres premiers de confinement sont , , et , mais cela devient plus intéressant. Étant donné que le premier nombre premier est 11, le numéro de confinement premier suivant n'est pas , mais il est car il est défini comme le plus petit nombre avec la propriété.
Cependant, le vrai défi vient quand vous allez au-delà de 11. Le prochain nombre de confinement principal est . Notez que dans ce nombre, les sous - chaînes et se chevauchent. Le nombre chevauche également le nombre .11
13
3
13
Il est facile de prouver que cette séquence est en augmentation, car le nombre suivant doit remplir tous les critères du nombre précédent et avoir une sous-chaîne de plus. Cependant, la séquence n'est pas strictement croissante, comme le montrent les résultats pour n=10
et n=11
.
Défi
Votre objectif est de trouver autant de numéros de confinement principaux que possible. Votre programme devrait les sortir de façon ordonnée, en commençant par 2 et en remontant.
Règles
- Vous êtes autorisé à coder en dur les nombres premiers.
- Vous n'êtes pas autorisé à coder en dur les nombres de confinement principaux (
2
est la seule exception), ou tout nombre magique qui rend le défi trivial. Soyez gentil. - Vous pouvez utiliser la langue de votre choix. Veuillez inclure une liste de commandes pour préparer l'environnement à l'exécution du code.
- Vous êtes libre d'utiliser à la fois le CPU et le GPU, et vous pouvez utiliser le multithreading.
Notation
Le score officiel sera celui de mon ordinateur portable (Dell XPS 9560). Votre objectif est de générer autant de nombres de confinement principaux que possible en 5 minutes.
Spécifications
- Intel Core i7-7700HQ 2,8 GHz (boost 3,8 GHz) 4 cœurs, 8 threads.
- 16 Go de RAM DDR4 à 2400 MHz
- NVIDIA GTX 1050
- Linux Mint 18.3 64 bits
Les nombres trouvés jusqu'à présent, ainsi que le dernier nombre premier ajouté au nombre:
1 => 2 ( 2)
2 => 23 ( 3)
3 => 235 ( 5)
4 => 2357 ( 7)
5 => 112357 ( 11)
6 => 113257 ( 13)
7 => 1131725 ( 17)
8 => 113171925 ( 19)
9 => 1131719235 ( 23)
10 => 113171923295 ( 29)
11 => 113171923295 ( 31)
12 => 1131719237295 ( 37)
13 => 11317237294195 ( 41)
14 => 1131723294194375 ( 43)
15 => 113172329419437475 ( 47)
16 => 1131723294194347537 ( 53)
17 => 113172329419434753759 ( 59)
18 => 2311329417434753759619 ( 61)
19 => 231132941743475375961967 ( 67)
20 => 2311294134347175375961967 ( 71)
21 => 23112941343471735375961967 ( 73)
22 => 231129413434717353759619679 ( 79)
23 => 23112941343471735359619678379 ( 83)
24 => 2311294134347173535961967837989 ( 89)
25 => 23112941343471735359619678378979 ( 97)
26 => 2310112941343471735359619678378979 (101)
27 => 231010329411343471735359619678378979 (103)
28 => 101031071132329417343475359619678378979 (107)
29 => 101031071091132329417343475359619678378979 (109)
30 => 101031071091132329417343475359619678378979 (113)
31 => 101031071091131272329417343475359619678378979 (127)
32 => 101031071091131272329417343475359619678378979 (131)
33 => 10103107109113127137232941734347535961967838979 (137)
34 => 10103107109113127137139232941734347535961967838979 (139)
35 => 10103107109113127137139149232941734347535961967838979 (149)
36 => 1010310710911312713713914923294151734347535961967838979 (151)
Merci à Ardnauld, Ourous et japh d'avoir étendu cette liste.
Notez que n = 10
et n = 11
sont le même nombre, puisque est le nombre le plus bas qui contient tous les nombres , mais il contient également .
Pour référence, vous pouvez utiliser le fait que le script Python original que j'ai écrit pour générer cette liste ci-dessus calcule les 12 premiers termes en environ 6 minutes.
Règles supplémentaires
Après les premiers résultats, j'ai réalisé qu'il y avait de fortes chances que les meilleurs résultats finissent par avoir le même score. En cas d'égalité, le gagnant sera celui qui aura le moins de temps pour générer son résultat. Si deux ou plusieurs réponses produisent leurs résultats aussi rapidement, ce sera simplement une victoire à égalité.
Note finale
Le temps d'exécution de 5 minutes est seulement mis pour assurer un score équitable. Je serais très intéressé de voir si nous pouvons pousser la séquence OEIS plus loin (en ce moment, elle contient 17 chiffres). Avec le code d'Ourous, j'ai généré tous les nombres jusqu'à n = 26
, mais je prévois de laisser le code fonctionner pendant une plus longue période.
Tableau d'affichage
- Python 3 + Google OR-Tools : 169
- Scala : 137 (non officiel)
- Solveur Concorde TSP : 84 (non officiel)
- Assemblage C ++ (GCC) + x86 : 62
- Propre : 25
- JavaScript (Node.js) : 24
n=11
triviale car il vous suffit de vérifier que celan=10
satisfait également la nouvelle condition. Je dirais également que le codage en dur n'aide que jusqu'àn=17
, car aucun nombre n'est connu au-delà de ce point pour autant que je puisse le savoir.[1,22,234,2356,112356,113256,1131724,113171924,1131719234,113171923294,113171923294,1131719237294]
et commencer une recherche à partir de chacunRéponses:
Python 3 + Google OR-Tools , marque 169 en 295 secondes (score officiel)
Comment ça marche
Après avoir supprimé les nombres premiers redondants contenus dans d'autres nombres premiers, tracez un graphique dirigé avec un bord de chaque nombre premier à chacun de ses suffixes, avec la distance zéro, et un bord à chaque nombre premier de chacun de ses préfixes, avec une distance définie par le nombre de chiffres ajoutés . Nous recherchons le premier chemin lexicographiquement le plus court à travers le graphe en commençant par le préfixe vide, en passant par chaque nombre premier (mais pas nécessairement par chaque préfixe ou suffixe), et se terminant par le suffixe vide.
Par exemple, voici les bords du chemin optimal ε → 11 → 1 → 13 → 3 → 31 → 1 → 17 → ε → 19 → ε → 23 → ε → 29 → ε → 5 → ε pour n = 11, correspondant à la chaîne de sortie 113171923295.
Par rapport à la réduction directe du problème du voyageur de commerce , notez qu'en connectant indirectement les nombres premiers via ces nœuds de suffixe / préfixe supplémentaires, au lieu de les directement, nous avons considérablement réduit le nombre d'arêtes que nous devons prendre en compte. Mais comme les nœuds supplémentaires n'ont pas besoin d'être traversés exactement une fois, ce n'est plus une instance de TSP.
Nous utilisons le solveur de contraintes CP-SAT incrémentiel de Google OR-Tools, d'abord pour minimiser la longueur totale du chemin, puis pour minimiser chaque groupe de chiffres ajoutés dans l'ordre. Nous initialisons le modèle avec seulement des contraintes locales: chaque premier précède un suffixe et succède à un préfixe, tandis que chaque suffixe / préfixe précède et réussit le même nombre de nombres premiers. Le modèle résultant pourrait contenir des cycles déconnectés; si c'est le cas, nous ajoutons dynamiquement des contraintes de connectivité supplémentaires et réexécutons le solveur.
Code
Résultats
Voici les 1000 premiers nombres de confinement principaux , calculés en 1½ jours sur un système à 8 cœurs / 16 fils.
la source
Assemblage C ++ (GCC) + x86, score
323662 en 259 secondes (officiel)Résultats calculés jusqu'à présent. Mon ordinateur manque de mémoire après
65
.Tous sont d'accord avec la sortie du solveur basé sur Concorde , ils ont donc de bonnes chances d'être corrects.
Journal des modifications:
Calcul incorrect pour la longueur de contexte nécessaire. La version précédente était 1 trop volumineuse et comportait également un bogue. Résultat:
3234Ajout d'une optimisation de groupe de contexte égal. Résultat:
3436Refonte de l'algorithme pour utiliser correctement les chaînes sans contexte, ainsi que d'autres optimisations. Résultat:
3662Ajout d'une écriture appropriée.
Ajout de la variante des nombres premiers.
Comment ça marche
Attention: c'est une décharge cérébrale. Faites défiler jusqu'à la fin si vous voulez juste le code.
Abréviations:
Ce programme utilise essentiellement l'algorithme de programmation dynamique des manuels pour le TSP.
C'est beaucoup de bugs potentiels. Après avoir joué avec l'entrée de anselm et avoir échoué à en tirer des résultats erronés, je devrais au moins prouver que mon approche globale est correcte.
Bien que la solution basée sur Concorde soit (beaucoup, beaucoup) plus rapide, elle est basée sur la même réduction, donc cette explication s'applique aux deux. De plus, cette solution peut être adaptée pour OEIS A054260 , la séquence de nombres premiers contenant des nombres premiers; Je ne sais pas comment résoudre cela efficacement dans le cadre du TSP. C'est donc encore un peu pertinent.
Réduction du TSP
Commençons par prouver que la réduction au TSP est correcte. Nous avons un ensemble de chaînes, disons
et nous voulons trouver la plus petite chaîne supérieure contenant ces éléments.
Connaître la longueur suffit
Pour le PCN, s'il existe plusieurs chaînes plus courtes, nous devons renvoyer la plus petite lexicographiquement. Mais nous examinerons un problème différent (et plus facile).
Si nous pouvons résoudre SCS-Length, nous pouvons reconstruire la plus petite solution et obtenir le PCN. Si nous savons que la plus petite solution commence par notre préfixe, nous essayons de l'étendre en ajoutant chaque élément, dans l'ordre lexicographique, et en résolvant à nouveau la longueur. Lorsque nous trouvons le plus petit élément pour lequel la longueur de la solution est la même, nous savons que ce doit être le prochain élément dans la plus petite solution (pourquoi?), Alors ajoutez-le et reprenez les éléments restants. Cette méthode pour atteindre la solution est appelée auto-réduction .
Visite du graphique de chevauchement maximal
Supposons que nous ayons commencé à résoudre SCS pour l'exemple ci-dessus à la main. Nous aurions probablement:
13
et37
, car ils sont déjà des sous-chaînes des autres éléments. Toute solution qui contient137
, par exemple, doit également contenir13
et37
.113,137 → 1137
,211,113 → 2113
etc.C'est en fait la bonne chose à faire, mais prouvons-le par souci d'exhaustivité. Prenez n'importe quelle solution SCS; par exemple, la chaîne la plus courte pour
A
estet il peut être décomposé en une concaténation de tous les éléments dans
A
:(Nous ignorons les éléments redondants
13, 37
.) Observez que:Nous montrerons que chaque super-chaîne la plus courte peut être décomposée de cette façon:
Pour chaque paire d'éléments adjacents
x,y
,y
commence et se termine à des positions ultérieures àx
. Si ce n'est pas vrai, alors c'est soitx
une sous-chaîne dey
ou vice versa. Mais nous avons déjà supprimé tous les éléments qui sont des sous-chaînes, ce qui ne peut pas se produire.Supposons que les éléments adjacents de la séquence aient un chevauchement inférieur au maximum, par exemple
21113
au lieu de2113
. Mais cela rendrait le1
redondant supplémentaire . Aucun élément ultérieur n'a besoin de l'initiale1
(comme dans 2 1 113), car il se produit plus tôt que113
, et tous les éléments qui apparaissent après113
ne peuvent pas commencer par un chiffre avant113
(voir point 1). Un argument similaire empêche le dernier extra1
(comme dans 211 1 3) d'être utilisé par n'importe quel élément avant211
. Mais notre super chaîne la plus courte , par définition, n'aura pas de chiffres redondants, de sorte que de tels chevauchements non maximaux ne se produiront pas.Avec ces propriétés, nous pouvons convertir tout problème SCS en TSP:
x
,y
ajoutez un bord dex
ày
dont le poids est le nombre de symboles supplémentaires ajoutés en ajoutanty
àx
avec chevauchement maximal. Par exemple, nous ajouterions un bord de211
à113
avec le poids 1, car2113
ajoute un chiffre de plus211
. Répétez l'opération pour le bord dey
àx
.Tout chemin sur ce graphique, à partir du préfixe initial, correspond à une concaténation à chevauchement maximal de tous les éléments sur ce chemin, et le poids total du chemin est égal à la longueur de chaîne concaténée. Par conséquent, chaque tour de poids le plus bas, qui visite tous les articles au moins une fois, correspond à une super chaîne la plus courte.
Et c'est la réduction de SCS (et SCS-Length) à TSP.
Algorithme de programmation dynamique
Il s'agit d'un algorithme classique, mais nous le modifierons un peu, alors voici un petit rappel.
(J'ai écrit cela comme un algorithme pour SCS-Length au lieu de TSP. Ils sont essentiellement équivalents, mais le vocabulaire SCS aide lorsque nous arrivons aux optimisations spécifiques à SCS.)
Appelez l'ensemble des éléments d'entrée
A
et le préfixe donnéP
. Pour chaquek
sous-ensembleS
d'élémentsA
et dans chaque élémente
deS
, nous calculons la longueur de la chaîne la plus courte qui commence parP
, contient toutS
et se termine pare
. Cela implique de stocker une table des valeurs de(S, e)
à leurs longueurs SCS.Lorsque nous arrivons à chaque sous - ensemble
S
, les besoins de table pour contenir déjà les résultatsS - {e}
pour touse
dansS
. Comme le tableau peut devenir assez volumineux, je calcule les résultats pour tous lesk
sous-ensembles d'éléments, puisk+1
, etc. Pour cela, nous devons uniquement stocker les résultats pourk
etk+1
à tout moment. Cela réduit l'utilisation de la mémoire d'un facteur d'environsqrt(|A|)
.Encore un détail: au lieu de calculer la longueur SCS minimum, je calcule en fait le chevauchement total maximum entre les articles. (Pour obtenir la longueur SCS, il suffit de soustraire le chevauchement total de la somme des longueurs des articles.) L'utilisation de chevauchements permet certaines des optimisations suivantes.
[2.] Contextes d'élément
Un contexte est le suffixe le plus long d'un élément qui peut chevaucher les éléments suivants. Si nos articles le sont
113,211,311
, alors11
est le contexte de211
et311
. (C'est aussi le contexte de préfixe pour113
lequel nous allons voir en partie [4.])Dans l'algorithme DP ci-dessus, nous avons gardé une trace des solutions SCS qui se terminent par chaque élément, mais nous ne nous soucions pas réellement de l'élément dans lequel se termine un SCS. Tout ce que nous devons savoir, c'est le contexte. Ainsi, par exemple, si deux SCS pour le même ensemble se terminent par
23
et43
, tout SCS qui continue de l'un fonctionnera également pour l'autre.Il s'agit d'une optimisation importante, car les nombres premiers non triviaux se terminent uniquement par les chiffres
1 3 7 9
. Les quatre contextes à un chiffre1,3,7,9
(plus le contexte vide) sont en fait suffisants pour calculer les PCN pour les nombres premiers jusqu'à131
.[3.] Éléments sans contexte
D'autres ont déjà souligné que de nombreux nombres premiers commencent par les chiffres
2,4,5,6,8
, tels que23,29,41,43...
. Aucun de ceux-ci ne peut chevaucher un nombre premier précédent (à l'exception de2
et5
, les nombres premiers ne peuvent pas se terminer par ces chiffres2
et5
auront déjà été supprimés comme redondants). Dans le code, celles-ci sont appelées chaînes sans contexte .Si notre entrée contient des éléments sans contexte, chaque solution SCS peut être divisée en blocs
et les chevauchements dans chaque bloc sont indépendants des autres blocs. Nous pouvons mélanger les blocs ou échanger des éléments entre des blocs qui ont le même contexte, sans changer la longueur du SCS.
Ainsi, nous devons seulement garder une trace des multisets possibles de contextes, un pour chaque bloc.
Exemple complet: pour les nombres premiers inférieurs à 100, nous avons 11 éléments sans contexte et leurs contextes:
Notre contexte multiset initial:
Le code les appelle des contextes combinés ou ccontextes . Ensuite, il nous suffit de considérer les sous-ensembles des éléments restants:
[4.] Fusion de contexte
Une fois que nous arrivons aux nombres premiers à 3 chiffres ou plus, il y a plus de redondances:
Ces groupes partagent les mêmes contextes de début et de fin (généralement - cela dépend des autres nombres premiers dans l'entrée), donc ils ne peuvent pas être distingués lorsqu'ils se chevauchent avec d'autres éléments. Nous ne nous soucions que des chevauchements, nous pouvons donc traiter les nombres premiers de ces groupes à contexte égal comme indiscernables. Maintenant, nos sous-ensembles DP sont condensés en plusieurs sous-ensembles
(C'est aussi pourquoi le solveur maximise la longueur de chevauchement au lieu de minimiser la longueur SCS: cette optimisation préserve la longueur de chevauchement.)
Résumé: les optimisations de haut niveau
L'exécution avec une
INFO
sortie de débogage affichera des statistiques commeCette ligne particulière concerne la longueur SCS des 62 premiers nombres premiers
2
à293
.1,3,7,11,13,27
plus la chaîne vide.43,47,53,59,61,89,211,223,227,229,241,251,257,263,269,281,283
. Ceux-ci et le préfixe donné (dans ce cas, une chaîne vide) forment la base du contexte combiné initial .N_search
), il existe 16 groupes à contexte égal non triviaux .En exploitant ces structures, le calcul de la longueur SCS n'a besoin que de vérifier 8498336
(multiset, ccontext)
combinaisons. Une programmation dynamique simple prendrait des43×2^43 > 3×10^14
mesures, et le forçage brutal des permutations prendrait des6×10^52
mesures. Le programme doit encore exécuter SCS-Length plusieurs fois pour reconstruire la solution PCN, mais cela ne prend pas beaucoup de temps.[5., 6.] Les optimisations de bas niveau
Au lieu d'effectuer des opérations de chaîne, le solveur SCS-Length fonctionne avec des indices d'éléments et de contextes. Je précalcule également le montant de chevauchement entre chaque contexte et chaque paire d'articles.
Le code utilisait initialement GCC
unordered_map
, qui semble être une table de hachage avec des compartiments de liste liés et des tailles de hachage principales (c'est-à-dire des divisions coûteuses). J'ai donc écrit ma propre table de hachage avec une sonde linéaire et des tailles de puissance de deux. Cela permet une accélération de 3 × et une réduction de 3 × de la mémoire.Chaque état de table se compose d'un ensemble multiple d'éléments, d'un contexte combiné et d'un nombre de chevauchements. Ceux-ci sont regroupés en entrées de 128 bits: 8 pour le nombre de chevauchements, 56 pour le multiset (comme un jeu de bits avec codage de longueur) et 64 pour le ccontext (RLE délimité par 1). Encoder et décoder le ccontext était la partie la plus délicate et j'ai fini par utiliser la nouvelle
PDEP
instruction (c'est tellement nouveau, GCC n'a pas encore intrinsèque pour cela).Enfin, l'accès à une table de hachage est très lent lorsqu'il
N
devient volumineux, car la table ne tient plus dans le cache. Mais la seule raison pour laquelle nous écrivons dans la table de hachage est de mettre à jour le nombre de chevauchements le plus connu pour chaque état. Le programme divise cette étape en une file d'attente de prélecture, et la boucle interne prélecte chaque recherche de table quelques itérations avant de mettre à jour cet emplacement. Une autre accélération 2 × sur mon ordinateur.Bonus: améliorations supplémentaires
AKA Comment Concorde est-il si rapide?
Je ne connais pas grand-chose aux algorithmes TSP, alors voici une estimation approximative.
Concorde utilise la méthode branch-and-cut pour résoudre les TSP.
Des idées évidentes que nous pourrions essayer:
Cependant, la combinaison branch-and-cut est très puissante, donc nous pourrions ne pas être en mesure de battre un solveur de pointe comme Concorde, pour les grandes valeurs de
N
.Bonus bonus: les nombres premiers de confinement
Contrairement à la solution basée sur le Concorde, ce programme peut être modifié pour trouver les contenant plus petits nombres premiers ( OEIS A054260 ). Cela implique trois changements:
Modifiez le code du solveur SCS-Length pour catégoriser les solutions selon que leurs chiffres sont divisibles par 3. Cela implique d'ajouter une autre entrée, la somme des chiffres mod 3, à chaque état DP. Cela réduit considérablement les chances que le solveur principal reste bloqué avec des permutations non principales. C'est le changement que je n'ai pas pu trouver comment traduire en TSP. Il peut être encodé avec ILP, mais je devrais alors en apprendre davantage sur cette chose appelée «inégalité de sous-circuit» et comment les générer.
Il se peut que tous les PCN les plus courts soient divisibles par 3. Dans ce cas, le plus petit nombre premier de confinement premier doit être au moins un chiffre plus long que le PCN. Si notre solveur SCS-Length le détecte, le code de reconstruction de la solution a la possibilité d'ajouter un chiffre supplémentaire à tout moment du processus. Il essaie d'ajouter chaque chiffre possible
0..9
et chaque élément restant au préfixe de solution actuel, dans l'ordre lexicographique comme précédemment.Avec ces changements, je peux obtenir les solutions jusqu'à
N=62
. Sauf pour47
, où le code de reconstruction est bloqué et abandonne après 1 million de pas (je ne sais pas encore pourquoi). Les nombres premiers de confinement principaux sont:Code
Compiler avec
Pour la version en nombre premier, liez également avec GMPlib, par exemple
Ce programme utilise l'instruction PDEP, qui n'est disponible que sur les processeurs x86 récents (Haswell +). Mon ordinateur et maxb le supportent. Si ce n'est pas le cas, le programme se compilera dans une version logicielle lente. Un avertissement de compilation sera imprimé lorsque cela se produira.
Essayez-le en ligne!
Et la version exclusive sur TIO . Désolé, mais je n'ai pas joué à ces programmes et il y a une limite de longueur de message.
la source
debug_dummy
, vous pouvez utiliser#define DEBUG(x) void(0)
.debug_dummy
parce que je veux que le type des arguments soit vérifié et évalué même lorsque le débogage est désactivé.N=32
me faut seulement environ 500 Mo, je pense.main
, mais je l'ai trouvé à partir du lien TIO.JavaScript (Node.js) , marque 24 en 241 secondes
Résultats
Algorithme
Il s'agit d'une recherche récursive qui essaie toutes les manières possibles de fusionner des nombres et finalement trie les listes résultantes dans l'ordre lexicographique lorsqu'un nœud feuille est atteint.
Au début de chaque itération, toute entrée qui peut être trouvée dans une autre entrée est supprimée de la liste.
Une accélération significative a été obtenue en gardant une trace des nœuds visités, afin que nous puissions abandonner tôt lorsque différentes opérations conduisent à la même liste.
Une petite accélération a été obtenue en mettant à jour et en restaurant la liste lorsque cela était possible plutôt qu'en générant une copie, comme suggéré par
un utilisateur anonymeNeil.Exemple
Code
Essayez-le en ligne!
la source
Solveur Concorde TSP , marque 84 en 299 secondes
Eh bien… je me sens stupide de ne l'avoir réalisé que maintenant.
Tout cela est essentiellement un problème de vendeur ambulant . Pour chaque paire de nombres premiers
p
etq
, ajoutez un bord dont le poids est le nombre de chiffres ajoutés parq
(en supprimant les chiffres qui se chevauchent). En outre, ajoutez un bord initial à chaque nombre premierp
, dont le poids est la longueur dep
. Le chemin de vendeur itinérant le plus court correspond à la longueur du plus petit nombre de confinement principal.Ensuite, un solveur TSP de qualité industrielle, tel que Concorde , résoudra rapidement ce problème.
Cette entrée devrait probablement être considérée comme non concurrente.
Résultats
Le solveur arrive à
N=350
environ 20 heures CPU. Les résultats complets sont trop longs pour un poste SE, et l'OEIS ne veut pas autant de termes de toute façon. Voici les 200 premiers:Code
Voici un script Python 3 pour appeler le solveur Concorde encore et encore jusqu'à ce qu'il construise les solutions.
Concorde est gratuit pour un usage académique. Vous pouvez télécharger un binaire exécutable de Concorde construit avec leur propre package de programmation linéaire QSopt, ou si vous avez en quelque sorte une licence pour IBM CPLEX, vous pouvez construire Concorde à partir de la source pour utiliser CPLEX.
la source
Propre , marque 25 en 231 secondes (score officiel)
Résultats
1 < n <= 23
en4236 secondes sur TIOn = 24 (2311294134347173535961967837989)
en3224 secondes localementn = 25 (23112941343471735359619678378979)
en210160secondes localementn = 1
an = 25
été trouvé en 231 secondes pour le score officiel (édité par maxb)Cela utilise une approche similaire à la solution JS d'Arnauld basée sur le rejet récursif de permutation, en utilisant un ensemble d'arbres spécialisé pour gagner beaucoup de vitesse.
Pour chaque nombre premier qui doit correspondre au nombre:
Ensuite, pour chaque paire de sous-chaînes que nous avons jointes, supprimez toutes les sous-chaînes de cette paire jointe de la liste des sous-chaînes et reprenez-la.
Une fois qu'aucune sous-chaîne supplémentaire ne peut être jointe à d'autres sous-chaînes sur n'importe quel bras de notre récursivité, nous utilisons l'ensemble d'arbres déjà ordonné pour trouver rapidement le nombre le plus bas contenant les sous-chaînes.
Points à améliorer / à ajouter:
Il y a eu de fortes baisses de performances entre
19 -> 20
et en24 -> 25
raison de la manipulation en double par l'étape d'essai de fusion et l'étape de rejet du candidat, mais celles-ci ont été corrigées.Optimisations:
removeOverlap
est conçu pour toujours donner un ensemble de sous-chaînes déjà dans l'ordre optimaluInsertMSpec
réduit check-if-is-member et insert-new-member à une traversée d'ensemblecontainmentNumbersSt
vérifie si la solution précédente fonctionne pour un nouveau numéroEssayez-le en ligne!
Enregistrer dans
main.icl
et compiler avec:clm -fusion -b -IL Dynamics -IL StdEnv -IL Platform main
Cela produit un fichier
a.out
qui doit être exécuté en tant quea.out -h <heap_size>M -s <stack_size>M
, où<heap_size> + <stack_size>
est la mémoire qui sera utilisée par le programme en mégaoctets.(Je règle généralement la pile à 50 Mo, mais j'ai rarement des programmes qui en utilisent autant)
la source
Scala , score 137Modifier:
Le code ici simplifie à l'excès le problème.
Ainsi, la solution fonctionne pour de nombreuses entrées, mais pas pour toutes.
Message d'origine:
Idée basique
Problème plus simple
Tout d'abord, nous générons l'ensemble des nombres premiers et supprimons tous ceux qui sont déjà des sous-chaînes des autres. Ensuite, nous pouvons appliquer plusieurs règles, c'est-à-dire s'il n'y a qu'une seule chaîne se terminant par une séquence et qu'une seule commençant par cette même séquence, nous pouvons les fusionner. Un autre serait que si une chaîne commence et se termine par la même séquence (comme le fait 101), nous pouvons l'ajouter / l'ajouter à une autre chaîne sans changer sa fin. (Ces règles ne donnent que sous certaines conditions, alors faites attention quand les appliquer)
Le vrai problème
10103
0
10103
1
Ainsi, si les règles de l'algorithme ci-dessus étaient toujours suffisantes, le problème aurait été démontré qu'il n'était pas NP-difficile.
findSeq
Essayez en ligne
Code
Comme Anders Kaseorg l'a souligné dans les commentaires, ce code peut renvoyer des résultats sous-optimaux (donc incorrects).
Résultats
187
188
189
193
la source
1234
,3423
,2345
, vous générez au123453423
lieu de la valeur optimale12342345
.457, 571, 757
(tous les nombres premiers).findSeq
reviendrait7574571
pour cela, mais la longueur la plus courte est457571
. Votre approche consiste donc à jouer avec le feu. Voté pour sa pure audace, cependant.