求两个数的最大公约数

阶乘最右连续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整除运算5iN
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整除运算2iN

算法时间复杂度为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+...+2nMZ=2N+22N+23N+...+2iN整除运算2iNN2nm2nm1+2nm2+...+1+0+...=2nm1Z=2n11+2n21+...+2nM1=2n1+2n2+...+2nMM=NM11

算法时间复杂度为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)本质相同,只是因子不同,所表现的进制数不同。

有任何疑问和建议,欢迎在评论区留言和指正!

感谢您所花费的时间与精力!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值