- 今天我们来做一个例题,这是个什么例题呢,就是让用户输入两个数,M,N然后打印出第M个素数到第N个素数,M小于N小于100000,很多人看到这个题目可能刚开始就感觉到比较简单,其实不然,也是有点小小的思考性的,那是你没有没有注意M小于N小于100000,如果你没有一个好的方法来做的话,假如用户输入1 100000 那么你就要打印出第一个到第10万个素数,那么这里面的计算是很长很长的
- 题目事意:用户输入M,N打印出第M个到第N个素数
例如:输入 1 10
输出 2 3 5 7 11 13 17 19 23 29
这些是第一个到第10个素数,我们都知道判断一个数是不是素数的方法很简单,那么打印出第多少个到第多少个还继续用普通方法可行吗?
我们先来看一下简单的方法,代码如下
import "fmt"
//判断一个数是不是素数
func IsPrime(a int)(result bool){
result=true
for i:=2;i<a;i++{
if(a%i==0){
result=false
}
}
return result
}
func main(){
var count int=0 //用来计数,便于每打印10个数换一行
for an:=100;an<200;an++{
if(IsPrime(an)){
fmt.Printf("%d\t",an)
count++// 用来计数
if(count%10==0){
fmt.Printf("\n")
}
}
}
fmt.Printf("\n一共有%d个数\n",count)
}
这个代码是判断了100到200之间的素数并打印出来,这样给定了具体范围是很好判断的,但是我们的题目要求是第多少个素数到第多少个,这样你就不知道具体范围,那么有什么方法呢,我们先看上一段代码来解决一些问题,要不然下面代码会变的很冗杂,
我们看这里,我们这里声明了一个函数,判断一个素数是不是素数用自己本身除以2一直除到自己本身减1,如果都除不尽那么这个素就是为素数,那么这样每求一个数是不是素数都要几乎判断n次这样不是很烦吗,我们来想一下这里的判断条件能不能改一下让执行效率变得高一点呢
当然可以的,我们来想一下这里的判断条件改为i<a/2
行不行呢,这样每次判断就少执行了一半,这是因为一个数如果他前半边没有因子的话那么后半边肯定也没有因子啊,但是这样虽然每次都少执行了一半的次数,但是如果数据很大的话少执行一般也无法改变,因为时间复杂度都是O(n),那么我们想想能不能继续优化了呢,
当然还是可以的,我们来想一下,这里的判断条件改为i<=int(math.Sqrt(float64(a)))
这样行不行呢,这里面用到了一个math包里面的sqrt函数,这个函数是用来求平方根的,我们来想一下这样为什么也行呢,比如a为100,那么除到10就能判断出a是不是素数这是因为一个数如果想有因子那么必须是两个数相乘得到他本身,那么肯定有一个数是小于他的平方根的,不会两个都大于他的平方根,如果两个都大于他的平方根那么相乘就不等于他本身了,所以这样是可以的,而且这样每次算的效率提高了很多,比如算1000000是不是素数,恰面两种方法要判断100万次和50万次,而我们说的这个方法只需要判断1000次就行了,是不是少了很多呢,这样的时间复杂度也降低了很多把,
其实我们还有一种方法也比前两种简单,那就是构造一个素数表,判断一个数是不是素数,用这个数去除以前面所有的素数,如果都除不尽那么这个数就是素数,只要有一个除得尽那就不是素数,
那么我们下面继续思考一下如何来做这道题目,我们可以定义一个切片来保存数据,来构造一个素数表,这样题目要求打印第M个到第N个我们只需要找到对应的下标就可以了,我们先来看第三种讲的除到平方根的代码
package main
import{
"fmt"
"math"
}
func main(){
slice:=make([]int,1)
slice[0]=2
var m,n int
var count int=2 //从2开始忘后面遍历
var countp int //用来计数多少个打一行
var flag bool=true //默认为真
fmt.Printf("请输入两个数字来打印第多少个到多少个之间的素数\n")
fmt.Scanf("%d%d",&m,&n)
for i:=1;i<=n-1;i++{ //用来构造素数表
flag=true //每次先默认为真
count++ //用来遍历出素数
temp:=count //临时保存
for j:=2;j<=int(math.Sqrt(float64(count)));j++{ //每次除到本身的平方根就行
if count%j==0{ //如果有能取余为0那么就跳出循环回退一下并且flag定义为flase
i--
flag=false
break
}
}
if flag{ //如果flag为真那么添加到切片里面
slice=append(slice,temp)
}
}
for k:=m-1;k<n;k++{ //打印第m个到第n个素数
countp++
if countp%10!=0&&countp<n{
fmt.Printf("%d ",slice[k])
}
if countp%10==0||countp==n-m+1{
fmt.Printf("%d\n",slice[k])
}
}
}
这样的思路我们就可以很简洁的看懂了,首先切片的第一个元素为0,然后一直往后面遍历,遇到一个素数就添加进切片,一直遍历到满足到第n个为止,然后打印出第M个到第N个素数
再来看另外一种除以前面的素数来判断是不是素数的方法
package main
import{
"fmt"
"math"
}
func main(){
slice:=make([]int,1)
slice[0]=2
var m,n,temp,j int
var count int=2
var countp int
var flag bool
fmt.Printf("请输入两个数字来打印出之间的素数\n")
fmt.Scanf("%d%d",&m,&n)
for i:=1;i<=n-1;i++{ //构造素数表
count++ //用来遍历出素数
temp=count //临时变量保存一下
flag=true //先默认为true默认为素数
for j=i-1;j>=0;j--{ //把前面所有的素数遍历一遍
if count%slice[j]==0{ // 如果有一个能取余为0那么就不是素数跳出本次循环
i-- //i--是因为需要回退一下
flag=false //定义为false
break
}
}
if flag{ //只有falg等于true为素数时才加入切片当中
slice=append(slice,temp)
}
}
for k:=m-1;k<n;k++{ //打印出第m个素数到第n个素数,
fmt.Printf("%d\t",slice[k])
countp++
if countp%20==0{ //用来计数一下,每10个换一行
fmt.Println()
}
}
}
其实这两种方法也很相似,就看怎么思考。多谢提意见