可以发现我们可以用一个递推公式来计算, S(N=4) = S(N=3) +new_points
new_points是新加的点中没有被原来点挡住的点
第一步:
仔细想来,如何判断一个点会不会被原来的点挡住 --> 相当于判断这个点的两个坐标是不是互质的
点(8,4)很明显会被点(4,2),而点(8,7)和点(8,1)就不会被之前的点挡住
第二步:
其实新增的点只需要算一半就可以了,因为图形和点是关于x=y这条直线对称的,也就是说如果(8,1)没有被挡住,我们直接可以知道(1,8)也一定没有被挡住
这诞生了我们第一版的基于递推和互质的程序
def gcd(x,y):
if x > y:
smaller = y
else:
smaller = x
for i in range(1,smaller + 1):
if((x % i == 0) and (y %i ==0)):
hcf = i
return hcf
count = 3
for i in range(2,N+1):
new_points = 0
for j in range(1,i):
if gcd(i,j) == 1:
new_points = new_points + 1
count = count + 2*new_points
S = count
print(S)
相当于当从n到n+1,我们遍历一遍(新加入的点的一半),然后算出有几个互质的坐标对,然后乘以2加到S(n)
第二版,我们将求最大公因数的步骤换成辗转相除法:
def gcd(x,y):
if x == y:
#print(x)
return x
else:
if x > y:
x = x - y
else:
y = y - x
return gcd(x,y)
count = 3
new_points = 0
for i in range(2,N+1):
for j in range(1,i):
if gcd(i,j) == 1:
new_points += 1
S = count + 2*new_points
print(S)
考虑一下,怎么简化gcd函数,其实不需要递归,只需要一个循环既可
def gcd(a, b):
while b:
a, b=b, a%b
return a
第四版,我们思考能不能不遍历所有的点,这时候考虑用欧拉公式。
即我们直接算出比n小且和n互质的正整数的个数,即我们一直想求的new_points的值。用欧拉公式我们不需要遍历:
import math
N=1000
def phi(n):
amount = 0
for k in range(1, n + 1):
if math.gcd(n, k) == 1:
amount += 1
return amount
count = 3
new_points = 0
for i in range(2,N+1):
new_points += phi(i)
S = count + 2*new_points
print(S)
但是上面这个是伪欧拉公式,实际上还是用gcd遍历来做的
第五版,我们自己来写正真的欧拉公式,发现一个简便的表达形式,稍后证明:
def isPrime(a):
return not ( a < 2 or any(a % i == 0 for i in range(2, int(a ** 0.5) + 1)))
def phi(n):
y = n
for i in range(2,n+1):
if isPrime(i) and n % i == 0 :
y -= y/i
else:
continue
return int(y)
https://www.cnblogs.com/linyujun/p/5194170.html
借鉴上面这个blog,可以将欧拉函数写成:
def phi(n):
y = n
#print(round(n**0.5)+1)
for i in range(2,round(n**0.5)+1):
if n % i == 0 :
y -= y/i
while (n % i ==0):
n /= i
else:
continue
if (n>1):
y -= y/n
return int(y)