Skip to content

Commit 9067ce1

Browse files
authored
Merge pull request #115 from JuliaMath/oscardssmith-faster-Factorization
WIP: increase speed of `Factorization` objects
2 parents 751eac7 + 669a9ed commit 9067ce1

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

src/Primes.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,15 @@ function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
254254

255255
local p::T
256256
for p in PRIMES
257+
num_p = 0
257258
while true
258259
q, r = divrem(n, T(p)) # T(p) so julia <1.9 uses fast divrem for `BigInt`
259260
r == 0 || break
260-
h[p] = get(h, p, 0) + 1
261+
num_p += 1
261262
n = q
262263
end
264+
# h[p] += num_p (about 2x faster, but the speed only matters for small numbers)
265+
num_p > 0 && increment!(h, num_p, p)
263266
p*p > n && break
264267
end
265268
n == 1 && return h

src/factorization.jl

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
struct Factorization{T<:Integer} <: AbstractDict{T, Int}
55
pe::Vector{Pair{T, Int}} # Prime-Exponent
66

7-
Factorization{T}() where {T<:Integer} = new{T}(Vector{Pair{T, Int}}())
7+
function Factorization{T}() where {T<:Integer}
8+
# preallocates enough space that numbers smaller than 2310 won't need to resize
9+
v = Vector{Pair{T, Int}}(undef, 4)
10+
empty!(v)
11+
new{T}(v)
12+
end
813
end
914

1015
function Factorization{T}(d::AbstractDict) where T<:Integer
@@ -19,23 +24,39 @@ Base.convert(::Type{Factorization}, d::AbstractDict) = Factorization(d)
1924
Base.iterate(f::Factorization, state...) = iterate(f.pe, state...)
2025

2126
function Base.get(f::Factorization, p, default)
22-
found = searchsorted(f.pe, p, by=first)
23-
isempty(found) ?
24-
default :
25-
last(f.pe[first(found)])
27+
found = searchsortedfirst(f.pe, p, by=first)
28+
(found > length(f.pe) || first(f.pe[found])) != p ? default : last(f.pe[found])
2629
end
2730

2831
Base.getindex(f::Factorization, p::Integer) = get(f, p, 0)
2932

3033
function Base.setindex!(f::Factorization{T}, e::Int, p::Integer) where T
31-
found = searchsorted(f.pe, p, by=first)
32-
if isempty(found)
33-
insert!(f.pe, first(found), T(p)=>e)
34+
found = searchsortedfirst(f.pe, p, by=first)
35+
if found > length(f.pe)
36+
push!(f.pe, T(p)=>e)
37+
elseif first(f.pe[found]) != p
38+
insert!(f.pe, found, T(p)=>e)
39+
else
40+
f.pe[found] = T(p)=>e
41+
end
42+
f
43+
end
44+
45+
"""
46+
impliments f[p] += e faster
47+
"""
48+
function increment!(f::Factorization{T}, e::Int, p::Integer) where T
49+
found = searchsortedfirst(f.pe, p, by=first)
50+
if found > length(f.pe)
51+
push!(f.pe, T(p)=>e)
52+
elseif first(f.pe[found]) != p
53+
insert!(f.pe, found, T(p)=>e)
3454
else
35-
f.pe[first(found)] = T(p)=>e
55+
f.pe[found] = T(p)=>(last(f.pe[found])+e)
3656
end
3757
f
3858
end
59+
increment!(f::AbstractDict, e::Int, p::Integer) = (f[p] = get(f, p, 0) + e)
3960

4061
Base.length(f::Factorization) = length(f.pe)
4162

0 commit comments

Comments
 (0)