leetcode之204计数质数Golang

题目描述

统计所有小于非负整数 n 的质数的数量。

示例 1:

输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

示例 2:

输入:n = 0
输出:0

示例 3:

输入:n = 1
输出:0

提示:

  • 0 <= n <= 5 * 106

算法描述

如果用暴力破解,那么毫无疑问会超时,从题解中知道了一个叫厄拉多塞筛法的方法

  • 首先定义一个bool数组,长度就是题目中的n,用来标识某个数是否是素数,如果不是素数就将数组中对应的位置置为true
  • 首先,0和1肯定不是素数,所以数组前两个元素的值置为true
  • 数组中初始化的时候每个元素对应的值都是false,从2开始,如果对应数组中的值还是false,那么说明了这个数一定是素数,为什么呢?因为我们是按照从小到大的数遍历的,如果他不是素数,那么在前面的处理,我们就已经将这个位置置为true
  • 所以2是素数,那么将2翻倍的数一定不是素数,所以我们将2一直这样翻倍翻下去,直到某一个倍数超过n,那么数组中就没有对应的位置了,我们在这个过程中,将翻倍得到的数字在数组中对应的位置置为true
  • 然后遍历2的下一个数,首先看这个数在数组中对应位置的值是true还是false,如果是true说明了这个位置是由前面某个数翻倍得到的,说明他是合数,所以跳过,如果是false,说明了这个数不能由前面的数翻倍得到,那么他肯定是素数
  • 一直重复上述过程,直到这个2变成了最后的n

代码如下

func countPrimes(n int) int {
	if n <= 2 {
		return 0
	}
	isPrime := make([]bool, n)
	isPrime[0], isPrime[1] = true, true
	for i := 2; i < n; i++ {
		if !isPrime[i] {
			for j := 2; ; {
				summation := j * i
				if summation < n {
					isPrime[summation] = true
					j++
					continue
				}
				break
			}
		}
	}
	count := 0
	for _, value := range isPrime {
		if !value {
			count++
		}
	}
	return count
}

在上面的代码中,我们构建了一个bool数组,如果n很大的时候,这个数组所占的内存空间也是很大的,所以我们将这个代码优化一下,以0,1比特来表示对应的数是不是素数。此时我们只需要一个128就能够表示n等于8的情况了,而上面的情况却需要8个字节,可以直接使用golang源码的math/big包来处理大数运算

优化代码如下:

func countPrimes1(n int) int {
	// 将第0位和第1为直接标志位1,因为0和1都不是素数
	bitMap := big.NewInt(3)
	for i := 2; i < n; i++ {
		// Bit(n)方法返回大数的第n位,如果结果是1,说明这个数不是素数
		if bitMap.Bit(i) == 1 {
			continue
		}
		// 如果这一位是0,说明他是素数,但是他的2倍,3倍。。。直到超过n,都是合数
		for multipul := 2; ; {
			// 将素数翻倍,得到的乘积
			summation := i * multipul
			// 这个乘积还在n的范围之内
			if summation < n {
				// 因为这个乘积是合数,所以它对应的位置应该置为1,SetBit()就是将bitMap的第summation位置为1,并且将结果返回给bitMap
				bitMap = bitMap.SetBit(bitMap, summation, 1)
				// 倍数增加
				multipul++
				continue
			}
			break
		}
	}
	count := 0
	// 直接计算n位之内有多少个0就可以了,就得到了素数的个数
	for i := 0; i < n; i++ {
		if bitMap.Bit(i) == 0 {
			count++
		}
	}
	return count
}
posted @ 2020-10-15 17:28  胖胖咩  阅读(166)  评论(0)    收藏  举报