From 370d4524abf8ca6078f2ac7db4c84992c851308f Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Mon, 5 May 2025 14:40:20 +0200 Subject: [PATCH 1/2] perf: optimize `permutations` (`increment!` loop) - The construct previously used in the `for` loop is inefficient. - The loop can exit early as soon as `state[i] <= max` is satisfied. - All array indexing can be marked `@inbounds`. --- src/permutations.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/permutations.jl b/src/permutations.jl index b6ef788..c1532ca 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -28,12 +28,14 @@ function has_repeats(state::Vector{Int}) end function increment!(state::Vector{Int}, min::Int, max::Int) - state[end] += 1 - for i in reverse(eachindex(state))[firstindex(state):end-1] - if state[i] > max - state[i] = min - state[i-1] += 1 - end + # All array indexing can be marked inbounds because of the type restriction in the signature. + # If the type restriction is ever loosened, please check safety of the `@inbounds`. + @inbounds state[end] += 1 + i = lastindex(state) + @inbounds while i > firstindex(state) && state[i] > max + state[i] = min + state[i-1] += 1 + i -= 1 end end From 19b6040e14971a092a1a5b8154b39cd7e2b6969f Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Mon, 5 May 2025 16:25:39 +0200 Subject: [PATCH 2/2] style: trailing white spaces --- src/permutations.jl | 6 +++--- test/permutations.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/permutations.jl b/src/permutations.jl index c1532ca..482333c 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -16,7 +16,7 @@ end function has_repeats(state::Vector{Int}) # This can be safely marked inbounds because of the type restriction in the signature. - # If the type restriction is ever loosened, please check safety of the `@inbounds` + # If the type restriction is ever loosened, please check safety of the `@inbounds`. @inbounds for outer in eachindex(state) for inner in (outer+1):lastindex(state) if state[outer] == state[inner] @@ -98,7 +98,7 @@ permutations(a) = permutations(a, length(a)) permutations(a, t) Generate all size `t` permutations of an indexable object `a`. -Only works for `a` with defined length. +Only works for `a` with defined length. If `(t <= 0) || (t > length(a))`, then returns an empty vector of eltype of `a` # Examples @@ -124,7 +124,7 @@ julia> [ (len, collect(permutations(1:3, len))) for len in -1:4 ] """ function permutations(a, t::Integer) if t == 0 - # Correct behavior for a permutation of length 0 is a vector containing a single empty vector + # Correct behavior for a permutation of length 0 is a vector containing a single empty vector return [Vector{eltype(a)}()] elseif t == 1 # Easy case, just return each element in its own vector diff --git a/test/permutations.jl b/test/permutations.jl index ae18b48..162cff7 100644 --- a/test/permutations.jl +++ b/test/permutations.jl @@ -30,7 +30,7 @@ end @test collect(permutations([], -1)) == Any[] @test collect(permutations([], 0)) == [Any[]] @test collect(permutations([], 1)) == Any[] - + @testset "permutation lengths" begin expected_lengths = [1, 5, 20, 60, 120, 120] ks = 0:5