应用highly composite number 的规则,您可以将数字分解为其主要因素,从而获得良好的性能。
- 所有素数必须至少按顺序使用一次
- 最后一个素数的幂必须是 1
- 质因数的幂必须是非递增顺序
- 如果满足以上所有条件,请检查其他素因数组合,这些组合可以产生更多(或相等)除数和更小的乘积。
- 特殊情况:4 和 36 是反素数
无限素数生成器(您可以根据需要制作自己的,或者只使用前 100 个素数的硬编码列表):
primes = [2,3]
skips = {9:6}
def getPrimes(count=-1):
yield from primes[:max(0,count) or None]
p = primes[-1]+2
while count:
if p in skips:
mult,step = p,skips.pop(p)
mult += step
while mult in skips: mult += step
skips[mult] = step
else:
skips[p*p] = 2*p
primes.append(p)
yield p
count -= 1
p += 2
反素检查功能:
from math import log
def isAntiPrime(N):
if N in [1,4,36]: return True # special cases
primeGen = getPrimes() # infinite primes generator
factors = [] # prime factors
lastPower = N # to check non-ascending order
divCount = 1 # number of divisors
R = N # reduced N (by factors)
for prime in primeGen: # go through prime factors
factors.append(prime)
if R==1: break # until number exausted
if R%prime: return False # unused prime factor
power = 0 # Count power of prime factor
while R%prime == 0:
power += 1
R //= prime # reduce number as we go
if lastPower<power: return False # increasing order
lastPower = power
divCount *= power+1 # compute number of divisors
if lastPower != 1: return False # last prime's power must be 1
def canReduce(i=0,prod=1,count=1): # recursively look for smaller number
if count>=divCount and prod<N: return True # found one
while i>=len(factors): # load primes list
factors.append(next(primeGen)) # up to index
prime = factors[i] # factor at index
for n in range(1,int(log(N/prod,prime))+1): # try prime^n
prod *= prime
if canReduce(i+1,prod,count*(n+1)): # combined
return True
return not canReduce() # no better power combo -> anti-prime
输出:
i = 0
for n in range(1,1000000):
if isAntiPrime(n):
i += 1
print(f"{i:2}",n)
1 1
2 2
3 4
4 6
5 12
6 24
7 36
8 48
9 60
10 120
11 180
12 240
13 360
14 720
15 840
16 1260
17 1680
18 2520
19 5040
20 7560
21 10080
22 15120
23 20160
24 25200
25 27720
26 45360
27 50400
28 55440
29 83160
30 110880
31 166320
32 221760
33 277200
34 332640
35 498960
36 554400
37 665280
38 720720
其他测试:
for n in [166320,166322,498960,494851, 169733893,
180068938933390833890254309768093,
7, 244877512201, 51983595331405913494380649,
2473017995717899, 252222613252687371148099,
7426227904486559070625807, 24247699, 3463957,
3674876304763078242658251219757, 713928607, 70693, 16807,
24975008738755062001]:
print(isAntiPrime(n),n)
True 166320
False 166322
True 498960
False 494851
False 169733893
False 180068938933390833890254309768093
False 7
False 244877512201
False 51983595331405913494380649
False 2473017995717899
False 252222613252687371148099
False 7426227904486559070625807
False 24247699
False 3463957
False 3674876304763078242658251219757
False 713928607
False 70693
False 16807
False 24975008738755062001
请注意,我没有包含任何计时基准,因为这都是瞬时的,只需使用前 100 个素数,您就可以在不到一秒的时间内处理多达 190 位的数字
寻找反素数
isAntiPrime 函数可以通过利用其 canReduce() 函数调整为返回最大的反素数
def antiPrime(N):
if N in [1,4,36]: return N # special cases
primeGen = getPrimes() # infinite primes generator
factors = [] # prime factors
divCount = 1 # number of divisors
R = N # reduced N (by factors)
for prime in primeGen: # go through prime factors
factors.append(prime)
if R==1: break # until number exausted
if prime*prime>R: # when past square root,
prime = R # R is last prime
power = 0 # Count power of prime factor
while R%prime == 0:
power += 1
R //= prime # reduce number as we go
divCount *= power+1 # compute number of divisors
betterN = 0
def reduce(i=0,prod=1,count=1,lastPower=N): # look for smaller number
nonlocal divCount,betterN
if prod>=N: return
if count>divCount or count==divCount and prod<betterN:
divCount,betterN = count,prod # found better
while i>=len(factors): # load primes list
factors.append(next(primeGen)) # up to index
n,prime = 1,factors[i] # factor at index
while n<=lastPower and prod*prime<N: # try prime^n
prod *= prime
reduce(i+1,prod,count*(n+1),n) # combined
n += 1
reduce()
return betterN or N
输出:
for n in (1000000,713928607,2473017995717899,24975008738755062001,
180068938933390833890254309768093):
print(n,"anti-prime:",antiPrime(n))
1000000 anti-prime: 720720
713928607 anti-prime: 698377680
2473017995717899 anti-prime: 2021649740510400
24975008738755062001 anti-prime: 18401055938125660800
180068938933390833890254309768093 anti-prime: 156839524845080402008057229856000
由于过程的组合性质,这对于非常大的数字会有点慢。