Kotlin 语言使用序列查找素数学习
在 Kotlin 中,使用序列(Sequence)查找素数是一个非常经典的学习案例,因为它展示了序列的惰性计算特性在处理无限流或大数据集时的优势。
1. 素数的基本概念与算法
什么是素数?
- 大于1的自然数
- 除了1和它本身以外没有其他因数
- 例如:2, 3, 5, 7, 11, 13, ...
常用的素数检测算法
// 方法1: 简单试除法(时间复杂度 O(√n))
fun isPrimeBasic(n: Int): Boolean {
if (n <= 1) return false
if (n <= 3) return true
if (n % 2 == 0 || n % 3 == 0) return false
var i = 5
while (i * i <= n) {
if (n % i == 0 || n % (i + 2) == 0) return false
i += 6
}
return true
}
// 方法2: 使用预计算的素数表(更高效)
class PrimeChecker {
private val primes = mutableListOf(2, 3)
fun isPrime(n: Int): Boolean {
if (n <= 1) return false
if (primes.contains(n)) return true
// 如果n比已知最大素数小,但不在列表中,则不是素数
if (n < primes.last()) return false
// 使用已知素数进行试除
for (prime in primes) {
if (prime * prime > n) break
if (n % prime == 0) return false
}
// 更新素数列表
primes.add(n)
return true
}
}
2. 使用序列生成无限素数流
简单的无限素数序列
// 方法1: 使用 generateSequence 生成无限素数序列
fun primeSequence(): Sequence<Int> = sequence {
var current = 2
while (true) {
if (isPrimeBasic(current)) {
yield(current)
}
current++
}
}
// 使用
val first10Primes = primeSequence().take(10).toList()
println("前10个素数: $first10Primes") // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
// 方法2: 更高效的版本,避免重复检测
fun efficientPrimeSequence(): Sequence<Int> = sequence {
yield(2) // 第一个素数
var current = 3
while (true) {
if (isPrimeBasic(current)) {
yield(current)
}
current += 2 // 偶数除了2都不是素数,所以只检查奇数
}
}
// 获取第100个素数
val hundredthPrime = efficientPrimeSequence().take(100).last()
println("第100个素数: $hundredthPrime") // 541
使用序列构建器优化
// 结合 yieldAll 和缓存的素数序列
fun optimizedPrimeSequence(): Sequence<Int> = sequence {
val primes = mutableListOf<Int>()
fun isPrimeWithCache(n: Int): Boolean {
for (prime in primes) {
if (prime * prime > n) break
if (n % prime == 0) return false
}
primes.add(n)
return true
}
yield(2)
primes.add(2)
var current = 3
while (true) {
if (isPrimeWithCache(current)) {
yield(current)
}
current += 2
}
}
// 性能测试
fun measurePerformance() {
val startTime = System.currentTimeMillis()
val count = 1000
val primes = optimizedPrimeSequence()
.take(count)
.toList()
val endTime = System.currentTimeMillis()
println("生成前 $count 个素数耗时: ${endTime - startTime}ms")
println("最后一个素数: ${primes.last()}")
}
3. 埃拉托斯特尼筛法(Sieve of Eratosthenes)
基本的埃拉托斯特尼筛法
// 生成直到 limit 的所有素数
fun sieveOfEratosthenes(limit: Int): List<Int> {
if (limit < 2) return emptyList()
val isPrime = BooleanArray(limit + 1) { true }
isPrime[0] = false
isPrime[1] = false
var p = 2
while (p * p <= limit) {
if (isPrime[p]) {
for (i in p * p..limit step p) {
isPrime[i] = false
}
}
p++
}
return isPrime.withIndex()
.filter { it.value }
.map { it.index }
}
// 使用
val primesUpTo100 = sieveOfEratosthenes(100)
println("100以内的素数: ${primesUpTo100.size} 个")
println(primesUpTo100.take(10))
无限筛法使用序列
// 无限版本的埃拉托斯特尼筛法(基于论文的算法)
fun infiniteSieve(): Sequence<Int> = sequence {
// 使用一个映射来存储下一个要标记为合数的数字
val composites = mutableMapOf<Int, MutableList<Int>>()
var current = 2
while (true) {
val factors = composites[current]
if (factors == null) {
// current 是素数
yield(current)
composites[current * current] = mutableListOf(current)
} else {
// current 是合数,更新映射
for (factor in factors) {
composites.getOrPut(current + factor) { mutableListOf() }.add(factor)
}
composites.remove(current)
}
current++
}
}
// 更高效的无限筛法实现
fun efficientInfiniteSieve(): Sequence<Int> = sequence {
val wheel = listOf(1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6)
val primes = mutableListOf(2, 3, 5)
yieldAll(primes)
val sieve = mutableMapOf<Int, Int>()
var candidate = 7
var wheelIndex = 0
while (true) {
var factor = sieve.remove(candidate)
if (factor == null) {
// candidate 是素数
yield(candidate)
factor = candidate
}
// 标记合数
var composite = candidate + factor * wheel[wheelIndex]
wheelIndex = (wheelIndex + 1) % wheel.size
while (sieve.containsKey(composite)) {
composite += factor * wheel[wheelIndex]
wheelIndex = (wheelIndex + 1) % wheel.size
}
sieve[composite] = factor
candidate += wheel[wheelIndex]
wheelIndex = (wheelIndex + 1) % wheel.size
}
}
4. 并行素数计算
使用协程并行计算素数
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
// 并行检查一定范围内的素数
suspend fun findPrimesInRangeParallel(start: Int, end: Int, chunkSize: Int = 1000): List<Int> = coroutineScope {
val primes = mutableListOf<Int>()
val jobs = mutableListOf<Deferred<List<Int>>>()
for (chunkStart in start..end step chunkSize) {
val chunkEnd = minOf(chunkStart + chunkSize - 1, end)
val job = async(Dispatchers.Default) {
(chunkStart..chunkEnd).filter { isPrimeBasic(it) }
}
jobs.add(job)
}
// 等待所有任务完成并收集结果
jobs.awaitAll().forEach { primes.addAll(it) }
primes.sorted()
}
// 使用
fun main() = runBlocking {
val time = measureTimeMillis {
val primes = findPrimesInRangeParallel(1, 1_000_000, 10000)
println("找到 ${primes.size} 个素数")
println("最大的素数: ${primes.last()}")
}
println("并行计算耗时: ${time}ms")
}
使用流进行惰性并行计算
import kotlinx.coroutines.flow.*
// 创建素数流
fun primeFlow(): Flow<Int> = flow {
var current = 2
while (true) {
if (isPrimeBasic(current)) {
emit(current)
delay(1) // 模拟耗时操作
}
current++
}
}
// 并行处理素数流
suspend fun processPrimesInParallel() = coroutineScope {
primeFlow()
.take(100) // 只取前100个素数
.buffer() // 缓冲以允许并行处理
.map { prime ->
async {
// 模拟对每个素数的复杂处理
delay(10)
"处理素数: $prime"
}
}
.collect { deferred ->
val result = deferred.await()
println(result)
}
}
5. 素数相关的数学函数
与素数相关的扩展函数
// 扩展函数:检查 Int 是否是素数
val Int.isPrime: Boolean
get() = when {
this <= 1 -> false
this <= 3 -> true
this % 2 == 0 || this % 3 == 0 -> false
else -> {
var i = 5
while (i * i <= this) {
if (this % i == 0 || this % (i + 2) == 0) return false
i += 6
}
true
}
}
// 扩展函数:获取下一个素数
fun Int.nextPrime(): Int {
var current = if (this < 2) 2 else this + 1
while (!current.isPrime) {
current++
}
return current
}
// 扩展函数:获取质因数分解
fun Int.primeFactors(): List<Int> {
var n = this
val factors = mutableListOf<Int>()
// 处理2
while (n % 2 == 0) {
factors.add(2)
n /= 2
}
// 处理奇数
var i = 3
while (i * i <= n) {
while (n % i == 0) {
factors.add(i)
n /= i
}
i += 2
}
// 如果剩下的数大于2,它本身是素数
if (n > 2) {
factors.add(n)
}
return factors
}
// 使用示例
fun demonstrateExtensions() {
val number = 84
println("$number 是素数吗? ${number.isPrime}")
println("$number 的下一个素数是: ${number.nextPrime()}")
println("$number 的质因数分解: ${number.primeFactors()}")
val prime = 17
println("$prime 是素数吗? ${prime.isPrime}")
println("$prime 的下一个素数是: ${prime.nextPrime()}")
println("$prime 的质因数分解: ${prime.primeFactors()}")
}
6. 优化技巧与性能对比
性能对比测试
import kotlin.system.measureTimeMillis
fun performanceComparison() {
val limit = 1_000_000
println("=== 素数计算性能对比 (1-${limit}) ===")
// 1. 简单循环
val time1 = measureTimeMillis {
val count = (2..limit).count { it.isPrime }
println("简单循环: 找到 $count 个素数")
}
println("时间: ${time1}ms")
// 2. 埃拉托斯特尼筛法
val time2 = measureTimeMillis {
val primes = sieveOfEratosthenes(limit)
println("埃拉托斯特尼筛法: 找到 ${primes.size} 个素数")
}
println("时间: ${time2}ms")
// 3. 序列方法
val time3 = measureTimeMillis {
val count = generateSequence(2) { it + 1 }
.takeWhile { it <= limit }
.count { it.isPrime }
println("序列方法: 找到 $count 个素数")
}
println("时间: ${time3}ms")
println("=== 性能对比完成 ===")
}
// 内存优化版本:分段筛法
fun segmentedSieve(limit: Int): List<Int> {
val segmentSize = 10000 // 每个段的大小
// 第一步:找到小于等于 √limit 的所有素数
val sqrtLimit = Math.sqrt(limit.toDouble()).toInt()
val smallPrimes = sieveOfEratosthenes(sqrtLimit)
val result = smallPrimes.toMutableList()
// 第二步:分段处理剩余范围
var low = sqrtLimit + 1
while (low <= limit) {
val high = minOf(low + segmentSize - 1, limit)
val segment = BooleanArray(high - low + 1) { true }
for (prime in smallPrimes) {
// 找到在 [low, high] 范围内能被 prime 整除的最小数字
var start = maxOf(prime * prime, ((low + prime - 1) / prime) * prime)
for (i in start..high step prime) {
segment[i - low] = false
}
}
// 收集当前段的素数
for (i in segment.indices) {
if (segment[i]) {
val prime = i + low
if (prime > sqrtLimit) {
result.add(prime)
}
}
}
low += segmentSize
}
return result
}
7. 实际应用案例
密码学应用:RSA 密钥生成
// 简化版 RSA 密钥生成
class SimpleRSA {
data class KeyPair(val publicKey: PublicKey, val privateKey: PrivateKey)
data class PublicKey(val n: Long, val e: Long)
data class PrivateKey(val n: Long, val d: Long)
companion object {
// 生成大素数(简化版)
fun generateLargePrime(bitLength: Int): Long {
// 在实际应用中,这里应该使用更安全的随机数生成器
val start = 1L shl (bitLength - 1)
val end = (1L shl bitLength) - 1
return generateSequence(start) { it + 1 }
.takeWhile { it <= end }
.filter { it.isPrime() }
.first()
}
// 扩展函数:检查 Long 是否是素数(简化版)
fun Long.isPrime(): Boolean {
if (this <= 1) return false
if (this <= 3) return true
if (this % 2 == 0L || this % 3 == 0L) return false
var i = 5L
while (i * i <= this) {
if (this % i == 0L || this % (i + 2) == 0L) return false
i += 6
}
return true
}
// 生成 RSA 密钥对
fun generateKeyPair(): KeyPair {
// 生成两个大素数
val p = generateLargePrime(16) // 实际应用中需要更大的素数
val q = generateLargePrime(16)
val n = p * q
val phi = (p - 1) * (q - 1)
// 选择公钥指数 e(通常为 65537)
val e = 65537L
// 计算私钥指数 d(e * d ≡ 1 mod phi)
val d = modInverse(e, phi)
return KeyPair(
publicKey = PublicKey(n, e),
privateKey = PrivateKey(n, d)
)
}
// 计算模逆
private fun modInverse(a: Long, m: Long): Long {
var t = 0L
var newt = 1L
var r = m
var newr = a
while (newr != 0L) {
val quotient = r / newr
val tempT = t
t = newt
newt = tempT - quotient * newt
val tempR = r
r = newr
newr = tempR - quotient * newr
}
if (r > 1) throw IllegalArgumentException("a is not invertible")
if (t < 0) t += m
return t
}
}
}
// 使用
fun demonstrateRSA() {
val keyPair = SimpleRSA.generateKeyPair()
println("RSA 公钥: n=${keyPair.publicKey.n}, e=${keyPair.publicKey.e}")
println("RSA 私钥: n=${keyPair.privateKey.n}, d=${keyPair.privateKey.d}")
}
质因数分解工具
class PrimeFactorizer {
// 使用 Pollard's Rho 算法进行质因数分解(适用于大数)
fun factorize(n: Long): List<Long> {
val factors = mutableListOf<Long>()
factorizeRecursive(n, factors)
return factors.sorted()
}
private fun factorizeRecursive(n: Long, factors: MutableList<Long>) {
if (n == 1L) return
if (n.isPrime()) {
factors.add(n)
return
}
val divisor = pollardRho(n)
factorizeRecursive(divisor, factors)
factorizeRecursive(n / divisor, factors)
}
private fun pollardRho(n: Long): Long {
if (n % 2 == 0L) return 2
var x = 2L
var y = 2L
var d = 1L
val f = { a: Long -> (a * a + 1) % n }
while (d == 1L) {
x = f(x)
y = f(f(y))
d = gcd(Math.abs(x - y), n)
}
return if (d == n) {
// 失败,重试
pollardRho(n)
} else {
d
}
}
private fun gcd(a: Long, b: Long): Long {
return if (b == 0L) a else gcd(b, a % b)
}
private fun Long.isPrime(): Boolean {
// Miller-Rabin 素数测试(简化版)
if (this <= 1) return false
if (this <= 3) return true
if (this % 2 == 0L || this % 3 == 0L) return false
var i = 5L
while (i * i <= this) {
if (this % i == 0L || this % (i + 2) == 0L) return false
i += 6
}
return true
}
}
// 使用示例
fun demonstrateFactorization() {
val factorizer = PrimeFactorizer()
val numbers = listOf(84L, 123456789L, 999999937L)
numbers.forEach { n ->
val factors = factorizer.factorize(n)
println("$n 的质因数分解: $factors")
// 验证结果
val product = factors.reduce { acc, factor -> acc * factor }
println("验证: $n = $product (${n == product})")
}
}
8. 数学研究与探索
孪生素数序列
// 生成孪生素数序列(相差2的素数对)
fun twinPrimeSequence(): Sequence<Pair<Int, Int>> = sequence {
var prevPrime = 2
primeSequence().forEach { currentPrime ->
if (currentPrime - prevPrime == 2) {
yield(Pair(prevPrime, currentPrime))
}
prevPrime = currentPrime
}
}
// 查找前N对孪生素数
fun findTwinPrimes(count: Int): List<Pair<Int, Int>> {
return twinPrimeSequence().take(count).toList()
}
// 哥德巴赫猜想验证(每个大于2的偶数都可以表示为两个素数之和)
fun goldbachConjecture(n: Int): List<Pair<Int, Int>> {
require(n > 2 && n % 2 == 0) { "输入必须是大于2的偶数" }
val primes = sieveOfEratosthenes(n).toSet()
return primes.mapNotNull { p ->
val q = n - p
if (q >= p && primes.contains(q)) {
Pair(p, q)
} else {
null
}
}
}
// 使用
fun demonstrateMathConcepts() {
println("=== 孪生素数 ===")
val twinPrimes = findTwinPrimes(10)
twinPrimes.forEachIndexed { index, (p, q) ->
println("${index + 1}. ($p, $q)")
}
println("\n=== 哥德巴赫猜想 ===")
val evenNumber = 100
val decompositions = goldbachConjecture(evenNumber)
println("$evenNumber 可以表示为以下素数对的和:")
decompositions.take(5).forEach { (p, q) ->
println(" $p + $q = ${p + q}")
}
println("... 总共 ${decompositions.size} 种表示方法")
}
9. 性能优化终极版
// 终极优化:结合位运算和缓存
object PrimeUtils {
private val smallPrimes = intArrayOf(
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
53, 59, 61, 67, 71, 73, 79, 83, 89, 97
)
private val smallPrimeSet = smallPrimes.toSet()
// 使用位运算进行快速素数检测(对于小于10^7的数)
fun isPrimeUltraFast(n: Int): Boolean {
if (n <= 1) return false
if (n < 100) return n in smallPrimeSet
// 快速排除常见合数
if (n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0) return false
val sqrt = Math.sqrt(n.toDouble()).toInt()
// 使用已知小素数进行快速试除
for (prime in smallPrimes) {
if (prime > sqrt) return true
if (n % prime == 0) return false
}
// 对于更大的素数,使用6k±1规则
var i = 101
while (i <= sqrt) {
if (n % i == 0 || n % (i + 2) == 0) return false
i += 6
}
return true
}
// 生成素数序列(高度优化版本)
fun optimizedPrimeSequence(): Sequence<Int> = sequence {
yieldAll(smallPrimes)
// 从101开始,只检查6k±1形式的数
var k = 17 // 6*17-1=101, 6*17+1=103
while (true) {
val candidate1 = 6 * k - 1
if (isPrimeUltraFast(candidate1)) {
yield(candidate1)
}
val candidate2 = 6 * k + 1
if (isPrimeUltraFast(candidate2)) {
yield(candidate2)
}
k++
}
}
}
// 性能测试
fun testUltraFastPrimeChecker() {
val limit = 10_000_000
val time = measureTimeMillis {
val count = (1..limit).count { PrimeUtils.isPrimeUltraFast(it) }
println("在 1-$limit 范围内找到 $count 个素数")
}
println("耗时: ${time}ms")
println("平均每微秒检查: ${limit.toDouble() / time} 个数")
}
总结
使用 Kotlin 序列查找素数展示了几个重要概念:
关键知识点:
- 序列的惰性计算:可以处理无限数据流
- 算法优化:从简单试除法到埃拉托斯特尼筛法
- 性能考虑:选择合适的算法和数据结构
- 数学应用:素数在密码学和数论中的重要性
性能建议:
- 小范围检测:使用简单的试除法
- 中等范围:使用埃拉托斯特尼筛法
- 大范围:使用分段筛法
- 无限序列:使用惰性序列生成器
实际应用:
- 密码学(RSA加密)
- 哈希算法
- 随机数生成
- 数学研究
通过学习和实现这些素数算法,你不仅掌握了 Kotlin 序列的使用,还深入理解了算法优化和数学计算的相关知识。