阶乘最右连续0的数量
题目1. N!末尾连续0的数量
题目1. 给定一个非负整数N,返回N!结果的末尾连续0的数量。
例如:
3!=6,结果的末尾没有0,则返回0。
5!=120,结果的末尾有1个0,返回 1。
1000000000!,结果的末尾有249999998个0,返回249999998。
解法1
算法思路:末尾结尾连续0数量,则需计算阶乘因子中拆分为10的数量。而10 = 2 * 5, 又2的数量多于5的数量,因此转换为求因子5的数量。
计算N中5i项的因子数量,求和
e.g. 26! = 1 * 2 * 3 *… * 26,求末尾为0的数量,即求因子中10的数量,又10 = 2 * 5,而因子2的数量,因此我们只需要求因子5的数量和,而因子当且仅当出现在5,10,15,20,25中。
算法时间复杂度为 O(NlogN)。
def zeroNum1(num):
if num < 0:
return 0
res = 0
for i in range(5, num+1, 5):
temp = i
while temp % 5 == 0:
res += 1
temp /= 5
return res
解法2
解题思路:求因子5总数,可以用 N // 5 + N // 25 + … + N //
5
i
5^i
5i(
5
i
5^i
5i恰好大于N )
本质在求能整除5的个数,能整除25的个数,…,能整除
5
i
5^i
5i的个数,再求和相加。
Z
=
N
5
+
N
5
2
+
N
5
3
+
.
.
.
+
N
5
i
(
此
处
的
除
法
运
算
为
整除运算
,
5
i
恰
好
≥
N
)
Z = \frac{N}{5} + \frac{N}{5^2} + \frac{N}{5^3} + ... + \frac{N}{5^i} ( 此处的除法运算为\textbf{整除运算},5^i恰好 \geq N )
Z=5N+52N+53N+...+5iN(此处的除法运算为整除运算,5i恰好≥N)
e.g. 26! = 1 * 2 * 3 *… * 26,5有1个因子5,25有两个因子5,在整除5的时候加5(5,10,15,20,25分别1次),再整除25的时候加1(25加一次),总共6次(末尾6个0)。
注意:25分别在整除5和整除25时计数了一次,总共两次,完全统计了因子5的数量。
算法时间复杂度为O(logN)。
def zeroNum2(num):
if num < 0:
return 0
res = 0
while num != 0:
num = num // 5
res += num
return res
# 简单测试
if __name__ == '__main__':
# 题目1 阶乘结果的末尾连续0的数量
print(zeroNum2(3)) # 0
print(zeroNum2(5)) # 1
print(zeroNum2(1000000000)) # 249999998
题目2. N!二进制化最低位的1所在位置
题目2. 给定一个非负整数N,用二进制数表达N!的结果,若最右的位置为位置0,返回最低位的1所在位置。
例如:
1!=1,最低位的1在0位置上。
2!=2,最低位的1在1位置上。
1000000000!,最低位的1在999999987位置上。
解法1
算法思路:将10进制改为了2进制(因子10(实际为5)转变成因子2),求因子2总数。
Z = N 2 + N 2 2 + N 2 3 + . . . + N 2 i ( 此 处 的 除 法 运 算 为 整除运算 , 2 i 恰 好 ≥ N ) Z = \frac{N}{2} + \frac{N}{2^2} + \frac{N}{2^3} + ... + \frac{N}{2^i} ( 此处的除法运算为\textbf{整除运算},2^i恰好 \geq N ) Z=2N+22N+23N+...+2iN(此处的除法运算为整除运算,2i恰好≥N)
算法时间复杂度为O(logN)。
def rightOne1(num):
if num <= 0:
return -1
res = 0
while num != 0:
num //= 2
res += num
return res
# 简单测试
if __name__ == '__main__':
# 题目2 阶乘结果转换为2进制的最低位1的位置(最右位为0,从右边计数位置,)
print(rightOne1(1)) # 0
print(rightOne1(2)) # 1
print(rightOne1(1000000000)) # 999999987
解法2
算法思路:更特殊的规律—— 最低位1的位置为进行阶乘运算的数减去其二进制1的个数。
即Z = N - M,Z为最低位1的位置(因子2总数),N为进行阶乘运算的数,M为N转换为2进制后1的个数。
N
=
2
n
1
+
2
n
2
+
.
.
.
+
2
n
M
∵
Z
=
N
2
+
N
2
2
+
N
2
3
+
.
.
.
+
N
2
i
(
此
处
的
除
法
运
算
为
整除运算
,
2
i
恰
好
≥
N
)
将
N
带
入
上
式
,
对
于
每
一
项
2
n
m
,
则
有
2
n
m
−
1
+
2
n
m
−
2
+
.
.
.
+
1
+
0
+
.
.
.
=
2
n
m
−
1
∴
Z
=
2
n
1
−
1
+
2
n
2
−
1
+
.
.
.
+
2
n
M
−
1
=
2
n
1
+
2
n
2
+
.
.
.
+
2
n
M
−
M
=
N
−
M
即
最
低
位
1
的
位
置
为
进
行
阶
乘
运
算
的
数
减
去
其
二
进
制
1
的
个
数
。
N = 2^{n_1} + 2^{n_2} + ... + 2^{n_M} \\ \because Z = \frac{N}{2} + \frac{N}{2^2} + \frac{N}{2^3} + ... + \frac{N}{2^i} ( 此处的除法运算为\textbf{整除运算},2^i恰好 \geq N ) \\ 将N带入上式,对于每一项2^{n_m},则有2^{n_m-1} + 2^{n_m-2} + ... + 1 + 0 + ...= 2^{n_m}-1 \\ \therefore Z = 2^{n_1} - 1 + 2^{n_2} -1 + ... + 2^{n_M} - 1 = 2^{n_1} + 2^{n_2} + ... + 2^{n_M} - M = N - M \\ 即最低位1的位置为进行阶乘运算的数减去其二进制1的个数。
N=2n1+2n2+...+2nM∵Z=2N+22N+23N+...+2iN(此处的除法运算为整除运算,2i恰好≥N)将N带入上式,对于每一项2nm,则有2nm−1+2nm−2+...+1+0+...=2nm−1∴Z=2n1−1+2n2−1+...+2nM−1=2n1+2n2+...+2nM−M=N−M即最低位1的位置为进行阶乘运算的数减去其二进制1的个数。
算法时间复杂度为O(logN)。
def rightOne2(num):
if num <= 0:
return -1
binary_num = 0
temp = num
while temp != 0:
binary_num += temp % 2
temp //= 2
return num - binary_num
# 简单测试
if __name__ == '__main__':
# 题目2 阶乘结果转换为2进制的最低位1的位置(最右位为0,从右边计数位置,)
print(rightOne2(1)) # 0
print(rightOne2(2)) # 1
print(rightOne2(1000000000)) # 999999987
小结
数值末尾连续0数量(题目1),与不为0的最低位(题目2)本质相同,只是因子不同,所表现的进制数不同。
有任何疑问和建议,欢迎在评论区留言和指正!
感谢您所花费的时间与精力!