我的Python数字查找器有什么问题?
我正在通过解决Project Euler的问题来学习Python。对于第40题,我写了以下代码:
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
这段代码的目的是返回包含第N位数字的那个数字。简单来说,它是用来追踪如果我把从1开始的整数一个个拼接成一个新数字会发生什么。我的目标是找出某个特定数字是什么。不过,这段代码在某个范围内是有效的,但当它到达第百万位数字时,就会出现偏差,少了一位。我在想我漏掉了什么?我见过其他的实现方式能节省时间,但我更想知道为什么计数在某个时刻会出错。
编辑:
把
MMM = int(math.log(i, 10)) + 1
替换成
MMM = len(str(i))
效果很好!
虽然有一个全数字的解决方案会更好,但我得等到我能信任Python中的对数函数再说。
3 个回答
1
这里的问题是浮点数的精确度并不是很好。随着数字位数的增加,精确度会变得越来越模糊。这是浮点数的基本特性,所以在进行这种数学运算时,你需要清楚自己需要多高的精确度。
标准库中的 decimal 模块可以让你更好地控制小数值的精确度。因为这个模块不是基于硬件的,所以用户可以调整浮点数的精确度(默认是28位)。你可以像下面这样创建小数:
import decimal
x = decimal.Decimal(1000000)
x.log10()
你可以这样调整所需的精确度:decimal.getcontext().prec = 8
4
在某个地方出现了浮点数错误吗?有可能在某个时刻,math.log
返回的值稍微低于(或者稍微高于,具体取决于你结果偏差的方向)一个整数的边界,因此 int()
把它截断成了错误的值。浮点数对于那些无法用固定数量的二进制位表示的数字来说,是不精确的。
3
我觉得这行代码可能是问题所在
MMM = int(math.log(i, 10)) + 1
这里有一些例子
>>> int(math.log(1000000, 10)) + 1
6
>>> int(math.log(1000001, 10)) + 1
7
不过我怀疑你其实想要的是这个
>>> len(str(1000000))
7
>>> len(str(1000001))
7
补充 实际上(正如你所提到的!)使用 math.log10
似乎是最好的解决方案,而且更符合你最初的想法
>>> 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
的精确度一定比 math.log(x, 10)
要好