diff --git a/src/permutations.jl b/src/permutations.jl index b6ef788..d0d213f 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -1,6 +1,7 @@ #Permutations export + derangements, levicivita, multiset_permutations, nthperm!, @@ -134,6 +135,42 @@ function permutations(a, t::Integer) return Permutations(a, t) end +""" + derangements(a) + +Generate all derangements of an indexable object `a` in lexicographic order. +Because the number of derangements can be very large, this function returns an iterator object. +Use `collect(derangements(a))` to get an array of all derangements. +Only works for `a` with defined length. + +# Examples +```jldoctest +julia> derangements("julia") |> collect +44-element Vector{Vector{Char}}: + ['u', 'j', 'i', 'a', 'l'] + ['u', 'j', 'a', 'l', 'i'] + ['u', 'l', 'j', 'a', 'i'] + ['u', 'l', 'i', 'a', 'j'] + ['u', 'l', 'a', 'j', 'i'] + ['u', 'i', 'j', 'a', 'l'] + ['u', 'i', 'a', 'j', 'l'] + ['u', 'i', 'a', 'l', 'j'] + ['u', 'a', 'j', 'l', 'i'] + ['u', 'a', 'i', 'j', 'l'] + ⋮ + ['a', 'j', 'i', 'l', 'u'] + ['a', 'l', 'j', 'u', 'i'] + ['a', 'l', 'u', 'j', 'i'] + ['a', 'l', 'i', 'j', 'u'] + ['a', 'l', 'i', 'u', 'j'] + ['a', 'i', 'j', 'u', 'l'] + ['a', 'i', 'j', 'l', 'u'] + ['a', 'i', 'u', 'j', 'l'] + ['a', 'i', 'u', 'l', 'j'] +``` +""" +derangements(a) = (d for d in multiset_permutations(a, length(a)) if all(t -> t[1] != t[2], zip(a, d))) + function nextpermutation(m, t, state) perm = [m[state[i]] for i in 1:t] diff --git a/test/permutations.jl b/test/permutations.jl index ae18b48..3098dba 100644 --- a/test/permutations.jl +++ b/test/permutations.jl @@ -66,6 +66,17 @@ end @test collect(multiset_permutations("", -1)) == Any[] @test length(multiset_permutations("aaaaaaaaaaaaaaaaaaaaab", 21)) == 22 + # derangements + @test length(collect(derangements(1:4))) == 9 + @test length(collect(derangements(1:8))) == derangement(8) == 14833 + @test collect(derangements(Int[])) == [Int[]] + @test collect(derangements([1])) == Vector{Int}[] + @test collect(derangements([1, 1])) == Vector{Int}[] + @test collect(derangements([1, 1, 2])) == Vector{Int}[] + @test collect(derangements([1, 1, 2, 2])) == [[2, 2, 1, 1]] + @test map(join, derangements("aabbc")) == ["bbaca", "bbcaa", "bcaab", "cbaab"] + @test map(join, derangements("aaabbbc")) == ["bbbaaca", "bbbacaa", "bbbcaaa", "bbcaaab", "bcbaaab", "cbbaaab"] + #nthperm! for n = 0:7, k = 1:factorial(n) p = nthperm!([1:n;], k)