2009-11-29 4 views
2

J'apprends Python à travers les problèmes de Project Euler. Pour problem 40 j'ai écrit ce code:Qu'est-ce qui ne va pas avec mon "digit finder" en python?

import math 
i = 1 
counter = 0 
while counter <= 1000000: 
    MMM = int(math.log(i, 10)) + 1 
    counter = counter + MMM 
    V = math.log(i, 10) 
    print(i, counter, MMM, V) 
    i += 1 

Il est censé revenir le numéro contenant le chiffre Nième. Fondamentalement, ceci est supposé garder une trace de ce qui se passerait si je concatenais les entiers de 1 à n'importe quoi dans un autre nombre. Le but est de déterminer ce qu'est un chiffre spécifique. Ce code fonctionne en dessous d'un certain seuil, cependant, au moment où il arrive au millionième chiffre, il est désactivé par un. Qu'est-ce que j'oublie ici? J'ai vu d'autres implémentations qui font gagner du temps, mais je suis plus intéressé à savoir pourquoi le compte devient mauvais à un moment donné

Edit:

remplaçant

MMM = int(math.log(i, 10)) + 1 

avec

MMM = len(str(i)) 

œuvres comme un champion!

Bien que ce soit sympa d'avoir une solution entièrement numérique, il faudra attendre que je puisse faire confiance aux fonctions de journalisation en Python.

+0

grand, grand, grand indice: ne pas penser à cela comme un nombre fractionnaire, pensez à ce que un string. –

+0

Je me suis assez vite qu'il devrait être fait en utilisant des chaînes, mais après avoir compilé une chaîne assez longue pour contenir 1000000 chiffres je me suis dit qu'il devrait y avoir un moyen de « compte » dont le nombre en annexe a quel chiffre et l'indice dans ce une façon de savoir quels chiffres étaient réellement représentés par le numéro en cours d'ajout. Le code que j'ai écrit fait cela, la valeur du compteur donne le numéro du dernier chiffre du numéro en cours d'ajout. Cependant, quelque part il y a une panne et le compteur est éteint par un. Je veux savoir pourquoi et quand cela arrive. – Jim

+0

Juste pour clarifier ce qui se passe derrière mes maths. La partie entière du journal commun de n'importe quel nombre est le nombre de chiffres moins un. (Pour autant que je sais ...) Donc, si j'ajoute un dans le journal commun des numéros successifs et garder une trace de ces valeurs acheter en gardant un total en cours d'exécution, le chiffre Nième doit être contenu par le nombre qui est ajoutée lorsque le compteur total est plus grand que N par exemple: quand le nombre 366 est ajouté il y a 987 chiffres, donc il contient les 988ème 989ème et 990ème chiffres. – Jim

Répondre

3

Je pense que c'est la ligne de problème

MMM = int(math.log(i, 10)) + 1 

Quelques exemples

>>> int(math.log(1000000, 10)) + 1 
6 
>>> int(math.log(1000001, 10)) + 1 
7 

Alors que je pense que vous vouliez vraiment

>>> len(str(1000000)) 
7 
>>> len(str(1000001)) 
7 

Modifier fait (comme vous le suggérez!) math.log10 semble être la meilleure solution et plus en li ne avec ce que vous écrit à l'origine

>>> int(math.log10(10000000)) 
7 
>>> int(math.log10(10000001)) 
7 
>>> int(math.log10(10**1000)) 
1000 
>>> int(math.log10(10**10000)) 
10000 
>>> int(math.log10(10**100000)) 
100000 

math.log10 doit conserver la précision meilleure que math.log(x, 10)

+0

en plus de ce que j'ai commenté ci-dessous, je ne savais pas que la fonction len() fonctionnait de cette manière sur les chaînes. J'étais sous l'impression que cela ne compterait que des éléments, ie len (str (100000)) serait 1 et non 6. Je garderai cela à l'esprit à partir de maintenant. – Jim

+0

il ne fait compte que des éléments, mais les chaînes sont vraiment des listes de caractères. pour obtenir un résultat de 1, vous devez compter une liste d'une chaîne: len ([str (100000)]) – hop

4

Erreur de virgule flottante quelque part sur le chemin? Il est possible qu'à un certain point, math.log renvoie une valeur qui est à peine inférieure (ou supérieure, selon la direction de votre résultat off-by-1) à une limite entière et que int() la tronque à la valeur incorrecte. Les nombres à virgule flottante ne sont pas précis pour les nombres qui ne peuvent pas être représentés en utilisant un certain nombre de chiffres binaires.

+2

Cela semble raisonnable. Puisque les valeurs de math.log seront de plus en plus proches des valeurs entières à mesure que le nombre de chiffres augmente. En d'autres termes, log (999999) a un "plus" neuf dans sa partie décimale que log (99). Y a-t-il un moyen de lutter contre cela ou suis-je à la merci de la nature des points flottants, peu importe ce que avec cette méthode? – Jim

+0

@ Jim: Je pense que vous avez raison, et en utilisant 'sujets log' vous l'inexactitude à virgule flottante dans les cas d'angle. Certainement une façon unique de résoudre, cependant. –

1

La question ici est d'une précision des nombres à virgule flottante qui n'est pas étonnant. Je deviens plus flou et plus flou lorsque le nombre de chiffres augmente. C'est la nature de base des nombres à virgule flottante, quand vous faites ce genre de maths, vous devez être conscient de la précision dont vous avez besoin.

Le module de bibliothèque standard decimal vous permet un contrôle précis de la précision des valeurs décimales. Puisque ce module n'est pas des bases matérielles, l'utilisateur peut modifier la précision du nombre à virgule flottante (la valeur par défaut est 28 places).Vous créez des nombres décimaux comme ci-dessous:

import decimal 
x = decimal.Decimal(1000000) 
x.log10() 

Vous pouvez modifier la précision dont vous avez besoin comme ceci: decimal.getcontext().prec = 8

Questions connexes