题目描述:
一个数如果恰好等于不包含它本身所有因子之和,这个数就称为"完数"。 例如,6的因子为1、2、3,而6=1+2+3,因此6是"完数"。 编程序找出N之内的所有完数,并按下面格式输出其因子
输入格式:
N
输出格式:
? its factors are ? ? ?
样例输入:
1000
样例输出:
6 its factors are 1 2 3 28 its factors are 1 2 4 7 14 496 its factors are 1 2 4 8 16 31 62 124 248
代码思路:
我的想法是用遍历来求因数,如何在输出。可奈何他的超时了,时间复杂度为o(n**2)
def ys(n):
ys_list = []
for i in range(1,int(n/2)+1):
if n % i == 0:
ys_list.append(i)
return ys_list
def list_print(ys_list):
print(*ys_list,end=' ')
print()
N = int(input())
for i in range(3,N+1):
ys_list = ys(i)
sn = sum(ys_list)
if sn == i:
print(i,'its factors are',end=' ')
list_print(ys_list)
想了想决定在找因数(ys()函数)方面动手:
def ys(n):
ys_list = [1]
c = int(n**0.5) + 1
for j in range(2, c):
if n % j == 0:
ys_list.append(j)
if j != n // j: # 避免重复添加平方根的因子
ys_list.append(n // j)
ys_list.sort()
return ys_list
从n/2变成了n**2降低了时间复杂度
通过gpt得知了筛选法(Sieve of Eratosthenes)(先求和判断,再求因数)
完全数是满足其所有因子(除了自身)之和等于本身的数。我们可以利用筛选法来排除非完全数,从而减少计算量。
def find_perfect_numbers(N):
is_perfect = [False] * (N + 1)
for i in range(2, N + 1):
if not is_perfect[i]:
sum_factors = 1
j = 2
while j * j <= i:
if i % j == 0:
sum_factors += j
if j * j != i:
sum_factors += i // j
j += 1
if sum_factors == i:
is_perfect[i] = True
print(i, 'its factors are', end=' ')
print_factors(i)
def print_factors(n):
factors = []
for i in range(1, int(n/2) + 1):
if n % i == 0:
factors.append(i)
print(*factors, end=' ')
print()
N = int(input())
find_perfect_numbers(N)
在这个改进后的代码中,我们使用了一个布尔型的列表 is_perfect
来记录每个数是否是完全数。当遍历到某个数 i
时,如果它不是完全数,则对其进行因子求和计算,然后标记为完全数。
在计算因子和时,我们只需要迭代到 sqrt(i)
,而不是 n/2
。这是因为大于 sqrt(i)
的因子可以通过小于 sqrt(i)
的因子来计算得出。
这种算法的时间复杂度主要由 find_perfect_numbers()
函数中的嵌套循环决定,即约为 O(N log(log N)),其中 N 是输入的整数。
总结:
因数的一些查找方法
-
暴力遍历法:
- 方法:从 1 遍历到 n 的一半,检查每个数是否是 n 的因子。
- 时间复杂度:O(n)
-
利用数学特性优化法:
- 方法:从 1 遍历到 sqrt(n),检查每个数是否是 n 的因子,并利用对称性同时找到另一半的因子。
- 时间复杂度:O(sqrt(n))
-
分解质因数法:
- 方法:通过不断地除以最小质因数,将一个数分解为质数的乘积,进而找到因数。
- 时间复杂度:取决于最小质因数的大小,但通常可以近似为 O(log n)
-
素数筛法:
- 方法:使用埃拉托斯特尼筛法等方法预先计算出一定范围内的所有质数,然后通过组合这些质数得出所有因数。
- 时间复杂度:O(n log log n)(埃拉托斯特尼筛法)