Disons que nous avons un entier non négatif qui est «lourd» (c'est-à-dire «lourd») si sa valeur numérique moyenne est supérieure à 7.
Le numéro 6959 est "lourd" car:
(6 + 9 + 5 + 9) / 4 = 7,5
Le numéro 1234 ne l'est pas, car:
(1 + 2 + 3 + 4) / 4 = 2,5
Écrivez une fonction, dans n'importe quelle langue,
HeftyDecimalCount(a, b)
qui, lorsqu'ils sont fournis, deux entiers positifs a et b renvoient un entier indiquant le nombre d'entiers "lourds" dans l'intervalle [a..b], inclus.
Par exemple, étant donné a = 9480 et b = 9489:
9480 (9+4+8+0)/4 21/4 = 5.25
9481 (9+4+8+1)/4 22/4 = 5.5
9482 (9+4+8+2)/4 23/4 = 5.75
9483 (9+4+8+3)/4 24/4 = 6
9484 (9+4+8+4)/4 25/4 = 6.25
9485 (9+4+8+5)/4 26/4 = 6.5
9486 (9+4+8+6)/4 27/4 = 6.75
9487 (9+4+8+7)/4 28/4 = 7
9488 (9+4+8+8)/4 29/4 = 7.25 hefty
9489 (9+4+8+9)/4 30/4 = 7.5 hefty
Deux des nombres de cette plage sont "lourds" et donc la fonction devrait retourner 2.
Quelques directives:
- supposons que ni a ni b ne dépasse 200 000 000.
- une solution n-carré fonctionnera, mais sera lente - quel est le plus rapide que nous pouvons résoudre cela?
Réponses:
Le problème peut être résolu dans O (polylog (b)).
Nous définissons
f(d, n)
comme le nombre d'entiers jusqu'à d chiffres décimaux avec une somme de chiffres inférieure ou égale à n. On voit que cette fonction est donnée par la formuleEn utilisant cette formule, nous pouvons par exemple trouver le nombre de nombres lourds dans l'intervalle de 8000 à 8999 car
1000 - f(3, 20)
, car il y a des milliers de nombres dans cet intervalle, et nous devons soustraire le nombre de nombres avec une somme de chiffres inférieure ou égale à 28 tout en tenant compte du fait que le premier chiffre contribue déjà 8 à la somme des chiffres.Comme exemple plus complexe, regardons le nombre de nombres lourds dans l'intervalle 1234..5678. Nous pouvons d'abord passer de 1234 à 1240 par étapes de 1. Ensuite, nous passons de 1240 à 1300 par étapes de 10. La formule ci-dessus nous donne le nombre de nombres lourds dans chacun de ces intervalles:
On passe maintenant de 1300 à 2000 par pas de 100:
De 2000 à 5000 par pas de 1000:
Nous devons maintenant réduire à nouveau la taille des pas, en passant de 5000 à 5600 par pas de 100, de 5600 à 5670 par pas de 10 et enfin de 5670 à 5678 par pas de 1.
Un exemple d'implémentation Python (qui a reçu entre-temps de légères optimisations et tests):
Edit : Remplacé le code par une version optimisée (qui semble encore plus moche que le code d'origine). J'ai également corrigé quelques cas d'angle pendant que j'y étais.
heavy(1234, 100000000)
prend environ une milliseconde sur ma machine.la source
binomial()
fonction. Il y a aussi quelques autres choses qui peuvent facilement être améliorées. Je posterai une mise à jour dans quelques minutes.f(d, n)
n'est généralement pas appelé deux fois avec les mêmes paramètres pendant une exécution du programme.Réexaminez et utilisez les permutations.
Supposons que nous définissions une fonction générale qui trouve les valeurs entre a et b avec une lourdeur supérieure à x:
Avec votre exemple de a = 8675 à b = 8689, le premier chiffre est 8, alors jetez-le - la réponse sera la même que 675 à 689, et encore de 75 à 89.
Le poids moyen des deux premiers chiffres 86 est 7, donc les chiffres restants ont besoin d'un poids moyen supérieur à 7 pour se qualifier. Ainsi, l'appel
est équivalent à
Notre plage pour le (nouveau) premier chiffre est donc de 7 à 8, avec ces possibilités:
Pour 7, nous avons encore besoin d'une moyenne de plus de 7, qui ne peut provenir que d'un chiffre final de 8 ou 9, ce qui nous donne 2 valeurs possibles.
Pour 8, nous avons besoin d'une moyenne de plus de 6, qui ne peut provenir que d'un chiffre final de 7-9, ce qui nous donne 3 valeurs possibles.
Donc, 2 + 3 donne 5 valeurs possibles.
Ce qui se passe, c'est que l'algorithme commence par le nombre à 4 chiffres et le divise en problèmes plus petits. La fonction s'appellerait à plusieurs reprises avec des versions plus faciles du problème jusqu'à ce qu'elle ait quelque chose qu'elle puisse gérer.
la source
Vous pouvez peut-être ignorer de nombreux candidats dans l'intervalle de a à b en accumulant leur "lourdeur".
si vous connaissez la longueur de votre numéro, vous savez que chaque chiffre ne peut changer la lourdeur que de 1 / longueur.
Donc, si vous commencez avec un nombre qui n'est pas lourd, vous devriez pouvoir calculer le nombre suivant qui sera lourd, si vous les augmentez d'un.
Dans votre exemple ci-dessus à partir de 8680 avg = 5,5, qui est à 7-5,5 = 1,5 point de votre frontière de lourdeur, vous savez qu'il y a 1,5 / (1/4) = 6 nombres entre les deux, qui ne sont PAS lourds.
Cela devrait faire l'affaire!
la source
/length
.Que diriez-vous d'une simple fonction récursive? Pour simplifier les choses, il calcule tous les nombres lourds avec des
digits
chiffres et une somme minimale de chiffresmin_sum
.Implémenté cela en python et il a trouvé tous les nombres lourds à 9 chiffres en ~ 2 secondes. Un peu de programmation dynamique pourrait améliorer cela.
la source
C'est une solution possible.
la source
C, pour l'intervalle [a, b] c'est O (ba)
//l'exercice
//Les resultats
la source