Skip to content

Commit 53c5e71

Browse files
committed
Improve performance of derangement/subfactorial with iterative implementation
Use the recursive formula !n = (n-1) * (!(n-1) + !(n-2)) presented here: https://en.wikipedia.org/wiki/Derangement#Counting_derangements
1 parent e6888be commit 53c5e71

File tree

2 files changed

+12
-3
lines changed

2 files changed

+12
-3
lines changed

src/factorials.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,17 @@ Base.factorial(n::Integer, k::Integer) = factorial(promote(n, k)...)
3838
Compute the number of permutations of `n` with no fixed points, also known as the
3939
subfactorial. An alias `subfactorial` for this function is provided for convenience.
4040
"""
41-
function derangement(sn::Integer)
42-
n = BigInt(sn)
43-
return numerator(factorial(n) * sum([(-1)^k // factorial(k) for k = 0:n]))
41+
function derangement(n::Integer)
42+
if n < 0
43+
throw(DomainError(n, "n must be nonnegative"))
44+
elseif n <= 1
45+
return BigInt(1-n)
46+
end
47+
a, b = BigInt(1), BigInt(0)
48+
for i in 2:n
49+
a, b = b, (i-1)*(a+b)
50+
end
51+
return b
4452
end
4553
const subfactorial = derangement
4654

test/factorials.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# derangement
1212
@test derangement(4) == subfactorial(4) == 9
1313
@test derangement(24) == parse(BigInt, "228250211305338670494289")
14+
@test_throws DomainError derangement(-1)
1415

1516
# partialderangement
1617
@test partialderangement(7, 3) == 315

0 commit comments

Comments
 (0)