多少(100)以内的素数

问题来源与100以内的素数,求素数在100以内这个范围其实是很好求的,但是假如现在换成了1亿内的素数呢?有没有更快更好的算法?

我们先来看看常规解法:


func getPrimeNumber1() []int {
	var i int
	var sum = 0
	var array = make([]int, 0)
	for i = 1; i < maxNum; i++ {

		var len = int(math.Sqrt(float64(i)))
		var flag = 0
		//i%2可以优化
		if i > 2 && i%2 == 0 {
			//sum++
			continue
		}
		for j := 2; j <= len; j++ {
			sum++
			if i%j == 0 {
				flag = 1
				break
			}

		}
		if flag == 0 {
			array = append(array, i)
		}
	}
	return array

}

常规解法中外循环从1~maxNum 遍历,内循环从 2 到 sqrt(i) 进行遍历,不停的 通过 i%j 取余来判定是否能被整除,同时可以将2的倍数剔除(通过步长剔除也可以),我们尝试将maxNum 扩大到1千万,此时算法执行了大概 18s左右,因此当计算大批量(范围内)的素数时并不太适合。

改进算法:

1. 我们用一个长度为 maxNum 的长度位的内存地址标示从 0-maxNum 中每个数是否是素数

2. 我们将 能被2整除的数进行标记(可以节约一半)

3. 外循环 循环 2 ~ sqrt(maxNum) 范围,内循环 循环 i ~ maxLen 步长为2,同时不停的计算 循环范围内的非素数,并标记出来。

4. 输出素数

//给定一个数字、类型长度、根号下的长度(位)算出 当前数 所属的 下标 以及校验位 leftVal
func getKeyVal(num int, typeLen int, typeLenSqrt int) (int, uint) {

	//求商
	var key = num >> uint(typeLenSqrt)
	//var key = num / typeLen

	//var val = num % typeLen
	//求余
	var valTmp = key << uint(typeLenSqrt)
	var val = num - valTmp
	var leftVal uint = 1 << uint(typeLen-val-1)

	//fmt.Println(num, typeLen, val, val1)
    //key 标示下标,leftVal 标示位移过后的校验位
	return key, leftVal
}
//计算素数前对 一些基本的 偶数 以及 填充长度、数值进行计算
func getPrimeNumberPre() ([]uint, int) {
	var iii iInt = 1
	var byteW = 8
	//var byteWStep = byteW / 2
	var typeW = int(unsafe.Sizeof(iii))
	var typeLen = int(typeW * 8)
	var num = maxNum / typeLen
	var tmp = make([]uint, num+1)
	var i int
    //对偶数进行处理的过程
	var oneInt uint = 0
	var commInt uint = 0
    //170 的2进制正好能够覆盖64位下的所有偶数
	for i = 0; i < typeW; i++ {
		commInt += 170 << uint(i*byteW)
	}
	//最高位因为是uint 取0
	oneInt = commInt >> 4
	fmt.Printf("%b\n%b\n", oneInt, commInt)

	//将每个数组下标对应的64个位置为所需
    //可以尝试加速  前后同时修改
	for i = 0; i <= num; i++ {
		tmp[i] |= commInt
	}
    //注意下标为0开始的几位(1,2,3)
	tmp[0] &= oneInt
	//fmt.Printf("%b\n%b\n", tmp[0], tmp[1])
	fmt.Printf("%b\n%b\n", oneInt, commInt)
	return tmp, typeLen
}
//计算素数
func getPrimeNumber() []int {
	var len = int(math.Sqrt(float64(maxNum)))
	var typeLenSqrt = 6 //2的6此方等于 64
	var i int
	var array = []int{}
	//var sum = 0
	tmp, typeLen := getPrimeNumberPre()

	for i = 2; i < len; i++ {
		key, leftVal := getKeyVal(i, typeLen, typeLenSqrt)
		if tmp[key]&leftVal == leftVal {
			//sum++
			continue
		}

		var maxLen = maxNum / i
		var tmpKey = i * i
		//如何在进行优化 步长为 2 说明 没有必要再进行 乘法操作
		for j := i; j <= maxLen; j += 2 {
			newKey, newLeft := getKeyVal(tmpKey, typeLen, typeLenSqrt)
			tmp[newKey] |= newLeft
			tmpKey += i + i

			//sum++
		}
	}
	//maxGroup := maxNum / typeLen
	for i = 1; i < maxNum; i++ {
		key, leftVal := getKeyVal(i, typeLen, typeLenSqrt)
		if tmp[key]&leftVal != leftVal {
			array = append(array, i)
			//fmt.Println("----", i)
		}

	}

	//fmt.Println("===", sum)
	return array
}

经过优化后,计算1千万以内的素数基本上是 66ms,1亿以内的素数800ms左右(mac pre i5 8g  语言版本 go1.10 ),不同环境可能结果不同,此处执行数据仅作为参考,但是已经相对能够在1s之内算出1亿范围内的素数了,效果还是很明显。第二种算法等于变相利用位操作标记了非素数,在后续的标记过程中直接跳过。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值