Algorithm for all combinations to divide set into equally sized subsets [duplicate] - algorithm

Let's say I have a set of elements S = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
I would like to create combinations of 3 and group them in a way such that no number appears in more than one combination.
Here is an example:
{ {3, 7, 9}, {1, 2, 4}, {5, 6, 8} }
The order of the numbers in the groups does not matter, nor does the order of the groups in the entire example.
In short, I want every possible group combination from every possible combination in the original set, excluding the ones that have a number appearing in multiple groups.
My question: is this actually feasible in terms of run time and memory? My sample sizes could be somewhere around 30-50 numbers.
If so, what is the best way to create this algorithm? Would it be best to create all possible combinations, and choose the groups only if the number hasn't already appeared?
I'm writing this in Qt 5.6, which is a C++ based framework.

You can do this recursively, and avoid duplicates, if you keep the first element fixed in each recursion, and only make groups of 3 with the values in order, eg:
{1,2,3,4,5,6,7,8,9}
Put the lowest element in the first spot (a), and keep it there:
{a,b,c} = {1, *, *}
For the second spot (b), iterate over every value from the second-lowest to the second-highest:
{a,b,c} = {1, 2~8, *}
For the third spot (c), iterate over every value higher than the second value:
{1, 2~8, b+1~9}
Then recurse with the rest of the values.
{1,2,3} {4,5,6} {7,8,9}
{1,2,3} {4,5,7} {6,8,9}
{1,2,3} {4,5,8} {6,7,9}
{1,2,3} {4,5,9} {6,7,8}
{1,2,3} {4,6,7} {5,8,9}
{1,2,3} {4,6,8} {5,7,9}
{1,2,3} {4,6,9} {5,7,8}
{1,2,3} {4,7,8} {5,6,9}
{1,2,3} {4,7,9} {5,6,8}
{1,2,3} {4,8,9} {5,6,7}
{1,2,4} {3,5,6} {7,8,9}
...
{1,8,9} {2,6,7} {3,4,5}
Wen I say "in order", that doesn't have to be any specific order (numerical, alphabetical...), it can just be the original order of the input. You can avoid having to re-sort the input of each recursion if you make sure to pass the rest of the values on to the next recursion in the order you received them.
A run-through of the recursion:
Let's say you get the input {1,2,3,4,5,6,7,8,9}. As the first element in the group, you take the first element from the input, and for the other two elements, you iterate over the other values:
{1,2,3}
{1,2,4}
{1,2,5}
{1,2,6}
{1,2,7}
{1,2,8}
{1,2,9}
{1,3,4}
{1,3,5}
{1,3,6}
...
{1,8,9}
making sure the third element always comes after the second element, to avoid duplicates like:
{1,3,5} ⇆ {1,5,3}
Now, let's say that at a certain point, you've selected this as the first group:
{1,3,7}
You then pass the rest of the values onto the next recursion:
{2,4,5,6,8,9}
In this recursion, you apply the same rules as for the first group: take the first element as the first element in the group and keep it there, and iterate over the other values for the second and third element:
{2,4,5}
{2,4,6}
{2,4,8}
{2,4,9}
{2,5,6}
{2,5,8}
{2,5,9}
{2,6,7}
...
{2,8,9}
Now, let's say that at a certain point, you've selected this as the second group:
{2,5,6}
You then pass the rest of the values onto the next recursion:
{4,8,9}
And since this is the last group, there is only one possibility, and so this particular recursion would end in the combination:
{1,3,7} {2,5,6} {4,8,9}
As you see, you don't have to sort the values at any point, as long as you pass them onto the next recursion in the order you recevied them. So if you receive e.g.:
{q,w,e,r,t,y,u,i,o}
and you select from this the group:
{q,r,u}
then you should pass on:
{w,e,t,y,i,o}
Here's a JavaScript snippet which demonstrates the method; it returns a 3D array with combinations of groups of elements.
(The filter function creates a copy of the input array, with elements 0, i and j removed.)
function clone2D(array) {
var clone = [];
for (var i = 0; i < array.length; i++) clone.push(array[i].slice());
return clone;
}
function groupThree(input) {
var result = [], combination = [];
group(input, 0);
return result;
function group(input, step) {
combination[step] = [input[0]];
for (var i = 1; i < input.length - 1; i++) {
combination[step][1] = input[i];
for (var j = i + 1; j < input.length; j++) {
combination[step][2] = input[j];
if (input.length > 3) {
var rest = input.filter(function(elem, index) {
return index && index != i && index != j;
});
group(rest, step + 1);
}
else result.push(clone2D(combination));
}
}
}
}
var result = groupThree([1,2,3,4,5,6,7,8,9]);
for (var r in result) document.write(JSON.stringify(result[r]) + "<br>");

For n things taken 3 at a time, you could use 3 nested loops:
for(k = 0; k < n-2; k++){
for(j = k+1; j < n-1; j++){
for(i = j+1; i < n ; i++){
... S[k] ... S[j] ... S[i]
}
}
}
For a generic solution of n things taken k at a time, you could use an array of k counters.

I think You can solve it by using coin change problem with dynamic programming, just assume You are looking for change of 3 and every index in array is a coin value 1, then just output coins(values in Your array) that has been found.
Link: https://www.youtube.com/watch?v=18NVyOI_690

Related

How to get original array from random shuffle of an array

I was asked in an interview today below question. I gave O(nlgn) solution but I was asked to give O(n) solution. I could not come up with O(n) solution. Can you help?
An input array is given like [1,2,4] then every element of it is doubled and
appended into the array. So the array now looks like [1,2,4,2,4,8]. How
this array is randomly shuffled. One possible random arrangement is
[4,8,2,1,2,4]. Now we are given this random shuffled array and we want to
get original array [1,2,4] in O(n) time.
The original array can be returned in any order. How can I do it?
Here's an O(N) Java solution that could be improved by first making sure that the array is of the proper form. For example it shouldn't accept [0] as an input:
import java.util.*;
class Solution {
public static int[] findOriginalArray(int[] changed) {
if (changed.length % 2 != 0)
return new int[] {};
// set Map size to optimal value to avoid rehashes
Map<Integer,Integer> count = new HashMap<>(changed.length*100/75);
int[] original = new int[changed.length/2];
int pos = 0;
// count frequency for each number
for (int n : changed) {
count.put(n, count.getOrDefault(n,0)+1);
}
// now decide which go into the answer
for (int n : changed) {
int smallest = n;
for (int m=n; m > 0 && count.getOrDefault(m,0) > 0; m = m/2) {
//System.out.println(m);
smallest = m;
if (m % 2 != 0) break;
}
// trickle up from smallest to largest while count > 0
for (int m=smallest, mm = 2*m; count.getOrDefault(mm,0) > 0; m = mm, mm=2*mm){
int ct = count.getOrDefault(mm,0);
while (count.get(m) > 0 && ct > 0) {
//System.out.println("adding "+m);
original[pos++] = m;
count.put(mm, ct -1);
count.put(m, count.get(m) - 1);
ct = count.getOrDefault(mm,0);
}
}
}
// check for incorrect format
if (count.values().stream().anyMatch(x -> x > 0)) {
return new int[] {};
}
return original;
}
public static void main(String[] args) {
int[] changed = {1,2,4,2,4,8};
System.out.println(Arrays.toString(changed));
System.out.println(Arrays.toString(findOriginalArray(changed)));
}
}
But I've tried to keep it simple.
The output is NOT guaranteed to be sorted. If you want it sorted it's going to cost O(NlogN) inevitably unless you use a Radix sort or something similar (which would make it O(NlogE) where E is the max value of the numbers you're sorting and logE the number of bits needed).
Runtime
This may not look that it is O(N) but you can see that it is because for every loop it will only find the lowest number in the chain ONCE, then trickle up the chain ONCE. Or said another way, in every iteration it will do O(X) iterations to process X elements. What will remain is O(N-X) elements. Therefore, even though there are for's inside for's it is still O(N).
An example execution can be seen with [64,32,16,8,4,2].
If this where not O(N) if you print out each value that it traverses to find the smallest you'd expect to see the values appear over and over again (for example N*(N+1)/2 times).
But instead you see them only once:
finding smallest 64
finding smallest 32
finding smallest 16
finding smallest 8
finding smallest 4
finding smallest 2
adding 2
adding 8
adding 32
If you're familiar with the Heapify algorithm you'll recognize the approach here.
def findOriginalArray(self, changed: List[int]) -> List[int]:
size = len(changed)
ans = []
left_elements = size//2
#IF SIZE IS ODD THEN RETURN [] NO SOLN. IS POSSIBLE
if(size%2 !=0):
return ans
#FREQUENCY DICTIONARY given array [0,0,2,1] my map will be: {0:2,2:1,1:1}
d = {}
for i in changed:
if(i in d):
d[i]+=1
else:
d[i] = 1
# CHECK THE EDGE CASE OF 0
if(0 in d):
count = d[0]
half = count//2
if((count % 2 != 0) or (half > left_elements)):
return ans
left_elements -= half
ans = [0 for i in range(half)]
#CHECK REST OF THE CASES : considering the values will be 10^5
for i in range(1,50001):
if(i in d):
if(d[i] > 0):
count = d[i]
if(count > left_elements):
ans = []
break
left_elements -= d[i]
for j in range(count):
ans.append(i)
if(2*i in d):
if(d[2*i] < count):
ans = []
break
else:
d[2*i] -= count
else:
ans = []
break
return ans
I have a simple idea which might not be the best, but I could not think of a case where it would not work. Having the array A with the doubled elements and randomly shuffled, keep a helper map. Process each element of the array and, each time you find a new element, add it to the map with the value 0. When an element is processed, increment map[i] and decrement map[2*i]. Next you iterate over the map and print the elements that have a value greater than zero.
A simple example, say that the vector is:
[1, 2, 3]
And the doubled/shuffled version is:
A = [3, 2, 1, 4, 2, 6]
When processing 3, first add the keys 3 and 6 to the map with value zero. Increment map[3] and decrement map[6]. This way, map[3] = 1 and map[6] = -1. Then for the next element map[2] = 1 and map[4] = -1 and so forth. The final state of the map in this example would be map[1] = 1, map[2] = 1, map[3] = 1, map[4] = -1, map[6] = 0, map[8] = -1, map[12] = -1.
Then you just process the keys of the map and, for each key with a value greater than zero, add it to the output. There are certainly more efficient solutions, but this one is O(n).
In C++, you can try this.
With time is O(N + KlogK) where N is the length of input, and K is the number of unique elements in input.
class Solution {
public:
vector<int> findOriginalArray(vector<int>& input) {
if (input.size() % 2) return {};
unordered_map<int, int> m;
for (int n : input) m[n]++;
vector<int> nums;
for (auto [n, cnt] : m) nums.push_back(n);
sort(begin(nums), end(nums));
vector<int> out;
for (int n : nums) {
if (m[2 * n] < m[n]) return {};
for (int i = 0; i < m[n]; ++i, --m[2 * n]) out.push_back(n);
}
return out;
}
};
Not so clear about the space complexity required in the question, so this is my top-of-the-mind attempt to this question if this requires O(n) time complexity.
If the length of the input array is not even, then its wrong !!
Create a map, add the elements of the input array to it.
Divide each element in the input array by 2 and check if that value exists in the map. If it exists, add it to the array (slice) orig.
There is a chance we have added duplicate values to this original array, clean it!!
Here is a sample go code:
https://go.dev/play/p/w4mm-rloHyi
I am sure we can optimize this code in a lot of ways for space complexities. But its O(n) time complexity.

how to find max sum of adjacent elements in 2d array from a starting point

there is 4x4 2d array such as, (range of each element between 0 and 9)
4512
3712
1345
3312
i'm trying to find max of 4 adjacent elements from a point.
( not including diagonal )
for example, if picking a point (1,2) for starting,
can move (1,1) or (2,2) or (1,3) adjacent element from (1,2).
if you choose (2,2) for next, you can move (2,1) or (3,2) or (2,3).
and so on until pick 4 elements.
if you pick 4 elements like,
(1,2)->(2,2)->(2,1)->(1,1)
sum of this is 3 + 7 + 5 + 4 = 19
i'm trying to make possible candidates using dfs or bfs.
but, it can't make the above for the candidate, (1,1) -> (1,2) -> (2,1) -> (2,2)
is there any solution for this problem?
One possible way is to create predefined constants of 5 tetrominoes (sorry, can't post images) with all rotations and reflections (of course you don't need to rotate 'square' or reflect symmetric ones). Then you can take each of these constants and map your starting point to each point of chosen constant.
Another approach is to enumerate tetrominoes algorithmically. Some algorithms are described in wikipedia.
Not complete answer yet
Using dynamic programming.
In sum array(s), value at position (i,j) contains maximum sum possible using (i,j) element and element in direction right or down only.
Missing chain: Will not consider chain of elements like (0,0), (0,1), (1,1) and (1,0)
var array = [
[4, 5, 1, 2],
[3, 7, 1, 2],
[1, 3, 4, 5],
[3, 3, 1, 2],
]
var sum1 = array;
var sum2 = getNextOrderSum(sum1, array);
var sum3 = getNextOrderSum(sum2, array);
var sum4 = getNextOrderSum(sum3, array);
print(sum4);
// Given max sum array of n adjacent elements and original array
// Return max sum array of n+1 adjacent elements
function getNextOrderSum(input, array) {
var sum = [];
for (var i = 0; i < input.length; i++) {
sum[i] = [];
for (var j = 0; j < input[0].length; j++) {
sum[i][j] = array[i][j] + Math.max(get(input, i, j + 1), get(input, i + 1, j));
}
}
return sum;
}
// Utility method to get i,j element of array with boundary checks
function get(array, i, j) {
if (i < 0 || j < 0)
return 0;
if (i >= array.length)
return 0;
if (j >= array[0].length)
return 0;
return array[i][j];
}
// Utility method for printing
function print(array) {
var s = "";
for (var i = 0; i < array.length; i++) {
s += array[i].toString() + "\n"
}
console.log(s);
}
Use a 3D array dp[n][m][4]
where n and m are dimensions of array
and the 4 is used to tell the position of element in chain.
Base case-
We store value of elment (i,j) in dp[i][j][k] if k=3 or when element is last one in chain or if i and j are out of bounds of array (trivial case).
DP formula-
Let function used for doing task be dpFUNCTION()
dp[i][j][k]=array[i][j] + mAX( dpFUNCTION(i+1,j,k+1), dpFUNCTION(i,j+1,k+1), dpFUNCTION(i-1,j,k+1), dpFUNCTION(i,j-1,k+1));
EDIT-
Lets start with simple case and extend it to your problem. Actually you can do most DP questions this way ie by breaking into simple form and extend it.
1)Now, if we only have to find max of adjacent numbers of a number in array we can simply fill dp1[i][j] with-
dp1[i][j]=max(array[i-1][j], array[i+1][j], array[i][j+1], array[i][j-1]);
//max of adjacent numbers
2)Now, if we have to find max of 2 adjacent numbers we can make use of our dp1[][] array as follows-
dp2[i][j]= max(array[i-1][j]+dp1[i-1][j], array[i-1][j]+dp1[i-1][j], array[i-1][j]+dp1[i-1][j], array[i-1][j]+dp1[i-1][j]);
As for chain of length 2, we need to get sum of its adjacent number(say array[i-1][j]) and max of its ajacent numbers(which was computed stored in dp[i-1][j]). Then we store max of all adjacent numbers in dp2.
3)Similarly, if length of chain is 3, we make use of dp2[][] as follows-
dp3[i][j]= max(array[i-1][j]+dp2[i-1][j], array[i-1][j]+dp2[i-1][j], array[i-1][j]+dp2[i-1][j], array[i-1][j]+dp2[i-1][j]);
4)Finally, for chain of length 4, we get-
dp4[i][j]= max(array[i-1][j]+dp3[i-1][j], array[i-1][j]+dp3[i-1][j], array[i-1][j]+dp3[i-1][j], array[i-1][j]+dp3[i-1][j]);
Which is the required solution and what I did was club all these 4 arrays into dp[n][m][4] and filled it a bit chaotic way instead of doing it in steps as explained. You could use same approach even when diagonals are included.

find all combinations that satisfies the constraint?

My problem can be simplified as follows.
There're s bins, and within each bin there're k numbers.
A combination consists of one number from each bin, so in total there're k^s possible combinations.
The score of a combination is the sum of s numbers it contains.
How can I find all the combinations with score less than some value r?
Right now what I'm doing is,
1) sort the numbers in each bin.
2) start with a priority queue that only contains the combination of the smallest number from each bin.
3) pop a combination from the queue, add s children of that combination to to queue. (a child of a combination is made of replacing one number of the combination to the next larger number in the same bin, so there're s children of a combination.)
4) repeat 3) till the combination popped is larger than r.
Suppose we find n combinations smaller than r, the time complexity of this algorithm is then O(nlog(s-1)n + sklogk).
Of course this algorithm is not optimal. For example instead of starting with the smallest combination, we can start with a known lower bound. And I sort of have a feeling that dynamic programming can be applied here too, but I didn't figure out how to do it.
Any suggestions are welcome, thanks.
After having sorted the bins, you could use a recursive algorithm, that extends a partial selection with an element from the next bin until the selection is complete (or overruns the sum limit). When complete, it is added to the result. Through backtracking all the valid selections get added to the result.
Here is some pseudo code. The last argument is both input and output:
function combinations(int[][] bins, int r, int[] selection, int[][] result):
if r < 0 then:
return
if selection.length >= bins.length then:
result.add(selection)
return
bin = bins[selection.length]
for (i = 0; i < bin.length; i++):
# Concatenate the i-th value from the bin to a new selection array
combinations(bins, r - bin[i], selection + bin[i], result)
Here is an implementation in JavaScript:
function sortBins(bins) {
for (bin of bins) {
bin.sort(function (a,b) { return a-b; });
}
}
function combinations(bins, r, selection, result) {
if (r < 0) return result; // nothing added to result
if (selection.length >= bins.length) return result.concat([selection]);
var bin = bins[selection.length];
for (var i = 0; i < bin.length; i++)
result = combinations(bins, r - bin[i], selection.concat([bin[i]]), result);
return result;
}
// Test data:
var r = 13;
var bins = [
[5, 2, 3],
[9, 4, 1],
[6, 5, 7]
];
// Get solution:
sortBins(bins);
var result = combinations(bins, r, [], []);
// Output results:
console.log(result);

How to count unique items in a list?

How would someone go on counting the number of unique items in a list?
For example say I have {1, 3, 3, 4, 1, 3} and I want to get the number 3 which represent the number of unique items in the list(namely |A|=3 if A={1, 3, 4}). What algorithm would someone use for this?
I have tryied a double loop:
for firstItem to lastItem
currentItem=a
for currentItem to lastItem
currentItem=b
if a==b then numberOfDublicates++
uniqueItems=numberOfItems-numberOfDublicates
That doesn't work as it counts the duplicates more times than actually needed. With the example in the beginning it would be:
For the first loop it would count +1 duplicates for number 1 in the list.
For the second loop it would count +2 duplicates for number 3 in the list.
For the third loop it would count +1 duplicates for number 3 again(overcounting the last '3') and
there's where the problem comes in.
Any idea on how to solve this?
Add the items to a HashSet, then check the HashSet's size after you finish.
Assuming that you have a good hash function, this is O(n).
You can check to see if there are any duplicates following the number. If not increment the uniqueCount:
uniqueCount = 0;
for (i=0;i<size;i++) {
bool isUnique = true;
for (j=i+1;j<size;j++)
if (arr[i] == arr[j] {
isUnique = false;
break;
}
}
if(isUnique) {
uniqueCount ++;
}
}
The above approach is O(N^2) in time and O(1) in space.
Another approach would be to sort the input array which will put duplicate elements next to each other and then look for adjacent array elements. This approach is O(NlgN) in time and O(1) in space.
If you are allowed to use additional space you can get this done in O(N) time and O(N) space by using a hash. The keys for the hash are the array elements and the values are their frequencies.
At the end of hashing you can get the count of only those hash keys which have value of 1.
Sort it using a decent sorting algorithm like mergesort or heapsort (both habe O(n log n) as worst-case) and loop over the sorted list:
sorted_list = sort(list)
unique_count = 0
last = sorted_list[0]
for item in sorted_list[1:]:
if not item == last:
unique_count += 1
last = item
list.sort();
for (i = 0; i < list.size() - 1; i++)
if (list.get(i)==list.get(i+1)
duplicates++;
Keep Dictionary and add count in loop
This is how it will look at c#
int[] items = {1, 3, 3, 4, 1, 3};
Dictionary<int,int> dic = new Dictionary<int,int>();
foreach(int item in items)
dic[item]++
Of course there is LINQ way in C#, but as I understand question is general ;)

How can I efficiently determine if two lists contain elements ordered in the same way?

I have two ordered lists of the same element type, each list having at most one element of each value (say ints and unique numbers), but otherwise with no restrictions (one may be a subset of the other, they may be completely disjunct, or share some elements but not others).
How do I efficiently determine if A is ordering any two items in a different way than B is? For example, if A has the items 1, 2, 10 and B the items 2, 10, 1, the property would not hold as A lists 1 before 10 but B lists it after 10. 1, 2, 10 vs 2, 10, 5 would be perfectly valid however as A never mentions 5 at all, I cannot rely on any given sorting rule shared by both lists.
You can get O(n) as follows. First, find the intersection of the two sets using hashing. Second, test whether A and B are identical if you only consider elements from the intersection.
My approach would be to first make sorted copies of A and B which also record the positions of elements in the original lists:
for i in 1 .. length(A):
Apos[i] = (A, i)
sortedApos = sort(Apos[] by first element of each pair)
for i in 1 .. length(B):
Bpos[i] = (B, i)
sortedBpos = sort(Bpos[] by first element of each pair)
Now find those elements in common using a standard list merge that records the positions in both A and B of the shared elements:
i = 1
j = 1
shared = []
while i <= length(A) && j <= length(B)
if sortedApos[i][1] < sortedBpos[j][1]
++i
else if sortedApos[i][1] > sortedBpos[j][1]
++j
else // They're equal
append(shared, (sortedApos[i][2], sortedBpos[j][2]))
++i
++j
Finally, sort shared by its first element (position in A) and check that all its second elements (positions in B) are increasing. This will be the case iff the elements common to A and B appear in the same order:
sortedShared = sort(shared[] by first element of each pair)
for i = 2 .. length(sortedShared)
if sortedShared[i][2] < sortedShared[i-1][2]
return DIFFERENT
return SAME
Time complexity: 2*(O(n) + O(nlog n)) + O(n) + O(nlog n) + O(n) = O(nlog n).
General approach: store all the values and their positions in B as keys and values in a HashMap. Iterate over the values in A and look them up in B's HashMap to get their position in B (or null). If this position is before the largest position value you've seen previously, then you know that something in B is in a different order than A. Runs in O(n) time.
Rough, totally untested code:
boolean valuesInSameOrder(int[] A, int[] B)
{
Map<Integer, Integer> bMap = new HashMap<Integer, Integer>();
for (int i = 0; i < B.length; i++)
{
bMap.put(B[i], i);
}
int maxPosInB = 0;
for (int i = 0; i < A.length; i++)
{
if(bMap.containsKey(A[i]))
{
int currPosInB = bMap.get(A[i]);
if (currPosInB < maxPosInB)
{
// B has something in a different order than A
return false;
}
else
{
maxPosInB = currPosInB;
}
}
}
// All of B's values are in the same order as A
return true;
}

Resources