Dynamic Programming · 4 of 60
Permutation Coefficient Problem
Count the ways to hand out k different prizes to n kids, one prize per kid, where who gets which prize matters.
- Simple way
- time O(2^n)
room O(n) - Clever way
- time O(k)
room O(1)
The problem, simply
You get two numbers, n and k. Count the ways to pick k things out of n and put them in order. This number is called P(n, k).
The formula is P(n, k) = n! / (n - k)!, which is the same as n x (n - 1) x ... x (n - k + 1), a product of k numbers. If k > n the answer is 0. Many versions ask for the answer modulo 1,000,000,007.
A story to remember it
Picture it
Three kids A, B and C and two prizes. Gold can go to 3 kids, then silver to 2 kids. That is 3 x 2 = 6 ways, the 6 bottom circles.
Examples
Two prizes, ten kids
- In
n = 10, k = 2- Out
90
10 choices for gold, then 9 for silver: 10 x 9.
The race
- In
n = 5, k = 3- Out
60
5 x 4 x 3.
Everyone gets a prize
- In
n = 4, k = 4- Out
24
When k = n this is n!, every way to line up all the kids.
No prizes
- In
n = 6, k = 0- Out
1
There is exactly one way to hand out nothing.
More prizes than kids
- In
n = 3, k = 5- Out
0
You cannot give 5 different kids a prize when there are only 3.
Way 1: the simple way
Think about the last kid, kid number n.
- Either that kid gets no prize. Then all
kprizes go to the othern - 1kids:P(n - 1, k)ways. - Or that kid gets one of the
kprizes (kchoices), and the otherk - 1prizes go to the othern - 1kids:k x P(n - 1, k - 1)ways.
So P(n, k) = P(n - 1, k) + k x P(n - 1, k - 1), with P(n, 0) = 1 and P(n, k) = 0 when k > n. Write it as a recursive function.
The calls for P(4, 2). Left child: the last kid gets no prize. Right child: the last kid gets a prize. Small numbers under the leaves are their answers. (2,1) and (1,1) are worked out more than once.
- checking
class Solution {
static final long MOD = 1_000_000_007L;
public long nPr(int n, int k) {
if (k == 0) return 1; // no prizes: one way
if (k > n) return 0; // more prizes than kids
long lastSkipped = nPr(n - 1, k); // last kid gets nothing
long lastWins = k * nPr(n - 1, k - 1) % MOD; // last kid gets one of k prizes
return (lastSkipped + lastWins) % MOD;
}
}How long it takes: each call makes two calls, so up to O(2^n) calls. Extra room: the call chain, O(n).
Way 2: the clever way
Hand out the prizes one at a time and keep a running answer. P(n, j) = P(n, j - 1) x (n - j + 1): the jth prize can go to any kid who does not have a prize yet, and there are n - j + 1 of them.
- Start with
ways = 1(zero prizes). - For
j = 1tok:ways = ways x (n - j + 1). - Return
ways.
Each step fills the next box of a small row of answers, and only the last box is needed, so one number is enough.
P(5, 3) one prize at a time
01 / 05
P(5, 0) = 1. Handing out zero prizes can be done in exactly one way: do nothing.
- kids left
- 5
- looking now
- checking
- answer
- just added
class Solution {
static final long MOD = 1_000_000_007L;
public long nPr(int n, int k) {
if (k > n) return 0;
long ways = 1; // zero prizes handed out
for (int j = 1; j <= k; j++) {
ways = ways * (n - j + 1) % MOD; // kids still without a prize
}
return ways;
}
}How long it takes: k multiplications, O(k). Extra room: one number, O(1).
Way 3: the full table
Some interviewers ask for the textbook table version, which turns Way 1 into a table. Row i holds P(i, j) for every j, and each cell uses the cell above it and the cell above and to the left.
P(5, 3) = P(4, 3) + 3 x P(4, 2) = 24 + 3 x 12 = 60. Cells where j is bigger than i are 0.
- checking
- answer
class Solution {
static final long MOD = 1_000_000_007L;
public long nPr(int n, int k) {
if (k > n) return 0;
long[][] P = new long[n + 1][k + 1];
for (int i = 0; i <= n; i++) {
P[i][0] = 1; // no prizes
for (int j = 1; j <= Math.min(i, k); j++) {
P[i][j] = (P[i - 1][j] + j * P[i - 1][j - 1]) % MOD;
}
}
return P[n][k];
}
}This takes O(n * k) time and O(n * k) room (or O(k) with one row filled from right to left). It is slower than Way 2, but it gives every P(i, j) at once, which helps when you have many questions.
Watch out for
k > nmust return 0, andk = 0must return 1.- Take the modulo after each multiply, not only at the end.
- Do not mix it up with
C(n, k), which ignores order.P(n, k) = C(n, k) x k!.
What interviewers ask next
- How is it related to combinations? Choosing the group ignores order,
C(n, k), then arranging the group givesk!orders. SoP = C x k!. - Answer many questions
P(n, k) mod pfast. Precompute factorials and inverse factorials up to the largestnonce. Then each answer isfact[n] x invFact[n - k], which isO(1). - What if
pis small, smaller thann? Thenn!is 0 modulopand the inverse trick breaks. The plain product from Way 2 still works, because it never divides.
Remember this
Grown-up words: handing out prizes in order is a permutation, P(n, k) is the permutation coefficient, the recursive rule is a Pascal style recurrence, the running answer is 1D dynamic programming, and the fast many question trick uses modular inverses.