Approach 1:
Simpler Idea:
Since the order matters, we will try to utilize next_permutation
algorithm. The next_permutation
function gives next lexicographically unique permutation.
Algorithm:
- Create a new array A of size equals to size of original array.
- Assign last k numbers 1,2,3,..k such that k ends last. Assign remaining numbers as 0.
- Now while iterating through permutations using
next_permutation
, select only indices in original array where value in A > 0, maintaning order.
Explanation for correctness:
Since in newly created array there are N numbers of which N-k are repeated, total unique permutations become N!/(N-k)! which gives us our desired outcome.
Example:
Input: X = [1,2,3], k=2
Now, we create A = [0,1,2].
All permutations:
[0, 1, 2],
[0, 2, 1],
[1, 0, 2],
[1, 2, 0],
[2, 0, 1],
[2, 1, 0].
Choose only indices i of these permutations from original array where A[i] > 0, which will yield,
[2,3],
[3,2],
[1,3],
[1,2],
[3,1],
[2,1].
If you want above in sorted order, use negative numbers and initialize first k numbers with -k,-k-1,..-1 and remaining with 0 and apply the algorithm with slight modification, by selecting index i in original array, such that A[i] < 0 while maintaining order.
Sidenote:
If order doesn't matter, initialize A
with k
-1s in the beginning and remaining 0 and use the iterative permutations algorithm which will generate unique possible k selections from N items.
Approach 2:(better than Approach 1)
Algorithm:
- Start by selecting first K numbers and populate in array
A
, it will store the index of chosen elements from original array. We mark it as the first selection.
- Now get all remaining permutations in lexicographical order.
- How to get next combination considering order? We will permute the selection if possible, otherwise return lexicographically greater selection.
Idea for getting next selection in lexicograhic order if permutations are exhausted:
We consider our current combination, and find the rightmost element
that has not yet reached its highest possible value. Once finding this
element, we increment it by 1, and assign the lowest valid value to
all subsequent elements.
from: https://cp-algorithms.com/combinatorics/generating_combinations.html
Example:
Input: X = [1,2,3,4], k=3
Now, we create A = [0,1,2].
All permutations:
[0,1,2] // initialization
[0,2,1] // next permutation
... // all permutations of [0,1,2]
...
[2,1,0] // reached last permutation of current selection
[0,1,3] // get next selection in lexicographic order as permutations are exhausted
...
[3,1,0] // reached last permutation of current selection
[0,2,3] // get next selection in lexicographic order as permutations are exhausted
...
[3,2,0] // reached last permutation of current selection
[1,2,3] // get next selection in lexicographic order as permutations are exhausted
...
[3,2,1] // reached last permutation of current selection
Code (0-based indexing, so start with 0-k-1 initialization):
bool next_combination_with_order(vector<int>& a, int n, bool order_matters=true) {
int k = (int)a.size();
if(order_matters && std::next_permutation(a.begin(), a.end()))return True; // check if next permutation exists otherwise move forward and get next unique combination
// if `a` was already in descending order,
// next_permutation returned false and changed the array to sorted order.
// now find next combination if possible
for (int i = k - 1; i >= 0; i--) {
if (a[i] < n - k + i + 1) {
a[i]++;
for (int j = i + 1; j < k; j++)
a[j] = a[j - 1] + 1;
return true;
}
}
return false;
}
References:
Next permutations:
https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
std::next_permutation Implementation Explanation
Next combination without order:
https://cp-algorithms.com/combinatorics/generating_combinations.html