原题

蓝桥杯备战日志(Python)3-货物摆放(枚举、因子分解)_整除

暴力枚举因子

首先明确“因子”的概念:对一个正整数n,若正整数i能够整除n,则i称为n的一个因子。很容易得知,因子是“对称的”,即对于n的因子i,有n//i (n整除i)的结果也是n的因子,所以给定一个正整数n,可以在√n的时间复杂度内使用“试除法”求出n的所有因子,代码如下:

# 暴力枚举,运行时间12秒左右

n=2021041820210418
# 存放因子的列表
factor_l = [1,n]

# 求n的因子的时间复杂度为n**0.5(根号n)
t = int(n ** 0.5) + 1
for i in range(2,t):
d,m = divmod(n,i)
if m == 0:
factor_l.append(i)
factor_l.append(d)

# 枚举满足条件的L,W,H
res_s = set() # 使用集合筛掉一些重复的情况
for L in factor_l:
for W in factor_l:
temp = L*W
H,m = divmod(n,temp)
if m == 0:
res_s.add( (L,W,H) )

print(len(res_s))

       蓝桥杯比赛中能做出以上的暴力枚举即可,时间复杂度不算很高,是能够拿下的!


枚举优化——质因数分解

本题可以使用数学方法对枚举进行优化,在此之前需要回顾一下质数和合数的一些概念:

  • 在所有正整数中(1,2,3,4,5,6,7,8,...)
  • 1既不是质数也不是合数
  • 质数:只能被1和它本身整除的数
  • 合数:除质数和1外的所有自然数
  • 相关定理
  • 一个合数等于有限个质数的乘积 (更严格的说,应该是如下基本定理的推论:任何一个大于1的正整数都能分解为有限个质数的幂的乘积

一个正整数n(n>1),若它本身不是质数,则其可以被分解为有限个质数(质因数)相乘。求出n的质因数后,可通过其质因数快速地求出n的所有因子,所以这里相对于上述暴力枚举的优化在于,更快地求出n的所有因子,n越大算法效率提升的效果越明显。

这里再作进一步说明为什么通过质因数求n的所有因子效率会更高。首先,在纯暴力枚举中,求n得所有因子需要遍历√n范围内的每个整数作除法求余数,而通过先求质因数(尽管n很大,其质因数的数量也很少)再求其所有因子的方法,将程序作除法的操作大大缩减(计算机中作乘法比作除法的效率更高)。


  • n的质因数分解
  • 从i=2开始枚举,若 n % i != 0,则i += 1继续往下枚举
  • 当 n % i == 0 成立时,n中已经不包含任何2 ~ (i - 1)的质因子,又因为此时n是i的倍数,所以i中也不包含2 ~ (i - 1)的因子,所以此时i一定是质数
  • 这里的一个关键是当 n % i == 0时,n的取值应更新为n//i
  • 因为我们最终求得的质因数列表的所有元素的乘积为最初的n
  • 如求得100的质因数列表为[2,2,5,5],有2*2*5*5=100
  • 通过质因数求n的因子
  • 初始化当前n的因子的集合(只有元素1)
  • 遍历求得的质因数列表,令每个元素(质因数)与当前已求得的因子作乘法,乘积结果加入存放因子的集合。

参考:​​https://www.acwing.com/blog/content/30069/​

# 对整数x进行质因数分解
def find_prime_factor(n: int):
prime_factor_l = []
i = 2
while i*i <= n:
d,m = divmod(n,i)
# i的起始值为2(质数),若当前x能被i整除,i添加至列表,并将x的值变为x//i,即d
# 这里反复利用了那个定理:一个合数能被分解成有限个质数相乘
if m == 0:
prime_factor_l.append(i)
n = d
else:
# 若i当前为2则下一个值为i+1, 否则为i+2,以更快地找到下一个质数
i = i+1 if i==2 else i+2
prime_factor_l.append(n)
return prime_factor_l

n = 2021041820210418
prime_factor_l = find_prime_factor(n)
s=set() # 用于存因子 如10=1*2*5*10
s.add(1)
# 通过质因数求出所有因子
for j in prime_factor_l:
p=set()
for k in s:
p.add(j*k)
for k in p:
s.add(k)
count = 0
for L in s: # 遍历两层求解
for W in s:
if n%(L*W)==0:
count += 1
print(count)


上一篇:​​蓝桥杯备战日志(Python)2-相乘(逆向枚举)​