Week 2:汇总求素数和杨辉三角的实现方法

一、求素数
1、优化一:过滤掉所有偶数和大于10的个位为5的数,第二层循环范围缩小到 √x

n=100
l=[2]
c=1
for x in range(3,n,2):
    if n>10 and n %5 == 0:
        continue
    r=int(x**0.5)+1
    for i in range(3,r,2):
        if x %i == 0:
            break
    else:
        c += 1
        l.append(x)
print(c)
print(l)

2、优化二:合数一定能找到可以整除的质数,因此第二层循环可采用之前计算出的质数列表,也将循环范围缩小到 √x

n=100
c=1
l=[2]
for x in range(3,n,2):
    flag=True
    r=x**0.5
    for i in l:
        if x%i == 0:
            flag=False
            break
        if i>r:
            break
    if flag:
        c+=1
        l.append(x)
print(c)
print(l)

自己当时犯了两个错误:一是未加flag,直接在if i > r 后continue,else子句照常运行,虽然也能运行出正确结果,但第二个for循环会一直运行,并未起到提前中止的效果;二是错误地将第一个flag写在了最外面,因不同分支会改变flag的值,每次大循环时flag都应重新赋值。

3、优化三:大于3的素数只有6N-1和6N+1两种形式,即6的倍数前后
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
发现质数之间的间隔大致满足2,4,2,4…的规律,可进一步缩小x的范围,由于range函数内部只求值一次、步长不可变,可考虑在外面加一层循环控制step

n=100
l=[2,3]
c=2
x=5
step=2
while x<n:
    r=int(x**0.5)+1
    for i in range(3,r,2):
        if x %i == 0:
            break
    else:
        c+=1
        l.append(x)
    x+=step
    step = 4 if step==2 else 2
print(c)
print(l)
#利用已计算出的质数列表

n=100
c=2
l=[2,3]
x=5
step=2
while x<n:
    for i in l:
        if x%i==0:
            break
    else:
        c+=1
        l.append(x)
    x + = step
    step = 4 if step==2 else 2
print(c)
print(l)

总结:
a. 关于计算尽量只算一次,避免每个小循环内都重新计算;
b. 利用上次计算结果,效率会更高;

二、杨辉三角
1、方法一:基本实现

n=int(input("n:"))
l=[[1],[1,1]]
for i in range(n-2):
    pre=l[i+1]
    cur=[1]
    for j in range(i+1):
        cur.append(pre[j]+pre[j+1])
    cur.append(1)
    l.append(cur)
print(l)

2、方法二:右端补0,利用负索引

n=int(input("n:"))
tri=[[1]]
for i in range(1,n):
    pre=tri[i-1]+[0]
    cur=[]
    for j in range(i+1):
        cur.append(pre[j-1]+pre[j])
    tri.append(cur)
print(tri)

此种方法多了pre=tri[i-1]+[0] 的拷贝和尾部追加0,空间复杂度增加,计算步骤也多于方法一,整体更慢

    cur=[]
    tri.append(cur)
    for j in range(i+1):
        cur.append(pre[j-1]+pre[j])

其中第二层循环的顺序可后调,因cur属引用类型,添加的是地址

3、方法三:对称性解法,减少计算
若计算一半,再根据对称逐个追加,效率较慢;鉴于杨辉三角每行的个数固定,可一次创建好所需的列表空间、再替换,这个过程比反复append要快

n=int(input("n:"))
tri=[[1],[1,1]]
for i in range(2,n):
    pre=tri[i-1]
    cur=[1]*(i+1)
    for j in range(i//2):
        cur[j+1]=pre[j]+pre[j+1]
        if i!=2*(j+1):
            cur[-j-2]=cur[j+1]
    tri.append(cur)
print(tri)

4、方法四:单行覆盖,进一步降低空间复杂度

n=int(input("n:"))
tri=[1]*n
for i in range(n):
    tem=1
    for j in range(1,i//2+1):
        val=tem+tri[j]
        tem=tri[j]
        tri[j]=val
        if i!=2j:
            tri[-j-n+i]=val
print(tri[:i+1])

构造一个临时变量承载被覆盖前的 tri [ j -1] 的值,一次循环后相当于 val = tri [ j -1] + tri [ j ]

5、优化方法一:初始设置空列表

#打印第m行第k项

n=int(input("input n:"))
tri=[]
for i in range(n):
    cur=[1]
    tri.append(cur)
    if i==0:
        continue
    for j in range(i-1):
        cur.append(tri[i-1][j]+tri[i-1][j+1])
    cur.append(1)

m=6
k=3
print(tri[m-1][k-1])

6、方法五:打印第m行第k项,新旧列表交替使用,一次性开辟足够新行

#一次性开辟好空间比append追加效率高
m=6
k=3

for i in range(m):
    newline=[1]*(i+1)
    for j in range(i-1):
        newline[j+1]=oldline[j]+oldline[j+1]
    oldline=newline
print(newline[k-1])

7、方法六:利用组合数公式
这里写图片描述
在不用函数的情况下,求阶乘可简化为只求一次最大的阶乘,r 和 n - r 的阶乘中途可算出

m=6
k=3

n=m-1
r=k-1
tri=[] #r!,(n-r)!,n!
factorial=1
for i in range(1,n+1):
    factorial*=i
    if i==r:
        tri.append(factorial)
    if i==n-r:
        tri.append(factorial)
    if i==n:
        tri.append(factorial)
print(int(tri[2]/(tri[0]*tri[1])))

该算法空间复杂度最少O(1),只开辟了一个小列表、引用一个变量factorial,只是在做极快的乘法运算,而且只用了一次循环。列表的方法没有组合数快。

猜你喜欢

转载自blog.csdn.net/weixin_42196568/article/details/81542715