diff --git a/src/permutations.jl b/src/permutations.jl index 6e270e5..93e1541 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -1,6 +1,7 @@ #Permutations export + derangements, levicivita, multiset_permutations, nthperm!, @@ -136,6 +137,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] @@ -249,8 +286,8 @@ julia> collect(multiset_permutations([1,1,2], 3)) ``` """ function multiset_permutations(a, t::Integer) - m = unique(collect(a)) - f = [sum([c == x for c in a]) for x in m] + m = unique(a) + f = [sum(c == x for c in a)::Int for x in m] multiset_permutations(m, f, t) end diff --git a/test/permutations.jl b/test/permutations.jl index 931cefe..b50d793 100644 --- a/test/permutations.jl +++ b/test/permutations.jl @@ -68,6 +68,18 @@ 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([])) == [[]] + @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)