Algorithme pour trouver les Numéros de la Chance
Je suis tombé sur cette question.Un nombre est appelé la chance si la somme de ses chiffres, ainsi que la somme des carrés de ses chiffres est un nombre premier. Combien de nombres compris entre A et B sont de la chance? 1 <= A <= B <= 1018. J'ai essayé ce.
- J'ai d'abord généré tous les possibles de nombres premiers entre 1 et le nombre qui pourrait être entraîné par l'addition des places (81 *18 = 1458).
- J'ai lu dans A et B, trouver le nombre maximal pouvant être obtenus en additionnant les chiffres, Si B est un nombre à 2 chiffres ( le nombre maximum est de 18 généré par 99).
- Pour chaque nombre premier entre 1 un nombre maximum. J'ai appliqué entier partition de l'algorithme.
- Pour chaque partition, j'ai vérifié si leur somme des carrés de leurs chiffres forment le premier. Si donc les permutations possibles de cette partition sont générés et s'ils se trouvent dans la gamme, ils sont des numéros de la chance.
C'est la mise en œuvre:
#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include <stdlib.h>
#include<string.h>
long long luckynumbers;
int primelist[1500];
int checklucky(long long possible,long long a,long long b){
int prime =0;
while(possible>0){
prime+=pow((possible%10),(float)2);
possible/=10;
}
if(primelist[prime]) return 1;
else return 0;
}
long long getmax(int numdigits){
if(numdigits == 0) return 1;
long long maxnum =10;
while(numdigits>1){
maxnum = maxnum *10;
numdigits-=1;
}
return maxnum;
}
void permuteandcheck(char *topermute,int d,long long a,long long b,int digits){
if(d == strlen(topermute)){
long long possible=atoll(topermute);
if(possible >= getmax(strlen(topermute)-1)){ //to skip the case of getting already read numbers like 21 and 021(permuted-210
if(possible >= a && possible <= b){
luckynumbers++;
}
}
}
else{
char lastswap ='\0';
int i;
char temp;
for(i=d;i<strlen(topermute);i++){
if(lastswap == topermute[i])
continue;
else
lastswap = topermute[i];
temp = topermute[d];
topermute[d] = topermute[i];
topermute[i] = temp;
permuteandcheck(topermute,d+1,a,b,digits);
temp = topermute[d];
topermute[d] = topermute[i];
topermute[i] = temp;
}
}
}
void findlucky(long long possible,long long a,long long b,int digits){
int i =0;
if(checklucky(possible,a,b)){
char topermute[18];
sprintf(topermute,"%lld",possible);
permuteandcheck(topermute,0,a,b,digits);
}
}
void partitiongenerator(int k,int n,int numdigits,long long possible,long long a,long long b,int digits){
if(k > n || numdigits > digits-1 || k > 9) return;
if(k == n){
possible+=(k*getmax(numdigits));
findlucky(possible,a,b,digits);
return;
}
partitiongenerator(k,n-k,numdigits+1,(possible + k*getmax(numdigits)),a,b,digits);
partitiongenerator(k+1,n,numdigits,possible,a,b,digits);
}
void calcluckynumbers(long long a,long long b){
int i;
int numdigits = 0;
long long temp = b;
while(temp > 0){
numdigits++;
temp/=10;
}
long long maxnum =getmax(numdigits)-1;
int maxprime=0,minprime =0;
temp = maxnum;
while(temp>0){
maxprime+=(temp%10);
temp/=10;
}
int start = 2;
for(;start <= maxprime ;start++){
if(primelist[start]) {
partitiongenerator(0,start,0,0,a,b,numdigits);
}
}
}
void generateprime(){
int i = 0;
for(i=0;i<1500;i++)
primelist[i] = 1;
primelist[0] = 0;
primelist[1] = 0;
int candidate = 2;
int topCandidate = 1499;
int thisFactor = 2;
while(thisFactor * thisFactor <= topCandidate){
int mark = thisFactor + thisFactor;
while(mark <= topCandidate){
*(primelist + mark) = 0;
mark += thisFactor;
}
thisFactor++;
while(thisFactor <= topCandidate && *(primelist+thisFactor) == 0) thisFactor++;
}
}
int main(){
char input[100];
int cases=0,casedone=0;
long long a,b;
generateprime();
fscanf(stdin,"%d",&cases);
while(casedone < cases){
luckynumbers = 0;
fscanf(stdin,"%lld %lld",&a,&b);
int i =0;
calcluckynumbers(a,b);
casedone++;
}
}
L'algorithme est trop lent. Je pense que la réponse peut être trouvée, basée sur la propriété des nombres.Veuillez partager vos pensées. Merci.
- Bug:
primelist
est de dimension 1400 mais vous traiter comme si elle est de dimension 1500 - Je pense que cette question devrait être déplacé vers la codereview.stackexchange.com
- R, je ne pense pas que c'est une grosse affaire
- Mais l'algorithme est lente. Il doit être améliorée ou un nouvel devraient être trouvées.
- vous pensez que l'écriture au-delà de la fin d'un tableau "n'est pas un big deal" ???
- êtes-vous sûr que ce n'est pas des devoirs?
- Ce n'est pas de devoirs. Mais c'est à partir du site web InterviewStreet.com. La résolution du problème est une chose. La résolution dans le temps qu'ils allouent est une autre bête complètement.
Vous devez vous connecter pour publier un commentaire.
Excellente solution OleGG, Mais ton code n'est pas optimisé. J'ai fait les changements suivants à votre code,
Il ne nécessite pas de passer par 9*9*i pour k dans count_lucky fonction, parce que pour 10000 cas, il irait à de nombreuses reprises, à la place j'ai réduit cette valeur à travers de début et de fin.
j'ai utilisé sna tableau pour stocker les résultats intermédiaires. Il pourrait ne pas ressembler à beaucoup, mais plus de 10000 cas, c'est le facteur majeur qui réduit le temps.
J'ai testé ce code, et il a passé tous les cas de test. Voici le code modifié:
Explication:
gen_primes() et gen_table() sont assez explicites.
count_lucky() fonctionne de la manière suivante:
diviser le nombre de split_max[], juste le stockage unique de chiffres pour les unes, des dizaines, des centaines, etc. les positions.
L'idée est la suivante: supposons que split_map[2] = 7, donc nous avons besoin de calculer le résultat pour
1 dans des centaines de position et tous les de 00 à 99.
2 dans des centaines de position et tous les de 00 à 99.
.
.
7 dans des centaines de position et tous les de 00 à 99.
c'est fait(dans l de la boucle) en termes de somme des chiffres et de la somme des carrés des chiffres qui a été precalcutaled.
pour cet exemple: somme varie de 0 à 9*i & somme des carré varie de 0 à 9*9* - je...c'est fait en j et k les boucles.
Ceci est répété pour toutes les longueurs je boucle
C'était l'idée de OleGG.
Pour l'optimisation suivant est considéré comme:
son inutile d'exécuter la somme des carrés de 0 à 9*9*i comme pour le particulier, les sommes des chiffres qu'il n'irait pas jusqu'à la gamme complète. Comme si i = 3 et la somme est égale à 5, la somme des carrés ne serait pas varier de 0 à 9*9*3.Cette partie est stockée dans des start[] et à la fin[] tableaux à l'aide de valeurs précalculées.
valeur pour un nombre donné de chiffres et de particulier chiffre le plus significatif de la position de numéro et jusqu'à somme et jusqu'à la somme des carrés isstored pour la mémorisation. Ses trop long, mais son environ 45 MO.
Je crois que ça pourrait être encore optimisé.
ans[]
(20x gain) et de se déplacerif(primes[j + y])
hors de la boucle (2x gain). Pour un total de ~40x gain.Vous devez utiliser DP pour cette tâche. Voici ma solution:
Brève explication:
C'est tout. Le calcul préalable de travaux pour O(log(MAX_NUMBER)^3), chaque étape ont également cette complexité.
J'ai testé ma solution contre linéaire simple et les résultats étaient équivalents
for (int l = 0; l < split_max[i]; ++l)
nous permet de traiter non seulement avec des chiffres comme 1100111, mais avec tous - j'ai pris en compte que le premier chiffre de la coupée en deux partie peut être non seulement 0 ou 1.Au lieu d'énumérer l'espace des nombres, l'énumération des différents "signatures" des chiffres qui ont de la chance. et puis l'impression de toutes les differnet combinaison de ceux-ci.
Cela peut être fait avec trivial retour en arrière:
Quand je le lance il ne:
Au lieu de trouvé++ vous souhaitez générer toutes les permutations distinctes de chiffres que vous pouvez construire avec ce numéro. J'ai aussi précalculer la première 1M de nombres premiers jamais.
Je n'ai pas vérifié si le code est correct à 100%, vous pouvez avoir à le corriger un peu. Mais le rought idée est là, et je suis en mesure de générer toutes les chance de permutation en dessous de 0,2 s (même sans les bugs, il ne devrait pas être plus de deux fois plus lent).
Et bien sûr, vous voulez générer les permutations que de vérifier Un <= B. Vous pouvez ignorer la génération des partitions qui ont plus de chiffres que de B ou de moins que trop. De toute façon vous pouvez améliorer sur mon idée générale à partir d'ici.
(Note: Le texte de présentation au début parce que j'ai couper et de coller le code que j'ai écrit pour le projet euler, d'où la très rapide is_prime qui travaille pour le N <= 1M 😉 )
Pour ceux qui n'étaient pas au courant déjà, c'est un problème sur le site InterviewStreet.com (et à mon avis, le plus difficile là-bas). Mon approche a commencé similaire (et a été inspiré par) OleGG ci-dessous. Cependant, après la création de la première [19][163][1459] tableau qu'il a fait (que je vais appeler table1), je suis allé dans une direction légèrement différente. J'ai créé un deuxième tableau de lambeaux longueur [19][x][3] (table2), où x est le numéro de la somme unique de paires pour le même nombre de chiffres. Et pour la troisième dimension de la table longueur 3, le 1er élément est la quantité d'unique "somme des paires" avec la somme et squareSum valeurs détenues par les 2e et 3e éléments.
Par exemple:
Les chiffres que j'ai pour la deuxième dimension de la longueur de la matrice (10, 55, 204, 518, ..., 15552, 17547) peut être vérifié par l'interrogation de la table1, et dans un mode similaire table2 peut être remplie. Maintenant, à l'aide de table2 on peut résoudre les grands "lucky" des requêtes beaucoup plus rapide que OleGG publiés méthode, bien qu'encore en employant une semblable "split" processus comme il l'a fait. Par exemple, si vous avez besoin de trouver de la chance(00000-54321) (c'est à dire les numéros de la chance entre 0 et 54321), il se décompose à la somme de 5 lignes:
Qui se décompose en outre:
Chacune de ces valeurs peuvent être obtenues facilement par l'interrogation de table2. Par exemple, de la chance(40000-49999) est trouvé par l'ajout de 4 et 16 de la 2e et 3e éléments de la troisième dimension table2:
Ou pour les chanceux(54200-54299):
Maintenant, OleGG la solution des résultats significativement plus rapide que tout ce que j'avais essayé jusqu'alors, mais avec mes modifications décrites ci-dessus, il effectue encore mieux qu'avant (par un facteur d'environ 100x pour un grand jeu de test). Cependant, il n'est toujours pas près assez rapide pour le test à l'aveugle cas sur InterviewStreet. Grâce à quelques petits malins hack, j'ai été en mesure de déterminer, je suis actuellement en cours d'exécution sur 20x trop lent pour compléter leur jeu de test dans le temps imparti. Cependant, je ne trouve pas d'autres optimisations. Le plus grand puits de temps ici est évidemment une itération dans la deuxième dimension de la table2, et le seul moyen d'éviter cela serait de compiler les résultats de ces sommes. Cependant, il y a trop de possibilités pour calculer tous dans le temps (5 secondes) ou de stocker toutes dans l'espace (256 MO). Par exemple, le chanceux(54200-54299) de la boucle ci-dessus pourrait être pré-calculé et stocké sous la forme d'une valeur unique, mais si c'était le cas, nous aurions également besoin de pré-calcul de la chance(123000200-123000299) et de la chance(99999200-99999299), etc etc. J'ai fait le calcul et il est beaucoup trop de calculs de pré-calcul.
Je viens de résoudre ce problème.
C'est juste un problème de Programmation Dynamique. Prendre
DP[n](sum-square_sum)
que le DP de la fonction, etDP[n](sum-square_sum)
est le nombre de tous les nombres dont les chiffres est inférieur ou égal à n, avec la somme et square_sum de chiffres du nombre est respectivement représentés par la somme et l'square_sum. Par exemple:Puisque l'on peut aisément comprendre la première DP DP état[1][..][..], il est:
alors nous pouvons déduire DP[1] à partir de DP[1], puis DP[3] ... DP[18]
le déduire ci-dessus est faite par le fait que chaque fois, quand n augmente de 1, par exemple à partir de DP[1] pour DP[2], nous avons obtenu un nouvel chiffres (0..9), et l'ensemble des (somme, square_sum) paire (c'est à dire DP[n]) doit être mis à jour.
Enfin, nous pouvons parcourir le DP[18] est réglé et le nombre des chiffres qui ont de la chance.
Bien, que diriez-vous le temps et l'espace de la complexité de l'algorithme ci-dessus?
Comme nous le savons somme <= 18*9=162, square_sum <= 18*9*9 = 1458, ainsi, l'ensemble de (somme, square_sum) paire
(c'est à dire DP[n]) est très faible, moins de 162*1458=236196, en fait, il est beaucoup plus petit que 236196;
Le fait est: mon ruby programme de comptage de tous les numéros de la chance entre 0 et 10^18 se termine en moins de 1s.
et je test mon programme en écrivant une fonction de test en utilisant la force brute de l'algorithme, et c'est juste pour les nombres inférieurs à 10^7 .
Sur la base des exigences, vous pouvez le faire de différentes manières. Si je le faisais, je voudrais calculer les nombres premiers en utilisant "Crible d'Eratosthène" dans la plage requise (de A à (9*2)*B. longueur), de les mettre en cache (encore une fois, selon votre configuration, vous pouvez utiliser de la mémoire ou de la mémoire cache du disque) et l'utiliser pour la prochaine course.
Je viens de codé une solution rapide (Java), comme ci-dessous (NOTE: débordement d'Entier n'est pas cochée. Juste un exemple rapide. Aussi, mon code n'est pas optimisé.):
Et le résultat a été:
[11, 12, 14, 16, 21, 23, 25, 32, 38, 41, 49, 52, 56, 58, 61, 65, 83, 85, 94, 101, 102, 104, 106, 110, 111, 113, 119, 120, 131, 133, 137, 140, 146, 160, 164, 166, 173, 179, 191, 197, 199, 201, 203, 205, 210, 223, 229, 230, 232, 250, 289, 292, 298, 302, 308, 311, 313, 317, 320, 322, 331, 335, 337, 344, 346, 353, 355, 364, 368, 371, 373, 377, 379, 380, 386, 388, 397, 401, 409, 410, 416, 434, 436, 443, 449, 461, 463, 467, 476, 490, 494, 502, 506, 508, 520, 533, 535, 553, 559, 560, 566, 580, 595, 601, 605, 610, 614, 616, 634, 638, 641, 643, 647, 650, 656, 661, 665, 674, 683, 689, 698, 713, 719, 731, 733, 737, 739, 746, 764, 773, 779, 791, 793, 797, 803, 805, 829, 830, 836, 838, 850, 863, 869, 883, 892, 896, 904, 911, 917, 919, 922, 928, 937, 940, 944, 955, 968, 971, 973, 977, 982, 986, 991]
Je n'ai pas analysé avec soin votre solution actuelle, mais cela pourrait l'améliorer:
Depuis l'ordre des chiffres n'a pas d'importance, vous devez passer par toutes les combinaisons possibles de chiffres de 0 à 9 de longueur 1 à 18, garder une trace de la somme de chiffres et de leurs places et l'ajout d'un chiffre à un moment, en utilisant le résultat du calcul précédent.
Donc, si vous savez que pour 12 somme des chiffres est 3 et de places est de 5, examinez les chiffres
120, 121, 122, etc... et de calculer des sommes pour eux trivialement à partir de la 3 et la 5 pour 12.
Parfois la solution la plus rapide est incroyablement simple:
Juste modifier votre code pour générer des "precomputedBitField".
Si vous êtes inquiet au sujet de la taille, afin de couvrir tous les nombres de 0 à 999, il ne vous coûtera 125 octets, de sorte que cette méthode sera probablement plus petit (et beaucoup plus rapide) que toute autre alternative.
J'ai essayé de trouver une solution à l'aide de Pierre de la méthode d'énumération, mais n'est jamais venu avec suffisamment rapide façon de compter les permutations. OleGG de la méthode de comptage est très intelligent, et les pirates du optimisations sont nécessaires pour les rendre assez rapidement. Je suis venu avec une légère amélioration, et une solution à un problème grave.
Tout d'abord, l'amélioration: vous n'avez pas à parcourir toutes les sommes et squaresums un par un pour vérifier les nombres premiers dans pirate's j et k de la boucle. Vous avez (ou peut facilement générer une liste de nombres premiers. Si vous utilisez les autres variables à comprendre que les nombres premiers sont dans la gamme, vous pouvez tout simplement par le biais de la liste des nombres premiers pour la somme et l'squaresum. Un tableau de nombres premiers et une table de recherche pour rapidement déterminer à partir de quel indice le premier >= un nombre est à est utile. Cependant, ce n'est probablement qu'une assez légère amélioration.
Le gros problème, c'est avec des pirates du sna tableau de cache. Il n'est pas 45 MO comme l'a prétendu; avec la version 64 bits d'entrées, c'est quelque chose comme 364MB. C'est à l'extérieur (en cours) admis les limites de la mémoire en C et Java. Elle peut être réduite à 37MB par se débarrasser de la "l" de dimension, ce qui est inutile et mal les performances du cache de toute façon. Vous êtes vraiment intéressés par la mise en cache de compte pour l + somme et l*l + squaresum, pas l, somme, et squaresum individuellement.
D'abord je tiens à ajouter qu'un nombre de la chance peut être calculé par un tamis, l'explication de l'tamis peuvent être trouvés ici: http://en.wikipedia.org/wiki/Lucky_number
de sorte que vous pouvez améliorer votre solution de la vitesse à l'aide d'un tamis afin de déterminer le nombre,