我的Python数字查找器有什么问题?

2 投票
3 回答
1006 浏览
提问于 2025-04-15 16:29

我正在通过解决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) 要好

撰写回答