From 7a54fe53a3cd0e084d82a11e1d794641a45a869b Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Fri, 15 Dec 2023 14:35:36 +0100 Subject: [PATCH 1/3] Add a function to generate all derangements. --- src/permutations.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/permutations.jl b/src/permutations.jl index b6ef788..8492948 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -1,6 +1,7 @@ #Permutations export + derangements, levicivita, multiset_permutations, nthperm!, @@ -134,6 +135,16 @@ 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. +""" +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] From a42609cbc2d7b65964f23c1dd05f1bc925cce246 Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Fri, 15 Dec 2023 14:46:39 +0100 Subject: [PATCH 2/3] Add tests for `derangements` --- test/permutations.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) 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) From 3db33bb801f29ed4ed7b935e863690a4a05afa3d Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Fri, 2 May 2025 17:43:47 +0200 Subject: [PATCH 3/3] Add doctest for `derangements` --- src/permutations.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/permutations.jl b/src/permutations.jl index 8492948..d0d213f 100644 --- a/src/permutations.jl +++ b/src/permutations.jl @@ -142,6 +142,32 @@ 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)))