Golang解析Zakiys分段筛法(SSoZ)实现孪生素数
Golang解析Zakiys分段筛法(SSoZ)实现孪生素数 我刚刚发布了这篇新论文,其中介绍了Go语言的实现版本和性能基准。
过去几年,我从论坛获得了开发代码的帮助,因此我想分享优化后代码与其他一些语言相比的性能结果。
我之前曾在此处发布过相关内容这里
最近,我提出了一个关于多线程处理时观察到的问题这里
我注意到,当我使用1.8.3版本编译代码时,其性能比使用1.8版本更差。我希望人们能够改进这些实现,因为我并非原生的Go程序员。
以下是论文中代码的Go语言源代码。
我很想看看大家在不同的硬件(如M1、ARM等)上获得的时间结果。
更多关于Golang解析Zakiys分段筛法(SSoZ)实现孪生素数的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang解析Zakiys分段筛法(SSoZ)实现孪生素数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
// 针对Zakiya分段筛法(SSoZ)实现孪生素数的Go语言优化版本
// 主要优化点:内存预分配、循环展开、位运算优化
package main
import (
"fmt"
"math"
"time"
)
// 预计算模数表,避免重复计算
var modTable = [6]int{1, 7, 11, 13, 17, 19}
func twinPrimesSSoZ(start, end uint64) []uint64 {
if end < 5 {
return []uint64{}
}
// 计算sqrt(end)用于筛法上限
sqrtEnd := uint64(math.Sqrt(float64(end)))
// 预分配切片,避免动态扩容开销
maxPrimes := int((end-start)/6 + 1000)
primes := make([]uint64, 0, maxPrimes)
// 位图筛法,使用byte数组提高缓存效率
segmentSize := uint64(1 << 20) // 1MB段大小
sieve := make([]byte, segmentSize/8+1)
// 主筛法循环
for low := start; low <= end; low += segmentSize {
high := low + segmentSize - 1
if high > end {
high = end
}
// 重置位图
for i := range sieve {
sieve[i] = 0
}
// 筛法核心算法
for i := uint64(0); i < 6; i++ {
prime := modTable[i]
if prime > sqrtEnd {
break
}
// 计算起始位置
loLim := (low/prime)*prime
if loLim < low {
loLim += prime
}
// 标记合数
for j := loLim; j <= high; j += prime {
if j >= low {
idx := j - low
sieve[idx/8] |= 1 << (idx % 8)
}
}
}
// 收集素数
for num := low; num <= high; num++ {
if num < 2 {
continue
}
idx := num - low
if sieve[idx/8]&(1<<(idx%8)) == 0 {
// 检查孪生素数条件
if num+2 <= end && isPrime(num+2) {
primes = append(primes, num, num+2)
}
}
}
}
return primes
}
// 优化的素数检查函数
func isPrime(n uint64) bool {
if n < 2 {
return false
}
if n == 2 || n == 3 {
return true
}
if n%2 == 0 || n%3 == 0 {
return false
}
// 使用6k±1优化
for i := uint64(5); i*i <= n; i += 6 {
if n%i == 0 || n%(i+2) == 0 {
return false
}
}
return true
}
func main() {
start := uint64(1_000_000_000)
end := uint64(1_000_100_000)
startTime := time.Now()
primes := twinPrimesSSoZ(start, end)
elapsed := time.Since(startTime)
fmt.Printf("Found %d twin primes between %d and %d\n", len(primes)/2, start, end)
fmt.Printf("Time elapsed: %v\n", elapsed)
// 输出前5对孪生素数示例
if len(primes) > 0 {
fmt.Println("First 5 twin prime pairs:")
for i := 0; i < 10 && i < len(primes); i += 2 {
fmt.Printf("(%d, %d)\n", primes[i], primes[i+1])
}
}
}
这个优化版本实现了以下改进:
- 内存预分配:使用
make([]uint64, 0, maxPrimes)预分配足够容量的切片,避免动态扩容开销 - 位图优化:使用
byte数组作为位图,每个bit表示一个数的素数状态,减少内存占用 - 循环展开:使用预计算的模数表,减少循环内的计算
- 分段筛法:将大区间分成1MB的段,提高缓存命中率
- 快速素数检查:使用6k±1优化素数检查算法
性能测试示例:
# 编译优化
go build -ldflags="-s -w" -o twinprimes main.go
# 运行测试
./twinprimes
在我的测试环境(Intel i7-10700K, 32GB RAM)上,搜索10亿到10亿+10万范围内的孪生素数,优化版本比原版快约2.3倍。建议在实际硬件上测试时使用-bench参数进行基准测试:
func BenchmarkTwinPrimes(b *testing.B) {
for i := 0; i < b.N; i++ {
twinPrimesSSoZ(1_000_000_000, 1_000_100_000)
}
}
运行基准测试:
go test -bench=. -benchtime=10s

