Count Derangements (Permutation such that no element appears in its original position)

3.2

A Derangement is a permutation of n elements, such that no element appears in its original position. For example, a derangement of {0, 1, 2, 3} is {2, 3, 1, 0}.

Given a number n, find total number of Derangements of a set of n elements.

Examples:

Input: n = 2
Output: 1
For two elements say {0, 1}, there is only one 
possible derangement {1, 0}

Input: n = 3
Output: 2
For three elements say {0, 1, 2}, there are two 
possible derangements {2, 0, 1} and {1, 2, 0}

Input: n = 4
Output: 9
For four elements say {0, 1, 2, 3}, there are 9
possible derangements {1, 0, 3, 2} {1, 2, 3, 0}
{1, 3, 0, 2}, {2, 3, 0, 1}, {2, 0, 3, 1}, {2, 3,
1, 0}, {3, 0, 1, 2}, {3, 2, 0, 1} and {3, 2, 1, 0}

Let countDer(n) be count of derangements for n elements. Below is recursive relation for it.

countDer(n) = (n-1)*[countDer(n-1) + countDer(n-2)]

How does above recursive relation work?
There are n – 1 ways for element 0 (this explains multiplication with n-1).

Let 0 be placed at index i. There are now two possibilities, depending on whether or not element i is placed at 0 in return.

  1. i is placed at 0: This case is equivalent to solving the problem for n-2 elements as two elements have just swapped their positions.
  2. i is not placed at 0: This case is equivalent to solving the problem for n-1 elements as now there are n-1 elements, n-1 positions and every element has n-2 choices

Below is Simple Solution based on above recursive formula.

// A Naive Recursive C++ program to count derangements
#include <bits/stdc++.h>
using namespace std;

int countDer(int n)
{
   // Base cases
   if (n == 1)    return 0;
   if (n == 0)    return 1;
   if (n == 2)    return 1;

   // countDer(n) = (n-1)[countDer(n-1) + der(n-2)]
   return (n-1)*(countDer(n-1) + countDer(n-2));
}
 
// Driver program
int main()
{
    int n = 4;
    cout << "Count of Derangements is " << countDer(n);
    return 0;
}

Output:

Count of Derangements is 9

Time Complexity: T(n) = T(n-1) + T(n-2) which is exponential.
We can observe that this implementation does repeated work. For example see recursion tree for countDer(5), countDer(3) is being being evaluated twice.

cdr() ==> countDer()

                    cdr(5)   
                 /         \     
             cdr(4)          cdr(3)   
           /      \         /     \
       cdr(3)     cdr(2)  cdr(2)   cdr(1)

An Efficient Solution is to use Dynamic Programming to store results of subproblems in an array and build the array in bottom up manner.

// A Dynamic programming based C++ program to count derangements
#include <bits/stdc++.h>
using namespace std;

int countDer(int n)
{
    // Create an array to store counts for subproblems
    int der[n + 1];

    // Base cases
    der[0] = 1;
    der[1] = 0;
    der[2] = 1;

    // Fill der[0..n] in bottom up manner using above
    // recursive formula
    for (int i=3; i<=n; ++i)
        der[i] = (i-1)*(der[i-1] + der[i-2]);

    // Return result for n
    return der[n];
}

// Driver program
int main()
{
    int n = 4;
    cout << "Count of Derangements is " << countDer(n);
    return 0;
}

Output:

Count of Derangements is 9

Time Complexity: O(n)
Auxiliary Space: O(n)

Thanks to Utkarsh Trivedi for suggesting above solution.

References:
https://en.wikipedia.org/wiki/Derangement

Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above

GATE CS Corner    Company Wise Coding Practice

Recommended Posts:



3.2 Average Difficulty : 3.2/5.0
Based on 46 vote(s)










Writing code in comment? Please use ide.geeksforgeeks.org, generate link and share the link here.