Open5
【Swift 5.6】エラトステネスの櫛を使って素因数分解【おまけもあるよ】
extension BinaryInteger {
static func gcd(_ x: Self, _ y: Self) -> Self {
y == 0 ? x : gcd(y, x % y)
}
static var Primes: UnfoldSequence<Self, (Self, Array<Self>)> {
sequence(state: (2, [])) { state in
while state.1.contains(where: { state.0 % $0 == 0 }) {
state.0 = state.0 + 1
}
state.1.append(state.0)
return state.0
}
}
var primeFactors: Array<(Self, Self)> {
var F = Array<(Self, Self)>()
var N = self
for p in Self.Primes {
guard N > 1 else { break }
guard N >= p * p else {
F.append((N, 1))
break
}
var C = 0 as Self
while N % p == 0 {
N /= p
C += 1
}
if 0 < C {
F.append((p, C))
}
}
return F
}
}
Primesで素数を列挙しながら剰余が0になる因数とその個数を配列に詰め込むだけ.
任意の整数に .primeFactors
をさせると[(Base, Exponential)]
という書式の配列が得られる.
例えば240.primeFactors
ならば [(2, 4), (3, 1), (5, 1)]
が得られる.
GCDは素因数分解に使わないけれども今後の拡張用おまけ.
BinaryInteger
プロトコルを実装すれば任意の整数型で使えるはず.
BigInteger
とか BInt
とか.
ただNaïve実装すぎて早くはない.
エラトステネスの櫛の最悪計算時間は
[(Base, Exponential)] where Base: BinaryInteger, Exponential: BinaryInteger
の書式を合成数に戻すコードは以下.
let today = 20220527
let today_pf = today.primeFactors
let today_synth = today_pf.reduce(1) {
$0 * Array(repeating: $1.0, count: Int($1.1)).reduce(1, *)
}
print(today == today_synth)
素数列を与える
while state.1.contains(where: { state.0 % $0 == 0 }) {
state.0 = state.0 + 1
}
の部分でテスト範囲に .lazy.filter { $0 < upperBound }
をしても,素数定理で得られた個数.prefix(n)
をしても上限がない時より遅くなって謎.多分最適化の問題とは思う.