100以内素数之和python123_Python-零基础自学系列之求素数、杨辉三角

求100内的素数

为了比较算法效率我们扩大到求100000内素数

# 1 简单算法#一个数能被从2开始到自己的平方根的正整数整数整除,就是合数

import datetime

start = datetime.datetime.now()

n = 100000

count = 0

for x in range(2, n):

for i in range(2, int(x**0.5) + 1):

if x % i == 0:

break

else:

count += 1

#print(x)

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 结果 9592

print(delta) # 时间 0.317

print('-' * 30)

# 2 使用奇数

import datetime

start = datetime.datetime.now()

n = 100000

count = 1

for x in range(3, n, 2):

for i in range(3, int(x**0.5) + 1, 2):

if x % i == 0:

break

else:

count += 1

#print(x)

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 结果 9592

print(delta) # 时间 0.151

print('-' * 30)

# 3 存储质数#合数一定可以分解为几个质数的乘积, 2 是质数

#质数一定不能整除1和本身之内的整数

import datetime

start = datetime.datetime.now()

n = 100000

count = 1

primenumbers = [2]

for x in range(3, n, 2):

for i in primenumbers:

if x % i == 0:

break

else:

primenumbers.append(x)

count += 1

#print(x)

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 结果 9592

print(delta) # 时间 4.539

print('-' * 30)

# 4 在#3的基础上缩小范围

import datetime

start = datetime.datetime.now()

n = 100000

count = 1

primenumbers = [2]

for x in range(3, n, 2):

flag = False # 不是素数

for i in primenumbers:

if i > int(x ** 0.5): # 素数

flag = True

break

if x % i == 0: # 合数

flag = False

break

if flag:

primenumbers.append(x)

count += 1

#print(x)

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 结果 9592

print(delta) # 时间 0.316

print('-' * 30)

算法2和算法4对比,算法2的奇数应该是多余算法4的,也就是算法4应该要快一点,但是测试的计时却不是,为什么?

结果是增加了质数列表反而慢了,为什么?

修改算法如下

# 4 缩小范围

# x ** 0.5 在循环中只需要计算一次

# 使用列表存储已有的质数,同时缩小范围

import datetime

start = datetime.datetime.now()

n = 100000

count = 1

primenumbers = [2]

for x in range(3, n, 2):

flag = False # 不是素数

edge = int(x ** 0.5)

for i in primenumbers:

if i > edge: # 素数

flag = True

break

if x % i == 0: # 合数

flag = False

break

if flag:

primenumbers.append(x)

count += 1

#print(x)

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 9592

print(delta) # 0.123

print('-' * 30)

这回测试,速递第一了。也就是增加了列表,记录了质数,控制了边界后,使用质数来取模比使用奇数计算更少。

空间换时间,使用列表空间存储质数列表,来换取计算时间。

孪生素数性质

大于3的素数只有6N-1和6N+1两种形式,如果6N-1和6N+1都是素数称为孪生素数

import datetime

start = datetime.datetime.now()

n = 100000

count = 3

x = 7

step = 4

while x < n:

if x % 5 !=0:

for i in range(3, int(x**0.5) + 1, 2):

if x %i == 0:

break

else:

count += 1

x += step

step = 4 if step == 2 else 2

delta = (datetime.datetime.now() - start).total_seconds()

print(count) # 结果 9592

print(delta) # 时间 0.122006

用了这个性质并没有超过算法4,原因还是在于使用列表存储已经计算得到的素数来减少计算。请自行使用列表完成素数的存储。

计算杨辉三角前6行

n行有n项,n是正整数

第n行数字之和为2**(n-1)

解法1 杨辉三角的基本实现

下一行依赖上一行所有元素,是上一行所有元素的两两相加的和,再在两头各加1

预先构建前两行,从而推导出后面的所有行

triangle = [[1], [1, 1]]

for i in range(2, 6):

cur = [1]

pre = triangle[i-1]

for j in range(i - 1):

cur.append(pre[j] + pre[j+1]) # 前一行2项之和

cur.append(1)

triangle.append(cur)

print(triangle)

变体

从第一行开始

triangle = []

n = 6

for i in range(n):

cur = [1]

triangle.append(cur)

if i == 0: continue

pre = triangle[i-1]

for j in range(i - 1):

cur.append(pre[j] + pre[j+1]) # 前一行2项之和

cur.append(1)

print(triangle)

解法2 补零除了第一行以外,每一行每一个元素(包括两头的1)都是由上一行的元素相加得到。如何得到两头的1呢?

目标是打印指定的行,所以算出一行就打印一行,不需要用一个大空间存储所有已经算出的行。

for循环实现

n = 6

newline = [1] # 第一行是特例,因为0+0不等于1

print(newline)

for i in range(1, n):

oldline = newline.copy() # 浅拷贝并补0

oldline.append(0) # 尾部补0相当于两端补0

newline.clear() # 使用append,所以要清除

for j in range(i+1):

newline.append(oldline[j - 1] + oldline[j])

print(newline)

解法3 对称性

思路:能不能一次性开辟空间,可以使用列表解析式或者循环迭代的方式。

能不能减少一半的数字计算。左右对称。

1、 构建行

triangle = []

n = 6

for i in range(n):

row = [1]

for k in range(i):

row.append(1) if k==i-1 else row.append(0)

triangle.append(row)

print(triangle)

上面创建每一行的代码过于啰嗦了,一次性创建出一行,以后覆盖其中数据就行了

triangle = []

n = 6

for i in range(n):

row = [1] *(i + 1) # 开辟速度快

triangle.append(row)

print(triangle)

2、 中点的确定

[1]

[1, 1]

[1, 2, 1]

[1, 3, 3, 1]

[1, 4, 6, 4, 1]

[1, 5, 10, 10, 5, 1]

把整个杨辉三角看成左对齐的二维矩阵。

i==2时,在第3行,中点的列索引j==1

i==3时,在第4行,无中点

i==4时,在第5行,中点的列索引j==2

得到以下规律,如果有i==2j,则有中点

triangle = []

n = 6

for i in range(n):

row = [1] *(i + 1) # 一次开辟空间

triangle.append(row)

# i为0、1不进来,前两行进不来

# i为2,range(1,2),j取1

# i为3,range(1,2),j取1

# i为4,range(1,3),j取1 2

for j in range(1, i//2+1):

val = triangle[i-1][j-1] + triangle[i-1][j]

row[j] = val

row[-j-1] = val

print(triangle)

上面的代码row[-j-1] = val 多做了一次

triangle = []

n = 6

for i in range(n):

row = [1] *(i + 1) # 一次开辟空间

triangle.append(row)

# i为0、1不进来,前两行进不来

# i为2,range(1,2),j取1

# i为3,range(1,2),j取1

# i为4,range(1,3),j取1 2

for j in range(1, i//2+1):

val = triangle[i-1][j-1] + triangle[i-1][j]

row[j] = val

if i != 2 * j:

row[-j-1] = val

print(triangle)

另一种中点的处理

i 中点索引 j

[1]

[1, 1]

[1, 2, 1] 2 1 or -2 0

[1, 3, 3, 1]

[1, 4, 6, 4, 1] 4 2 or -3 1

[1, 5, 10, 10, 5, 1]

[1, 6, 15, 20, 15, 6, 1] 6 3 or -4 2

由此,得到中点的索引处i和j关系为i=2*(j+1)

其实,相当于上例中代码的j循环向左平移1,将所有的j变成 j+1

# 对称

triangle = [[1], [1, 1]] # 所有行

n = 10

for i in range(2, n): # 121

row = [1] * (i + 1) # 一次性开辟内存空间,效率更高

triangle.append(row)

for j in range(i//2): # i=4, range(2) => 0, 1

val = triangle[i-1][j] + triangle[i-1][j+1] # 1 3 3 1

row[j+1] = val

if i != 2*(j+1):

row[-(j+2)] = val # 有可能被重置

else: # i == 2*(j+1) 中点,n=7出现过3次中点

print(',,,,,', val)

print(triangle)

解法4 单行覆盖方法2每次都要清除列表,有点浪费时间。

能够用上方法3的对称性的同时,只开辟1个列表实现吗?

首先我们明确的知道所求最大行的元素个数,例如前6行的最大行元素个数为6个。

下一行等于首元素不变,覆盖中间元素。

n = 6

row = [1] * n # 一次性开辟足够的空间

print(row)

print('-' * 30)

for i in range(n):

print(row[:i+1])

运行结果如下

[1, 1, 1, 1, 1, 1]

------------------------------

[1]

[1, 1]

[1, 1, 1]

[1, 1, 1, 1]

[1, 1, 1, 1, 1]

[1, 1, 1, 1, 1, 1]

由于,这一次直接开辟了所有大小的列表空间,导致负索引比较难计算。

是否可以考虑直接使用正索引计算呢?

以第七行为例,索引1对称索引为5,而1 + 5 = 6当前索引值。

n = 6

row = [1] * n # 一次性开辟足够的空间

print(row)

print('-' * 30)

for i in range(n):

for j in range(i//2): # i为0,1不进入

val = row[j] + row[j+1]

row[j+1] = val

if i != 2 * (j+1):

row[i-(j+1)] = val

print(row[:i+1])

运行结果如下

[1, 1, 1, 1, 1, 1]

------------------------------

[1]

[1, 1]

[1, 2, 1]

[1, 3, 3, 1]

[1, 4, 7, 4, 1]

[1, 5, 12, 12, 5, 1]

问题出在哪里了呢?

原因在于,4覆盖了3,导致3+3变成了3+4才有了7。使用一个临时变量解决

n = 6

row = [1] * n # 一次性开辟足够的空间

print(row)

print('-' * 30)

for i in range(n):

old = 1 # 相当于每行行首1,因为i从4开始就有覆盖了,引入这个变量

for j in range(i//2): # i为0,1不进入

val = old + row[j+1]

old = row[j+1]

row[j+1] = val

if i != 2 * (j+1):

row[i-(j+1)] = val

print(row[:i+1])

也可以写成下面这样

n = 6

row = [1] * n # 一次性开辟足够的空间

print(row)

print('-' * 30)

for i in range(n):

old = 1 # 相当于每行行首1,因为i从4开始就有覆盖了,引入这个变量

for j in range(i//2): # i为0,1不进入

# val = old + row[j+1]

# old = row[j+1]

# row[j+1] = val

row[j+1], old = old + row[j+1], row[j+1]

if i != 2 * (j+1):

row[i-(j+1)] = row[j+1]

print(row[:i+1])

持续更新中......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值