在Python中,如何找到一个整数的数字个数?
```
```
在Python中,如何找到一个整数的数字个数?
```为了记录,毫无疑问这是解决这个问题最慢的方法:
def num_digits(num, number_of_calls=1):
"Returns the number of digits of an integer num."
if num == 0 or num == -1:
return 1 if number_of_calls == 1 else 0
else:
return 1 + num_digits(num/10, number_of_calls+1)
log10
会导致大数字n
的错误结果,而使用len(str(...))
或手动循环会导致大数字n
的性能下降。Jodag's answer提供了一个非常好的替代方法,不过它只适用于可能会导致计算机崩溃的整数。但我们可以做得更好,还可以更快(对于小到足以保证math.log2
准确的n
),避免使用对数并改用二进制:def num_digits(n: int) -> int:
assert n > 0
i = int(0.30102999566398114 * (n.bit_length() - 1)) + 1
return (10 ** i <= n) + i
让我们来分析一下。首先有一个奇怪的n.bit_length()
。这个函数计算二进制长度:
assert 4 == (0b1111).bit_length()
assert 8 == (0b1011_1000).bit_length()
assert 9 == (0b1_1011_1000).bit_length()
floor(log2(n)) + 1
的结果。为了单独获得 floor(log2(n))
,我们需要减去 1
,因此得到 n.bit_length() - 1
。0.30102999566398114
。这相当于略微向下舍入的 log10(2)
。这利用对数规则从 floor(log2(n))
计算出 floor(log10(n))
的估计值。0.30102999566398114 * log2(n) ~ log10(n)
,但对于 floor(0.30102999566398114 * floor(log2(n))) ~ floor(log10(n))
却不成立。请记住 x - 1 < floor(x) <= x
,因此我们可以进行一些快速的数学运算:log2(n) - 1 < floor(log2(n)) <= log2(n)
log10(n) - 0.30102999566398114 < 0.30102999566398114 * floor(log2(n)) <= log10(n)
floor(log10(n) - 0.30102999566398114) < floor(0.30102999566398114 * floor(log2(n))) <= floor(log10(n))
floor(log10(n) - 0.30102999566398114)
至少是 floor(log10(n)) - 1
,这意味着我们的结果最多只会偏差 1
。这就是最终校正的地方,我们检查 10 ** i <= n
,如果结果太小,则会得到额外的 1 +
,如果结果恰好,则会得到 0 +
。n
(大约是 10 ** 2 ** 52
)会失败,其中 i
的偏差超过了 -1
。但是,那么大的整数可能会导致计算机崩溃,因此这应该足够了。from math import log10
digits = lambda n: ((n==0) and 1) or int(log10(abs(n)))+1
将科学计数法转换为普通数字并去掉指数:
int("{:.5e}".format(1000000).split("e")[1]) + 1
我不知道速度如何,但它很简单。
请注意小数点后的有效数字位数(在科学计数法中,".5e"中的“5”可能会将小数部分四舍五入到另一个数字。我将其任意设置为较大值,但可以反映出您所知道的最大数字的长度。
通过使用以下方法,可以快速地完成整数运算:
len(str(abs(1234567890)))
如何获取绝对值为"1234567890"的字符串长度?
abs
返回不带负号的数字(仅数字的大小),str
将其转换为字符串,len
返回该字符串的长度。
如果您想使其适用于浮点数,可以使用以下任一方法:
# Ignore all after decimal place
len(str(abs(0.1234567890)).split(".")[0])
# Ignore just the decimal place
len(str(abs(0.1234567890)))-1
供将来参考。
int
)比截断它的十进制字符串表示更简单: len(str(abs(int(0.1234567890))))
返回 1。 - David Foersterdef length(i):
return len(str(i))
def digits(n)
count = 0
if n == 0:
return 1
if n < 0:
n *= -1
while (n >= 10**count):
count += 1
n += n%10
return count
print(digits(25)) # Should print 2
print(digits(144)) # Should print 3
print(digits(1000)) # Should print 4
print(digits(0)) # Should print 1
from math import fabs
len(format(fabs(100),".0f"))
Out[102]: 3
len(format(fabs(1e10),".0f"))
Out[165]: 11
len(format(fabs(1235.4576),".0f"))
Out[166]: 4
我进行了一项简要的基准测试,循环10,000次
num len(str(num)) ---- len(format(fabs(num),".0f")) ---- speed-up
2**1e0 2.179400e-07 sec ---- 8.577000e-07 sec ---- 0.2541
2**1e1 2.396900e-07 sec ---- 8.668800e-07 sec ---- 0.2765
2**1e2 9.587700e-07 sec ---- 1.330370e-06 sec ---- 0.7207
2**1e3 2.321700e-06 sec ---- 1.761305e-05 sec ---- 0.1318
这是一种速度较慢但更简单的选择。
但是即使使用这种解决方案,也会在9999999999999998处产生错误的结果。
len(format(fabs(9999999999999998),".0f"))
Out[146]: 16
len(format(fabs(9999999999999999),".0f"))
Out[145]: 17
这是一种快速的解决方案,它使用了基于“Better way to compute floor of log(n,b) for integers n and b?”的自校正实现的floor(log10(n))
。
import math
def floor_log(n, b):
res = math.floor(math.log(n, b))
c = b**res
return res + (b*c <= n) - (c > n)
def num_digits(n):
return 1 if n == 0 else 1 + floor_log(abs(n), 10)
这非常快,只要n < 10**(2**52)
(非常非常大),就可以使用。