Fast way of getting r-long combinations of set A that have at least one element from set B, which is a subset of A - algorithm

For example, if A={0,1,2,3,4}, r=3 and B={1,4}, the result would be:
[0, 1, 2]
[0, 1, 3]
[0, 1, 4]
[0, 2, 4]
[0, 3, 4]
[1, 2, 3]
[1, 2, 4]
[1, 3, 4]
[2, 3, 4]
That's all the r-long combinations of A, excluding [0, 2, 3], because that one doesn't contain either 1 or 4.
The solution that I currently have is the following, using the fastest algorithm for getting normal combinations I know of, and just doing a simple check to see if combinations generated also contain an element of B (java):
int[] A = new int[]{0,1,2,3,4};
int[] B = new int[]{1,4};
int n = A.length;
int r = 3;
int[] picks = new int[r]; //Holds indexes of elements in A
for (int i = 0; i < picks.length; i++)
picks[i] = i;
int lastindex = picks.length - 1;
outer:
while (true) {
int at = lastindex;
while (true) {
picks[at] += 1;
if (picks[at] < n) {
int displacement = picks[at] - at; // at + displacement = picks[at], at + displacement + 1 = picks[at] + 1 ,...
// Make all picks elements after at y = picks[at] + x, so picks={0, 2, 4, 6, 18, 30} & at=3 --> picks={0, 2, 4, 5, 6, 7}
// (Note that this example will never take place in reality, because the 18 or the 30 would be increased instead, depending on what n is)
// Do the last one first, because that one is going to be the biggest,
picks[lastindex] = lastindex + displacement;
if (picks[lastindex] < n) { // and check if it doesn't overflow
for (int i = at + 1; i < lastindex; i++)
picks[i] = i + displacement;
int[] combination = new int[r];
for (int i = 0; i < r; i++)
combination[i] = A[picks[i]];
System.out.print(Arrays.toString(combination));
//^With this, all r-long combinations of A get printed
//Straightforward, bruteforce-ish way of checking if int[] combination
//contains any element from B
presence:
for (int p : combination) {
for (int b : B) {
if (p==b) {
System.out.print(" <-- Also contains an element from B");
break presence;
}
}
}
System.out.println();
break;
}
}
at--;
if (at < 0) {
//Moving this check to the start of the while loop will make this natively support pick 0 cases (5C0 for example),
//but reduce performance by I believe quite a bit. Probably better to special-case those (I haven't
// done that in this test tho)
break outer;
}
}
}
output:
[0, 1, 3] <-- Also contains an element from B
[0, 1, 4] <-- Also contains an element from B
[0, 2, 3]
[0, 2, 4] <-- Also contains an element from B
[0, 3, 4] <-- Also contains an element from B
[1, 2, 3] <-- Also contains an element from B
[1, 2, 4] <-- Also contains an element from B
[1, 3, 4] <-- Also contains an element from B
[2, 3, 4] <-- Also contains an element from B
As written in the comments, I believe this method to be very rudimentary. Can anyone think of a faster way to do this?

Assuming you have a int[][] FindCombinations(int[] set, int length) function that returns a list of all the length-long combinations of set, do the following (pseudo-code):
for i=1 to B.length
{
int bi = B[i];
A = A - bi; // remove bi from A
foreach C in FindCombinations(A, r-1)
{
output C+bi // output the union of C and {bi}
}
}
This way all combinations contain at least one element from B (and may also contain elements of B that have not yet been used) without much extra work. All other combinations are eliminated at no cost (the don't have to be found at all) and also the test that a combination contains an element from B for each combination is also eliminated.
Whether this algorithm is faster, greatly depends on how efficently you can add/remove elements from a set and the percentage of included vs excluded combinations (i.e. if you only end up excluding 1% of the total combinations it is probably not worth it)
Note that when getting the combinations to union with {b[i]} these may also contain an element B[j] where j>i. When you get to the point that you get the combinations to union with B[j] none of them will contain B[i], so all combinations are unique.

Related

Length of maximum continuous subarray with 2 unique numbers

I have an array of numbers and I want to figure out the maximum length of a continuous subarray of 2 unique numbers repeating.
For example, [2, 3, 4, 3, 2, 2, 4] would return 3 since [3, 2, 2] is of length 3.
[2, 4, 2, 5, 1, 5, 4, 2] would return 3.
[7, 8, 7, 8, 7] would return 5.
Edit: I have considered an O(n^2) solution where I start at each value in the array and iterate until I see a third unique value.
for item in array:
iterate until third unique element
if length of this iteration is greater than existing max, update the max length
return maxlength
I do not, however, think this is an efficient solution.
It can be done O(n). The code is in python3. o and t are one and two respectively. m is the max and c is the current count variable.
a = [7, 8, 7, 8, 7]
m = -1
o = a[0]
t = a[1]
# in the beginning one and two are the first 2 numbers
c = 0
index = 0
for i in a:
if i == o or i == t:
# if current element is either one or two current count is increased
c += 1
else:
# if current element is neither one nor two then they are updated accordingly and max is updated
o = a[index - 1]
t = a[index]
m = max(m, c)
c = 2
index += 1
m = max(m, c)
print(m)
We can use two pointer technique to solve this problem in O(n) run time complexity. These two pointer for example startPtr and endPtr will represent the range in the array. We will maintain this range [startPtr, endPtr] in such way that it contains no more than 2 unique number. We can do this by keeping track of position of the 2 unique number. My implement in C++ is given below:
int main()
{
int array[] = {1,2,3,3,2,3,2,3,2,2,2,1,3,4};
int startPtr = 0;
int endPtr = 0;
// denotes the size of the array
int size= sizeof(array)/sizeof(array[0]);
// contain last position of unique number 1 in the range [startPtr, endPtr]
int uniqueNumPos1 = -1; // -1 value represents it is not set yet
// contain last position of unique number 2 in the range [startPtr, endPtr]
int uniqueNumPos2 = -1; // -1 value represents it is not set yet
// contains length of maximum continuous subarray with 2 unique numbers
int ans = 0;
while(endPtr < size) {
if(uniqueNumPos1 == -1 || array[endPtr] == array[uniqueNumPos1]) {
uniqueNumPos1 = endPtr;
}
else {
if(uniqueNumPos2 == -1 || array[endPtr] == array[uniqueNumPos2]) {
uniqueNumPos2 = endPtr;
}
else {
// for this new third unique number update startPtr with min(uniqueNumPos1, uniqueNumPos2) + 1
// to ensure [startPtr, endPtr] does not contain more that two unique
startPtr = min(uniqueNumPos1, uniqueNumPos2) + 1;
// update uniqueNumPos1 and uniqueNumPos2
uniqueNumPos1 = endPtr -1;
uniqueNumPos2 = endPtr;
}
}
// this conditon is to ensure the range contain exactly two unique number
// if you are looking for the range containing less than or equal to two unique number, then you can omit this condition
if (uniqueNumPos1 != -1 && uniqueNumPos2 !=-1) {
ans = max( ans, endPtr - startPtr + 1);
}
endPtr++;
}
printf("%d\n", ans);
}
Thanks #MBo for pointing out the mistakes.
import java.util.Arrays;
import static java.lang.System.out;
class TestCase{
int[] test;
int answer;
TestCase(int[] test,int answer){
this.test = test;
this.answer = answer;
}
}
public class Solution {
public static void main(String[] args) {
TestCase[] tests = {
new TestCase(new int[]{2, 3, 4, 3, 2, 2, 4},3),
new TestCase(new int[]{2, 3, 3, 3, 3, 4, 3, 3, 2, 2, 4},7),
new TestCase(new int[]{1,2,3,3,4,2,3,2,3,2,2,2,1,3,4},7),
new TestCase(new int[]{2, 7, 8, 7, 8, 7},5),
new TestCase(new int[]{-1,2,2,2,2,2,2,2,2,2,2,-1,-1,4},13),
new TestCase(new int[]{1,2,3,4,5,6,7,7},3),
new TestCase(new int[]{0,0,0,0,0},0),
new TestCase(new int[]{0,0,0,2,2,2,1,1,1,1},7),
new TestCase(new int[]{},0)
};
for(int i=0;i<tests.length;++i){
int ans = maxContiguousArrayWith2UniqueElements(tests[i].test);
out.println(Arrays.toString(tests[i].test));
out.println("Expected: " + tests[i].answer);
out.println("Returned: " + ans);
out.println("Result: " + (tests[i].answer == ans ? "ok" : "not ok"));
out.println();
}
}
private static int maxContiguousArrayWith2UniqueElements(int[] A){
if(A == null || A.length <= 1) return 0;
int max_subarray = 0;
int first_number = A[0],second_number = A[0];
int start_index = 0,same_element_run_length = 1;
for(int i=1;i<A.length;++i){
if(A[i] != A[i-1]){
if(first_number == second_number){
second_number = A[i];
}else{
if(A[i] != first_number && A[i] != second_number){
max_subarray = Math.max(max_subarray,i - start_index);
start_index = i - same_element_run_length;
first_number = A[i-1];
second_number = A[i];
}
}
same_element_run_length = 1;
}else{
same_element_run_length++;
}
}
return first_number == second_number ? max_subarray : Math.max(max_subarray,A.length - start_index);
}
}
OUTPUT:
[2, 3, 4, 3, 2, 2, 4]
Expected: 3
Returned: 3
Result: ok
[2, 3, 3, 3, 3, 4, 3, 3, 2, 2, 4]
Expected: 7
Returned: 7
Result: ok
[1, 2, 3, 3, 4, 2, 3, 2, 3, 2, 2, 2, 1, 3, 4]
Expected: 7
Returned: 7
Result: ok
[2, 7, 8, 7, 8, 7]
Expected: 5
Returned: 5
Result: ok
[-1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, 4]
Expected: 13
Returned: 13
Result: ok
[1, 2, 3, 4, 5, 6, 7, 7]
Expected: 3
Returned: 3
Result: ok
[0, 0, 0, 0, 0]
Expected: 0
Returned: 0
Result: ok
[0, 0, 0, 2, 2, 2, 1, 1, 1, 1]
Expected: 7
Returned: 7
Result: ok
[]
Expected: 0
Returned: 0
Result: ok
Algorithm:
So, we maintain 2 variables first_number and second_number which will hold those 2 unique numbers.
As you know, there could be many possible subarrays we have to consider to get the max subarray length which has 2 unique elements. Hence, we need a pointer variable which will point to start of a subarray. In this, that pointer is start_index.
Any subarray breaks when we find a third number which is not equal to first_number or second_number. So, now, we calculate the previous subarray length(which had those 2 unique elements) by doing i - start_index.
Tricky part of this question is how to get the start_index of the next subarray.
If you closely observe, second_number of previous subarray becomes first_number of current subarray and third number we encountered just now becomes second_number of this current subarray.
So, one way to calculate when this first_number started is to run a while loop backwards to get that start_index. But that would make the algorithm O(n^2) if there are many subarrays to consider(which it will be).
Hence, we maintain a variable called same_element_run_length which just keeps track of the length or frequency of how many times the number got repeated and gets updated whenever it breaks. So, start_index for the next subarray after we encounter the third number becomes start_index = i - same_element_run_length.
Rest of the computation done is self-explanatory.
Time Complexity: O(n), Space Complexity : O(1).

Dynamic programming (Solve combination of scores) [duplicate]

It was one of my interview question, and I could not think of the good way to get number N. (plus, I did not understand the American football scoring system as well)
6 points for the touchdown
1 point for the extra point (kicked)
2 points for a safety or a conversion (extra try after a touchdown)
3 points for a field goal
What would be an efficient algorithm to get all combinations of point-accumulations necessary to get a certain score N?
Assuming here you are looking for a way to get number of possibilities and not the actual possibilities.
First let's find a recursive function:
f(n) = (f(n-6) >= 0? f(n-6) : 0) + (f(n-1) >= 0 ? f(n-1) : 0) + (f(n-2) >= 0 ? f(n-2) : 0) + (f(n-3) >= 0 ? f(n-3) : 0)
base: f(0) = 1 and f(n) = -infinity [n<0]
The idea behind it is: You can always get to 0, by a no scoring game. If you can get to f(n-6), you can also get to f(n), and so on for each possibility.
Using the above formula one can easily create a recursive solution.
Note that you can even use dynamic programming with it, initialize a table with [-5,n], init f[0] = 0 and f[-1] = f[-2] = f[-3] = f[-4] = f[-5] = -infinity and iterate over indexes [1,n] to achieve the number of possibilities based on the the recursive formula above.
EDIT:
I just realized that a simplified version of the above formula could be:
f(n) = f(n-6) + f(n-1) + f(n-2) + f(n-3)
and base will be: f(0) = 1, f(n) = 0 [n<0]
The two formulas will yield exactly the same result.
This is identical to the coin change problem, apart from the specific numbers used. See this question for a variety of answers.
You could use dynamic programming loop from 1 to n, here is some pseudo code:
results[1] = 1
for i from 1 to n :
results[i+1] += results[i]
results[i+2] += results[i]
results[i+3] += results[i]
results[i+6] += results[i]
this way complexity is O(N), instead of exponential complexity if you compute recursively by subtracting from the final score... like computing a Fibonacci series.
I hope my explanation is understandable enough..
I know this question is old, but all of the solutions I see help calculate the number of scoring permutations rather than the number of scoring combinations. (So I think either something like this should be an answer or the question title should be changed.)
Some code such as the following (which could then be converted into a dp) will calculate the number of possible combinations of different scores:
int getScoreCombinationCount(int score, int scoreVals[], int scoreValIndex) {
if (scoreValIndex < 0)
return 0;
if (score == 0)
return 1;
if (score < 0)
return 0;
return getScoreCombinationCount(score - scoreVals[scoreValIndex], scoreVals, scoreValIndex) +
getScoreCombinationCount(score, scoreVals, scoreValIndex - 1);
}
This solution, implemented based on a solution in the book Elements of Programming Interviews seems to be correct for counting the number of 'combinations' (no duplicate sets) for a set of score points.
For example, if points = {7, 3, 2}, there are 2 combinations for a total score of 7:
{7} and {3, 2, 2}.
public static int ScoreCombinationCount(int total, int[] points)
{
int[] combinations = new int[total + 1];
combinations[0] = 1;
for (var i = 0; i < points.Length; i++)
{
int point = points[i];
for (var j = point; j <= total; j++)
{
combinations[j] += combinations[j - point];
}
}
return combinations[total];
}
I am not sure I understand the logic though. Can someone explain?
The answer to this question depends on whether or not you allow the total number of combinations to include duplicate unordered combinations.
For example, in American football, you can score 2, 3, or 7 points (yes, I know you can miss the extra point on a touchdown, but let's ignore 1 point).
Then if your target N is 5, then you can reach it with {2, 3} or {3, 2}. If you count that as two combinations, then the Dynamic Programming solution by #amit will work. However, if you count those two combinations as one combination, then the iterative solution by #Maximus will work.
Below is some Java code, where findWays() corresponds to counting all possible combinations, including duplicates, and findUniqueWays() corresponds to counting only unique combinations.
// Counts the number of non-unique ways to reach N.
// Note that this algorithm counts {1,2} separately from {2,1}
// Applies a recurrence relationship. For example, with values={1,2}:
// cache[i] = cache[i-1] + cache[i-2]
public static long findWays(int N, int[] values) {
long cache[] = new long[N+1];
cache[0] = 1;
for (int i = 1; i <= N; i++) {
cache[i] = 0;
for (int value : values) {
if (value <= i)
cache[i] += cache[i-value];
}
}
return cache[N];
}
// Counts the number of unique ways to reach N.
// Note that this counts truly unique combinations: {1,2} is the same as {2,1}
public static long findUniqueWays(int N, int[] values) {
long [] cache = new long[N+1];
cache[0] = 1;
for (int i = 0; i < values.length; i++) {
int value = values[i];
for (int j = value; j <= N; j++) {
cache[j] += cache[j-value];
}
}
return cache[N];
}
Below is a test case where the possible points are {2,3,7}.
private static void testFindUniqueWaysFootball() {
int[] points = new int[]{2, 3, 7}; // Ways of scoring points.
int[] NValues = new int[]{5, 7, 10}; // Total score.
long result = -1;
for (int N : NValues) {
System.out.printf("\nN = %d points\n", N);
result = findWays(N, points);
System.out.printf("findWays() result = %d\n", result);
result = findUniqueWays(N, points);
System.out.printf("findUniqueWays() result = %d\n", result);
}
}
The output is:
N = 5 points
findWays() result = 2
findUniqueWays() result = 1
N = 7 points
findWays() result = 4
findUniqueWays() result = 2
N = 10 points
findWays() result = 9
findUniqueWays() result = 3
The results above show that to reach N=7 points, then there 4 non-unique ways to do so (those ways are {7}, {2,2,3}, {2,3,2}, {3,2,2}). However, there are only 2 unique ways (those ways are {7} and {2,2,3}). However, .
Below is a python program to find all combinations ignoring the combination order (e.g. 2,3,6 and 3,2,6 are considered one combination). This is a dynamic programming solution with order(n) time. Scores are 2,3,6,7.
We traverse from row score 2 to row score 7 (4 rows). Row score 2 contains the count if we only consider score 2 in calculating the number of combinations. Row score 3 produces each column by taking the count in row score 2 for the same final score plus the previous 3 count in its own row (current position minus 3). Row score 6 uses row score 3, which contains counts for both 2,3 and adds in the previous 6 count (current position minus 6). Row score 7 uses row score 6, which contains counts for row scores 2,3,6 plus the previous 7 count.
For example, numbers[1][12] = numbers[0][12] + numbers[1][9] (9 = 12-3) which results in 3 = 1 + 2; numbers[3][12] = numbers[2][12] + numbers[3][9] (9 = 12-3) which results in 7 = 6 + 1;
def cntMoney(num):
mSz = len(scores)
numbers = [[0]*(1+num) for _ in range(mSz)]
for mI in range(mSz): numbers[mI][0] = 1
for mI,m in enumerate(scores):
for i in range(1,num+1):
numbers[mI][i] = numbers[mI][i-m] if i >= m else 0
if mI != 0: numbers[mI][i] += numbers[mI-1][i]
print('m,numbers',m,numbers[mI])
return numbers[mSz-1][num]
scores = [2,3,6,7]
num = 12
print('score,combinations',num,cntMoney(num))
output:
('m,numbers', 2, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
('m,numbers', 3, [1, 0, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 3])
('m,numbers', 6, [1, 0, 1, 1, 1, 1, 3, 1, 3, 3, 3, 3, 6])
('m,numbers', 7, [1, 0, 1, 1, 1, 1, 3, 2, 3, 4, 4, 4, 7])
('score,combinations', 12, 7)
Below is a python program to find all ordered combinations (e.g. 2,3,6 and 3,2,6 are considered two combinations). This is a dynamic programming solution with order(n) time. We build up from the start, adding the combinations calculated from previous score numbers, for each of the scores (2,3,6,7).
'vals[i] += vals[i-s]' means the current value equals the addition of the combinations from the previous values for the given scores. For example, for column vals[12] = the addition of scores 2,3,6,7: 26 = 12+9+3+2 (i-s = 10,9,6,5).
def allSeq(num):
vals = [0]*(num+1)
vals[0] = 1
for i in range(num+1):
for s in scores:
if i-s >= 0: vals[i] += vals[i-s]
print(vals)
return vals[num]
scores = [2,3,6,7]
num = 12
print('num,seqsToNum',num,allSeq(num))
Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsToNum', 12, 26)
Attached is a program that prints the sequences for each score up to the given final score.
def allSeq(num):
seqs = [[] for _ in range(num+1)]
vals = [0]*(num+1)
vals[0] = 1
for i in range(num+1):
for sI,s in enumerate(scores):
if i-s >= 0:
vals[i] += vals[i-s]
if i == s: seqs[i].append(str(s))
else:
for x in seqs[i-s]:
seqs[i].append(x + '-' + str(s))
print(vals)
for sI,seq in enumerate(seqs):
print('num,seqsSz,listOfSeqs',sI,len(seq),seq)
return vals[num],seqs[num]
scores = [2,3,6,7]
num = 12
combos,seqs = allSeq(num)
Output:
[1, 0, 1, 1, 1, 2, 3, 4, 6, 9, 12, 18, 26]
('num,seqsSz,listOfSeqs', 0, 0, [])
('num,seqsSz,listOfSeqs', 1, 0, [])
('num,seqsSz,listOfSeqs', 2, 1, ['2'])
('num,seqsSz,listOfSeqs', 3, 1, ['3'])
('num,seqsSz,listOfSeqs', 4, 1, ['2-2'])
('num,seqsSz,listOfSeqs', 5, 2, ['3-2', '2-3'])
('num,seqsSz,listOfSeqs', 6, 3, ['2-2-2', '3-3', '6'])
('num,seqsSz,listOfSeqs', 7, 4, ['3-2-2', '2-3-2', '2-2-3', '7'])
('num,seqsSz,listOfSeqs', 8, 6, ['2-2-2-2', '3-3-2', '6-2', '3-2-3', '2-3-3', '2-6'])
('num,seqsSz,listOfSeqs', 9, 9, ['3-2-2-2', '2-3-2-2', '2-2-3-2', '7-2', '2-2-2-3', '3-3-3', '6-3', '3-6', '2-7'])
('num,seqsSz,listOfSeqs', 10, 12, ['2-2-2-2-2', '3-3-2-2', '6-2-2', '3-2-3-2', '2-3-3-2', '2-6-2', '3-2-2-3', '2-3-2-3', '2-2-3-3', '7-3', '2-2-6', '3-7'])
('num,seqsSz,listOfSeqs', 11, 18, ['3-2-2-2-2', '2-3-2-2-2', '2-2-3-2-2', '7-2-2', '2-2-2-3-2', '3-3-3-2', '6-3-2', '3-6-2', '2-7-2', '2-2-2-2-3', '3-3-2-3', '6-2-3', '3-2-3-3', '2-3-3-3', '2-6-3', '3-2-6', '2-3-6', '2-2-7'])
('num,seqsSz,listOfSeqs', 12, 26, ['2-2-2-2-2-2', '3-3-2-2-2', '6-2-2-2', '3-2-3-2-2', '2-3-3-2-2', '2-6-2-2', '3-2-2-3-2', '2-3-2-3-2', '2-2-3-3-2', '7-3-2', '2-2-6-2', '3-7-2', '3-2-2-2-3', '2-3-2-2-3', '2-2-3-2-3', '7-2-3', '2-2-2-3-3', '3-3-3-3', '6-3-3', '3-6-3', '2-7-3', '2-2-2-6', '3-3-6', '6-6', '3-2-7', '2-3-7'])
~

An efficient method to generate all possible ways to pair up items in a data set

This is somewhat of a combinatorial problem; I'm trying to figure out an efficient way to pair up all items in a data set.
For example, I have an array of length 6: [1,2,3,4,5,6], and I want to make all possible pairings of the contents in the array as so:
[1,2],[3,4],[5,6]
[1,2],[3,5],[4,6]
[1,2],[3,6],[4,5]
[1,3],[2,4],[5,6]
[1,3],[2,5],[4,6]
...
and so on. In this example, there would be 15 combinations total. In general, the number of possibilities in this solution should be (N-1)!! where N is the length of the array or the number of items to be paired up. N will always be even in this case. Ideally, an algorithm will generate all possibilities serially, without having to store very large arrays.
For example, one way would work somewhat like a 'round robin' scheduling algorithm where you split the array into N/2:
[1,2,3]
[4,5,6]
and rotate the [5,3,6] clockwise to generate 3 unique pairings (counting vertically) with [1,2,4] fixed as so:
[1,2,3]
[4,5,6]
[1,2,5]
[4,6,3]
[1,2,6]
[4,3,5]
and then rotate [4,2,3,6,5] clockwise to generate 5 unique pairings with [1] fixed, going from the smallest innermost case (N=4) outwards until the end, but recursively. As it is I'm not sure how to best express this as code or if there is a more efficient way of doing this, as N can be very large.
You can generate the pairs using the standard recursive algorithm for generating permutations of a list, but with a twist that each time you recurse you advance 2 elements along the list, and the first remaining element in the list is always the first element of the pair you output at each recursion, with the second of the pair being each of the other remaining elements.
Always choosing the first remaining element as the first item in the pair avoids generating the same pairings with the pairs in different order.
As with the standard algorithm, you can generate the permutations in place without making copies of the array, by swapping elements in place, recursing and then swapping back.
Here is some C code to demonstrate the algorithm (I realise this question is tagged Fortran but just think of it as pseudo code). This code passes the list and count when it recurses, but you could implement it with items and itemcount as global variables:
// start is the current position in the list, advancing by 2 each time
// pass 0 as start when calling at the top level
void generatePairings(int* items, int itemcount, int start)
{
if(itemcount & 1)
return; // must be an even number of items
// is this a complete pairing?
if(start == itemcount)
{
// output pairings:
int i;
for(i = 0; i<itemcount; i+=2)
{
printf("[%d, %d] ", items[i], items[i+1]);
}
printf("\n");
return;
}
// for the next pair, choose the first element in the list for the
// first item in the pair (meaning we don't have to do anything
// but leave it in place), and each of the remaining elements for
// the second item:
int j;
for(j = start+1; j<itemcount; j++)
{
// swap start+1 and j:
int temp = items[start+1];
items[start+1] = items[j];
items[j] = temp;
// recurse:
generatePairings(items, itemcount, start+2);
// swap them back:
temp = items[start+1];
items[start+1] = items[j];
items[j] = temp;
}
}
int main(void) {
int items[6] = {1, 2, 3, 4, 5, 6};
generatePairings(items, 6, 0);
return 0;
}
Output:
[1, 2] [3, 4] [5, 6]
[1, 2] [3, 5] [4, 6]
[1, 2] [3, 6] [5, 4]
[1, 3] [2, 4] [5, 6]
[1, 3] [2, 5] [4, 6]
[1, 3] [2, 6] [5, 4]
[1, 4] [3, 2] [5, 6]
[1, 4] [3, 5] [2, 6]
[1, 4] [3, 6] [5, 2]
[1, 5] [3, 4] [2, 6]
[1, 5] [3, 2] [4, 6]
[1, 5] [3, 6] [2, 4]
[1, 6] [3, 4] [5, 2]
[1, 6] [3, 5] [4, 2]
[1, 6] [3, 2] [5, 4]
If you are doing this on a list of large objects, it's more efficient to permute a list of indices and then use those to index into your array of objects, rather than doing expensive swap operations on large amounts of data.
Wow. Now there's a blast from the past. I wrote about this back in 1993 and provided Pascal source code for it. Surprisingly, the article in which it appeared is available online at http://www.drdobbs.com/database/algorithm-alley/184409099#02e5_000d.
Basically, I adapted a selection sort algorithm:
for x = 0 to n-2
for y = x+1 to n-1
write x, y
The problem with that approach is that it generates {1,2},{1,3},{1,4},{2,3},{2,4}...
It turns out that you can modify that output by maintaining a swap array that you manipulate after every iteration of the outer loop. Here's the Pascal source code that I supplied with the article.
(* ----------------------------------------------------------- *(
** pairings.pas -- Select sports-event team pairings **
** ------------------------------------------------------------**
** This program generates team pairings for sports events. **
** Each team is guaranteed to play each other team exactly **
** once. No team will play more than one game per day. **
** An asterisk ('*') means a day off for that team. **
** For example, 5 teams produces this output: **
** Day 1 - 12 34 5* **
** Day 2 - 13 25 4* **
** Day 3 - 14 2* 35 **
** Day 4 - 15 3* 24 **
** Day 5 - 1* 45 23 **
** ------------------------------------------------------------**
** Copyright (c) 1993 by Jim Mischel. All rights reserved. **
)* ----------------------------------------------------------- *)
program pairings;
const
TEAMCOUNT = 5;
var
TeamNames: Array [1 .. TEAMCOUNT + 1] of Char;
SwapArray: Array [1 .. TEAMCOUNT + 1] of Integer;
x, Temp, Day: Integer;
TempChar: Char;
const
NTeams: Integer = TEAMCOUNT;
begin
{ Set up team names. Normally read from a file. }
for x := 1 to NTeams do
TeamNames[x] := Chr(x + Ord('0'));
if Odd(NTeams) then
begin
NTeams := NTeams + 1;
TeamNames[NTeams] := '*'
end;
{ Set up the array that controls swapping. }
for x := 1 to NTeams do
SwapArray[x] := x;
for Day := 1 to NTeams - 1 do
begin
Write('Day ', Day, ' -');
{ Write the team pairings for this day }
x := 1;
while x < NTeams do
begin
Write(' ', TeamNames[x], TeamNames[x + 1]);
x := x + 2;
end;
WriteLn;
{ Perform swaps to prepare array for next day's pairings. }
if Odd(Day)
then x := 2
else x := 3;
while x < NTeams do
begin
TempChar := TeamNames[SwapArray[x]];
TeamNames[SwapArray[x]] := TeamNames[SwapArray[x + 1]];
TeamNames[SwapArray[x + 1]] := TempChar;
Temp := SwapArray[x];
SwapArray[x] := SwapArray[x + 1];
SwapArray[x + 1] := Temp;
x := x + 2
end
end
end.
For those looking for less of a mathematical solution but utilizing data structure in JS. A combination of maps, set, and recursion:
function findUnmatchedCouples(people, couples) {
const copy = [...people]
if (copy.length == 0) {
return;
}
const p = copy[0];
copy.splice(0, 1);
for (let i = 0; i < copy.length; i++) {
const q = copy[i];
if (matchedThisRound.has(p)) {
continue;
}
if (!isCouple(p, q)) {
copy.splice(i, 1);
findUnmatchedCouples(copy, couples);
couples.push([p, q]);
matchedThisRound.add(p);
registerCouple(p, q)
}
}
return couples;
}
function isCouple(p, q) {
if (!registered.has(p)) {
return false;
} else {
const currentset = registered.get(p)
if (currentset.has(q)) {
//console.log(`${p} already matched with ${q}`)
return true;
}
currentset.add(q)
registered.set(p, currentset)
}
return false;
}
function registerCouple(p, q) {
if (!registered.has(p)) {
registered.set(p, new Set([q]))
} else {
const currentset = registered.get(p)
currentset.add(q)
registered.set(p, currentset)
}
}
//Start Secret Santa
const people = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
for (let i = 0; i < 10; i++) {
matchedThisRound = new Set([]);
console.log(`round ${i}`)
console.log(findUnmatchedCouples(people, []))
}

How do you check if one array is a subsequence of another?

I'm looking to explore different algorithms, both recursive and dynamic programming, that checks if one arrayA is a subsequence of arrayB. For example,
arrayA = [1, 2, 3]
arrayB = [5, 6, 1, 7, 2, 9, 3]
thus, arrayA is indeed a subsequence of arrayB.
I've tried a few different searches, but all I can seem to find is algorithms to compute the longest increasing subsequence.
Since you must match all elements of arrayA to some elements of arrayB, you never need to backtrack. In other words, if there are two candidates in arrayB to match an element of arrayA, you can pick the earliest one, and never retract the choice.
Therefore, you do not need DP, because a straightforward linear greedy strategy will work:
bool isSubsequence(int[] arrayA, int[] arrayB) {
int startIndexB = 0;
foreach (int n in arrayA) {
int next = indexOf(arrayB, startIndexB , n);
if (next == NOT_FOUND) {
return false;
}
startIndexB = next+1;
}
return true;
}
As dasblinkenlight has correctly said(and i could not have phrased it better than his answer!!) a greedy approach works absolutely fine. You could use the following pseudocode (with just a little more explanation but totally similar to what dasblinkenlight has written)which is similar to the merging of two sorted arrays.
A = {..}
B = {..}
j = 0, k = 0
/*j and k are variables we use to traverse the arrays A and B respectively*/
for(j=0;j<A.size();){
/*We know that not all elements of A are present in B as we
have reached end of B and not all elements of A have been covered*/
if(k==B.size() && j<A.size()){
return false;
}
/*we increment the counters j and k both because we have found a match*/
else if(A[j]==B[k]){
j++,k++;
}
/*we increment k in the hope that next element may prove to be an element match*/
else if(A[j]!=B[k]){
k++;
}
}
return true; /*cause if we have reached this point of the code
we know that all elements of A are in B*/
Time Complexity is O(|A|+|B|) in the worst case, where |A| & |B| are the number of elements present in Arrays A and B respectively. Thus you get a linear complexity.
As #sergey mentioned earlier, there is no need to do backtracking in this case.
Here just another Python version to the problem: [Time complexity: O(n) - worst]
>>> A = [1, 2, 3]
>>> B = [5, 6, 1, 7, 8, 2, 4, 3]
>>> def is_subsequence(A, B):
it = iter(B)
return all(x in it for x in A)
>>> is_subsequence(A, B)
True
>>> is_subsequence([1, 3, 4], B)
False
>>>
Here is an example in Ruby:
def sub_seq?(a_, b_)
arr_a = [a_,b_].max_by(&:length);
arr_b = [a_,b_].min_by(&:length);
arr_a.select.with_index do |a, index|
arr_a.index(a) &&
arr_b.index(a) &&
arr_b.index(a) <= arr_a.index(a)
end == arr_b
end
arrayA = [1, 2, 3]
arrayB = [5, 6, 1, 7, 2, 9, 3]
puts sub_seq?(arrayA, arrayB).inspect #=> true
Here is an example in GOLANG...
func subsequence(first, second []int) bool {
k := 0
for i := 0; i < len(first); i++ {
j := k
for ; j < len(second); j++ {
if first[i] == second[j] {
k = j + 1
break
}
}
if j == len(second) {
return false
}
}
return true
}
func main(){
fmt.Println(subsequence([]int{1, 2, 3}, []int{5, 1, 3, 2, 4}))
}

Getting ALL permutations of ALL sublists of a list of integers

I've been having trouble with this problem. Basically, I have a list of integers, such as
list = [1, 2, 3]
I want to get all possible permutations of every subset. I know similar questions exist online, but I couldn't find one that does every permutation as well as every subset. In other words, I want:
function(list) =
[], [1], [2], [3],
[1, 2], [2, 1], [1, 3], [3,1], [2, 3], [3,2],
[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]
I understand the output will get extremely large even for a small input list size. Unfortunately, I just cannot figure out how to do such a problem.
Thank you!
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
public class Test {
private static boolean[] used;
private static int[] a;
private static void f(int curCount,int subsetSize,ArrayDeque<Integer> perm){
// System.out.println("in rec "+curCount+" "+subsetSize);
if(curCount < subsetSize){
for(int i=0;i<a.length;i++) {
if (!used[i]) { // try to add i-th elem of array as a next element of permutation if it's not busy
perm.add(a[i]);
used[i] = true; //mark i-th element as used for future recursion calls
f(curCount + 1, subsetSize,perm); // curCount+1 because we added elem to perm. subsetSize is const and it's needed just for recursion exit condition
used[i] = false; // "free" i-th element
perm.removeLast();
}
}
}
else{ //some permutation of array subset with size=subsetSize generated
for(Integer xx:perm) System.out.print(xx+" ");
System.out.println();
}
}
public static void main(String[] args){
a = new int[]{1,2,3};
used = new boolean[a.length];
Arrays.fill(used, false);
// second param is a subset size (all sizes from 1 to n)
// first param is number of "collected" numbers, when collected numbers==required subset size (firstparam==second param) exit from recursion (from some particular call-chain)
// third param is data structure for constructing permutation
for(int i=1;i<=a.length;i++)f(0,i,new ArrayDeque<Integer>());
} //end of main
} //end of class
output
1 2 3 1 2 1 3 2 1 2 3 3 1
3 2 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2
3 2 1
So what you are looking for is all the possible permutations of the Power Set.
This seems to go into some depth about strategies for doing this.
If your list in N elements long, you want to get all the combinations of N taken by M, where M is between 1 and N. For each of the combination you want to get all the permutations. You can probably figure out algorithms for combinations and permutations via google.
I ended up using a combination of these two functions. Not sure if it works as intended, but so far it has been working properly.
// Generates all permutations of a set. Thus, given an input like [1, 2, 3] it changes the null
// final_list input to be [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
static void heappermute(List<List<Integer>> waypoints, int n, List<List<List<Integer>>> final_list) {
int i;
if (n == 1) {
final_list.add(waypoints);
}
else {
for (i = 0; i < n; i++) {
heappermute(waypoints, n-1, final_list);
if (n % 2 == 1) {
swap(waypoints.get(0), waypoints.get(n-1));
}
else {
swap(waypoints.get(i), waypoints.get(n-1));
}
}
}
}
static void swap (List<Integer> x, List<Integer> y)
{
List<Integer> temp = new ArrayList<>();
temp = x;
x = y;
y = temp;
}
// Generates all subsets of a given set. Thus, given a list of waypoints, it will return a list of
// waypoint lists, each of which is a subset of the original list of waypoints.
// Ex: Input originalSet = {1, 2, 3}
// Output: = {}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}
// Code modified from http://stackoverflow.com/questions/4640034/calculating-all-of-the-subsets-of-a-set-of-numbers
public static List<List<List<Integer>>> powerSet(List<List<Integer>> originalSet) {
List<List<List<Integer>>> sets = new ArrayList<>();
if (originalSet.isEmpty()) {
sets.add(new ArrayList<List<Integer>>());
return sets;
}
List<List<Integer>> list = new ArrayList<List<Integer>>(originalSet);
List<Integer> head = list.get(0);
List<List<Integer>> rest = new ArrayList<List<Integer>>(list.subList(1, list.size()));
for (List<List<Integer>> set : powerSet(rest)) {
List<List<Integer>> newSet = new ArrayList<List<Integer>>();
newSet.add(head);
newSet.addAll(set);
sets.add(newSet);
sets.add(set);
}
return sets;
}

Resources