一.前提
今天上离散数学课上看到一个题目:使用包含排斥原理求不超过120的素数个数,其实按照一般算法是这样的:
count = 0
l = []
for x in range(121):
#判断如果x是素数,则打印,如果不是素数就跳过
if x <2:
continue
for i in range(2,x):
#如果能在2到该数的范围内找出一个数整除,就停止(相当于此数为合数),继续判断下一个数
if x % i == 0:
break
else:
l.append(x)
count = count+1
print(l)
print(count)
这个方法就是一个一个判断,是毫不遗漏地找出全部素数,理解较简单。
而包含排斥原理解释如下图:
二.解题思路
今天讲的包含排斥原理求120以内素数个数,按照这个思维,我们只要找出120以内合数即可。11*11=121>120,可以推出120以内任意一个合数它的因子必定为{2,3,5,7}里的一个。
设集合Ai(i = 2,3,5,7)是120内能整除2,3,5,7这些数的集合。(手打太麻烦 嘿嘿(滑稽))
三.包含排斥法代码实现
N = 120
countA = int(N/2) #|A| 能被2整除的数的个数
countB = int(N/3) #|B| 能被3整除的数的个数
countC = int(N/5) #|C| 能被5整除的数的个数
countD = int(N/7) #|D| 能被7整除的数的个数
countAB = int(N / (2 * 3)); # | AB | 能同时被2,3整除的数的个数
countAC = int(N / (2 * 5)); # | AC | 能同时被2,5整除的数的个数
countAD = int(N / (2 * 7)); # | AD | 能同时被2,7整除的数的个数
countBC = int(N / (3 * 5)); # | AB | 能同时被3,5整除的数的个数
countBD = int(N / (3 * 7)); # | AB | 能同时被3,7整除的数的个数
countCD = int(N / (5 * 7)); # | AB | 能同时被5,7整除的数的个数
countABC =int( N / (2 * 3 * 5)); # | ABC | 能同时被2,3,5整除的数的个数
countABD = int(N / (2 * 3 * 7)); # | ABD | 能同时被2,3,7整除的数的个数
countACD = int(N / (2 * 5 * 7)); # | ABD | 能同时被2,5,7整除的数的个数
countBCD = int(N / (3 * 5 * 7)); # | ABD | 能同时被3,5,7整除的数的个数
countABCD =int(N / (2 * 3 * 5 * 7)); # | ABCD | 能同时被2,3,5,7整除的数的个数
#包含排斥原理
S = int(N - (countA + countB + countC + countD)
+ (countAB + countAC + countAD + countBC + countBD + countCD)
- (countABC + countABD + countACD + countBCD)
+ countABCD)+4-1
# 加4是原来的2,3,5,7这四个数也是质数,减1是1既不是质数也不是合数
print(S)
#属于估值法,适用于N不是特别大的情况,当N足够大时,素因子集合过大,本算法运算起来将会复杂。
四.改正
经过网友指正改正了答案,最后公式求出来的是~(A2∪A3∪A5∪A7),即除去这四个集合的部分。但通过韦恩图可以看出,2,3,5,7本身也是质数,1既不是质数也不是合数,所以最后结果是27+4-1=30个。
主要是做题太拘泥于公式,没有理解到精髓是通过韦恩图来检查结果。