Python算法:数论问题

目录

完全数

水仙花数

素数

回文素数

平方回文数

分解质因数

最大公约数

最小公倍数

Stein 算法求最大公约数

小结


数论是研究整数及其性质的数学学科。在数论中,研究的对象主要是整数、质数、素数、约数、同余、模运算等基本概念和性质。数论的问题简单而又充满了智慧,本文将探讨一些基本的数论问题和算法。

完全数

完全数指的是一个正整数,它的所有因子之和等于它本身,而因子不包括该数本身。一个数的因子就是所有可以整除这个数的数,而不包括该数本身。可以通过完全数的定义来编写查找完全数的算法。计算一个数的所有因子之和,判断与其是否相等。

目前并没有找到一种高效的方法来直接计算任意大数是否为完全数。已知的完全数非常稀少,而且它们的规律并不明确。随着数字的增大,它的因子数量也随之增多,计算过程会变得更加耗时,越往后查找完全数的速度越慢。为避免造成“程序失去响应”的误会,本案例会输出当前正在判断的数字,并且覆盖已经输出的非完全数。

# 判断一个数是否完全数,如果是完全数,返回因子列表
def perfect_number(number):
    factor_sum = 0
    factors = []
    # 从 1 开始,逐个判断是否是 number 的因子,不包括 number 自身
    for num in range(1, number):
        #  如果 num 是 number 的因子,则累加,并将 num 加入因子列表
        if number % num == 0:
            factor_sum += num
            if factor_sum > number:
                # 如果因子之和已经大于 number,说明 number 不是完全数,直接返回
                return False, []
            factors.append(num)

    # 上面的代码可以使用列表推导式简化,为了方便理解,这里不使用列表推导式
    # factors = [num for num in range(1, number // 2 + 1) if number % num == 0]
    # factor_sum = sum(factors)

    if factor_sum == number:
        # 如果因子之和等于 num,则 num 是完全数,同时返回因子列表
        return True, factors
    else:
        # 否则,num 不是完全数,同时返回空列表
        return False, []


n = int(input("请输入一个正整数 n:"))
print("小于 %d 的完全数有:" % n)
for i in range(1, n):
    # 覆盖输出,不换行
    print("\r%d" % i, end="")
    # 判断 i 是否是完全数,如果是,则输出完全数及其因子
    is_perfect, pn = perfect_number(i)
    if is_perfect:
        print(" = ", end="")
        # 用 + 号连接完全数的因子
        print(" + ".join(map(str, pn)))

# 输出 n 的宽度个空格,覆盖最后一个非完数的输出
print("\r%s" % " " * len(str(n)), end="")

水仙花数

水仙花数(也称为自恋数、自幂数)指的是一个 n 位的正整数(n>=3),它的每个数字的 n 次幂之和等于它本身。

from math import log10


# 判断一个数是否水仙花数
def narcissus_number(num):
    factor_sum = 0
    temp = num
    # 求 num 的位数
    num_len = int(log10(num)) + 1

    # 计算 num 的各位数字的 n 次方和
    while temp > 0:
        # 取出 temp 的个位数字
        digit = temp % 10
        # 累加 digit 的 n 次方
        factor_sum += digit ** num_len
        # 去掉 temp 的个位数字
        temp //= 10

    return factor_sum == num


n = int(input("请输入一个正整数 n:"))
print("小于 %d 的水仙花数有:" % n)
for i in range(100, n):
    if narcissus_number(i):
        print(i, end=" ")

素数

素数又称质数,指在一个大于 1 的自然数中,除了 1 和此自然数自身外,无法被其它自然数整除的数。素数的分布没有明显的规律,一般根据素数的定义来分析数值不大的数是否为素数。

# 判断一个数是否素数
def is_prime_number(number):
    # 素数定义为大于 1 的自然数,所以小于等于 1 的数都不是素数
    if number <= 1:
        return False
    # 从 2 开始,逐个判断是否能被 num 整除,不包括 num 自身
    for i in range(2, number):
        if number % i == 0:
            # 如果能被整除,则 num 不是素数
            return False
    # 如果不能被整除,则 num 是素数
    return True


n = int(input("请输入一个正整数 n:"))
m = int(input("每行输出多少个数:"))

# 输出素数宽度为 n 的宽度+1
width = len(str(n)) + 1

line_nums = 0
print("小于 %d 的素数有:" % n)
for i in range(2, n):
    if is_prime_number(i):
        # 输出素数,每个数占 width 个字符宽度
        print("%*d" % (width, i), end="")
        # 每行输出 m 个数
        line_nums += 1
        if line_nums % m == 0:
            print()

回文素数

回文素数指的是一个既是回文数又是素数的正整数。回文数是指正着读和倒着读都一样的数字。判断回文素数也从其定义出发,复用上一案例的素数判断方法,本案例增加方法判断是否为回文数。

# 判断一个数是否回文数
def is_palindrome(number):
    num, reverse = number, 0
    while num > 0:
        # 将 reverse 左移一位,然后加上 num 的个位数字
        reverse = reverse * 10 + num % 10
        # 去掉 n 的个位数字
        num //= 10
    return number == reverse


# 判断一个数是否素数
def is_prime_number(number):
    # 素数定义为大于 1 的自然数,所以小于等于 1 的数都不是素数
    if number <= 1:
        return False
    # 从 2 开始,逐个判断是否能整除 number,不包括 number 自身
    for i in range(2, number):
        if number % i == 0:
            # 如果能被整除,则 number 不是素数
            return False
    # 如果不能被整除,则 number 是素数
    return True


n = int(input("请输入一个正整数 n:"))
print("小于 %d 的回文素数有:" % n)
for i in range(2, n):
    if is_palindrome(i) and is_prime_number(i):
        print(i, end=" ")

平方回文数

平方回文数是一个回文数,这个回文数可以表示成某个自然数的平方的形式。

# 判断一个数是否回文数
from math import sqrt


def is_palindrome(number):
    num, reverse = number, 0
    while num > 0:
        # 将 reverse 左移一位,然后加上 num 的个位数字
        reverse = reverse * 10 + num % 10
        # 去掉 n 的个位数字
        num //= 10
    return number == reverse


n = int(input("请输入一个正整数 n:"))
print("小于 %d 的平方回文数有:" % n)
for i in range(1, int(sqrt(n))):
    if is_palindrome(i * i):
        print("%d = %d * %d" % (i * i, i, i))

分解质因数

分解质因数是指将一个合数分解成若干个质数的乘积的过程。合数是指在大于 1 的整数中除了能被 1 和本身整除外,还能被其他数整除的数。质数即前面提到的素数,是只能被 1 和自身整除的正整数。分解质因数的过程可以用试除法实现,具体步骤如下:

1. 从 2 开始,依次尝试将待分解的数除以 2,如果可以整除,则将 2 作为一个质因子,继续将商分解成质因数;

2. 如果商不可以被 2 整除,则尝试将其除以 3,如果可以整除,则将 3 作为一个质因子,继续将商分解成质因数;

3. 依此类推,直到商为 1,分解过程结束。

# 分解质因数
def factorize(number):
    factors = []
    i, num = 2, number
    while num > 1:
        # 如果能被整除,则 i 是 n 的质因子
        if num % i == 0:
            factors.append(i)
            # 去掉 n 的因子 i
            num //= i
        else:
            i += 1

    return factors


n = int(input("请输入一个正整数 n:"))
# 输出 n 的质因数,质因数之间用 * 号连接
print("%d = " % n, end="")
print(" * ".join(map(str, factorize(n))))

最大公约数

最大公约数是指两个或多个整数的公共因数中最大的一个。可以用辗转相除法来求两个数的最大公约数。已知两自然数 m、n,辗转相除法求最大公约数执行过程如下:

1. 计算 m 除以 n 的余数 r;

2. 如果 r==0,则 n 为求得的最大公约数,否则执行下一步;

3. 将 n 的值保存到 m 中,将 r 的值保存到 n 中,重复执行步骤 1 和 2,直到 r==0 得到最大公约数。

上述过程可以通过递归来实现:

1. 如果 n 是 0,则 m 是最大公约数;

2. 递归调用计算 n 和 r(即 m % n)两数的最大公约数。

由于是取模运算,m 和 n 谁大谁小没有关系,如果 m<n,执行一遍上述步骤后,m 和 n 的值就交换过来了,然后正式进入辗转相除的流程。

# 辗转相除法求最大公约数
def gcd(m, n):
    if n == 0:
        return m
    else:
        return gcd(n, m % n)


# 求最小公倍数
def lcm(m, n):
    return m * n // gcd(m, n)


# 输入两个正整数,以空格分隔
str1, str2 = input("请输入两个正整数:").split()
num1, num2 = int(str1), int(str2)
print("它们的最大公约数是: %d" % gcd(num1, num2))
print("它们的最小公倍数是: %d" % lcm(num1, num2))

最小公倍数

最小公倍数是两个数共有的倍数中最小的一个。计算两个数的最小公倍数的方法比较简单,先计算它们的最大公约数,然后用两个数的乘积除以它们的最大公约数,便可得到两数的最小公倍数。相关代码见上一案例。

Stein 算法求最大公约数

还可以使用Stein算法来求两个数的最大公约数,该算法相比前面的辗转相除法,使用位移和加减运算,且递归深度更小,效率更高。该算法如下:

# Stein 算法求最大公约数
def gcd_stein(a, b):
    # m 是两个数中较大的那个,n 是较小的那个
    if a > b:
        m, n = a, b
    else:
        m, n = b, a

    # 如果其中一个数为0,另一个数就是它们的最大公约数
    if n == 0:
        return m

    # 如果两个数都是偶数,则公约数是 m/2 和 n/2 的最大公约数的两倍
    if m & 1 == 0 and n & 1 == 0:
        return 2 * gcd_stein(m // 2, n // 2)
    # 如果 m 是偶数,n 是奇数,则公约数是 m 的一半和 n 的最大公约数
    if m & 1 == 0:
        return gcd_stein(m // 2, n)
    # 如果 m 是奇数,n 是偶数,则公约数是 m 和 n 的一半的最大公约数
    if n & 1 == 0:
        return gcd_stein(m, n // 2)
    # 如果两个数都是奇数,则公约数是 (m+n)/2 和 (m-n)/2 的最大公约数
    return gcd_stein((m + n) >> 1, (m - n) >> 1)


# 输入两个正整数,以空格分隔
str1, str2 = input("请输入两个正整数:").split()
num1, num2 = int(str1), int(str2)
print("它们的最大公约数是: %d" % gcd_stein(num1, num2))

小结

当探索数论问题时,我们遇到了许多有趣的概念和算法。我们了解了完全数、水仙花数、回文素数、平方回文数等等。我们还学习了一些常见的数论算法,如分解质因数、最大公约数、最小公倍数等。

数论作为数学的一个分支,不仅令人着迷,而且具有广泛的应用。它在密码学、编码理论、计算机科学等领域起着重要的作用。通过深入研究数论问题,我们能够培养出抽象思维、逻辑推理和问题解决能力。

希望本文对读者有所启发,增加对数论问题的兴趣和理解。数论世界广阔而精彩,还有许多有待探索的领域和问题。无论是学术研究还是日常生活中的应用,数论都是一个充满挑战和奇妙的领域,值得我们深入探索。祝愿大家在数论的世界中找到乐趣,并不断拓展自己的知识和思维。

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创意程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值