find all combinations that satisfies the constraint? - algorithm

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);

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 number of possible ways to pick numbers greater than or equal to a given number from multiple lists?

I am trying to pick one number from multiple arraylists and find all possible ways to pick the numbers such that the sum of those numbers is greater than a given number. I can only think of brute force implementation.
For example, I have five arraylists such as
A = [2, 6, 7]
B = [6, 9]
C = [4]
D = [4, 7]
E = [8, 10, 15]
and a given number is 40.
Then after picking one number from each list, all possible ways could be
[7, 9, 4, 7, 15]
[6, 9, 4, 7, 15]
So, these are the two possible ways to pick numbers greater than or equal to 40. In case the given number is small then there could be many solutions. So how can I count them without brute force? Even with brute force how do I devise the solution in Java.
Below is my implementation. It works fine for small numbers but if the numbers are large then it gives me runtime error since the program runs for too long.
public static void numberOfWays(List<Integer> A, List<Integer> B, List<Integer> C, List<Integer> D,
List<Integer> E, int k){
long ways = 0;
for(Integer a:A){
for(Integer b:B){
for(Integer c:C){
for(Integer d:D){
for(Integer e:E){
int sum = a+b+c+d+e;
//System.out.println(a+" "+b+" "+c+" "+d+" "+e+" "+sum);
if(sum > k)
ways++;
}
}
}
}
}
System.out.println(ways);
}
The list can contain up to 1000 elements and the elements can range from 1 to 1000. The threshold value k can range from 1 to 10^9.
I am not a java programmer.But I think its a logical problem.So,I have solved it for you in python.I am pretty sure you can convert it into java.
Here is the code:
x = input('Enter the number:')
a = [2, 6, 7]
b = [6, 9]
c = [4]
d = [4, 7]
e = [8, 10, 15]
i = 0
z = 0
final_list = []
while i <= int(x):
try:
i += a[z]
final_list.append(a[z])
except BaseException:
pass
try:
i += b[z]
final_list.append(b[z])
except BaseException:
pass
try:
i += c[z]
final_list.append(c[z])
except BaseException:
pass
try:
i += d[z]
final_list.append(d[z])
except BaseException:
pass
try:
i += e[z]
final_list.append(e[z])
except BaseException:
pass
z += 1
print(final_list)
One way is this. There has to be at least one solution where you pick one number from each array and add them up to be greater than or equal to another.
Considering the fact that arrays might have random numbers in any order, first use this sort function to have them in decreasing order (largest number first, smallest number last) :
Arrays.sort(<array name>, Collections.reverseOrder());
Then pick the 1st element in the array :
v = A[0]
w = B[0]
x = C[0]
y = D[0]
z = E[0]
Then you can print them like this : v,w,x,y,z
Now your output will be :
7,9,4,7,15
Since it took the largest number of each array, it has to be equal to or greater than the given number, unless the number is greater than all of these combined in which case it is impossible.
Edit : I think I got the question wrong. If you want to know how many of the possible solutions there are, that is much easier.
First create a variable to store the possibilities
var total = 0
Use the rand function to get a random number. In your array say something like :
v=A[Math.random(0,A[].length)]
Do the same thing for all arrays, then add them up
var sum = v+w+x+y+z
Now you have an if statement to see if the sum is greater than or equal to the number given (lets say the value is stored in the variable "given")
if(sum >= given){
total+=1
}else{
<repeat the random function to restart the process and generate a new sum>
}
Finally, you need to repeat this multiple times as incase there are multiple solutions, the code will only find one and give you a false total.
To solve this, create a for loop and put all of this code inside it :
//create a variable outside to store the total number of elements in all the arrays
var elements = A[].length + B[].length + C[].length + D[].length + E[].length
for(var i = 0; i <= elements; i++){
<The code is inside here, except for "total" as otherwise the value will keep resetting>
}
The end result should look something like this :
var total = 0
var elements = A[].length + B[].length + C[].length + D[].length + E[].length
for(var i = 0; i <= elements; i++){
v=A[Math.random(0,A[].length)]
w=B[Math.random(0,B[].length)]
x=C[Math.random(0,C[].length)]
y=D[Math.random(0,D[].length)]
z=E[Math.random(0,E[].length)]
var sum = v+w+x+y+z
if(sum >= given){
total+=1
}else{
v=A[Math.random(0,A[].length)]
w=B[Math.random(0,B[].length)]
x=C[Math.random(0,C[].length)]
y=D[Math.random(0,D[].length)]
z=E[Math.random(0,E[].length)]
}
}
At the end just print the total once the entire cycle is over or just do
console.log(total)
This is just for reference and the code might not work, it probably has a bunch of bugs in it, this was just my 1st draft attempt at it. I have to test it out on my own but i hope you see where I'm coming from. Just look at the process, make your own amendments and this should work fine.
I have not deleted the first part of my answer even though it isn't the answer to this question just so that if you're having trouble in that part as well, where you select the highest possible number, it might help you
Good luck!

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

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} &lrarr; {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

Maximum sum increasing subsequence, changing algorithm to use memoization

I have the following code which implements a recursive solution for this problem, instead of using the reference variable 'x' to store overall max, How can I or can I return the result from recursion so I don't have to use the 'x' which would help memoization?
// Test Cases:
// Input: {1, 101, 2, 3, 100, 4, 5} Output: 106
// Input: {3, 4, 5, 10} Output: 22
int sum(vector<int> seq)
{
int x = INT32_MIN;
helper(seq, seq.size(), x);
return x;
}
int helper(vector<int>& seq, int n, int& x)
{
if (n == 1) return seq[0];
int maxTillNow = seq[0];
int res = INT32_MIN;
for (int i = 1; i < n; ++i)
{
res = helper(seq, i, x);
if (seq[i - 1] < seq[n - 1] && res + seq[n - 1] > maxTillNow) maxTillNow = res + seq[n - 1];
}
x = max(x, maxTillNow);
return maxTillNow;
}
First, I don't think this implementation is correct. For this input {5, 1, 2, 3, 4} it gives 14 while the correct result is 10.
For writing a recursive solution for this problem, you don't need to pass x as a parameter, as x is the result you expect to get from the function itself. Instead, you can construct a state as the following:
Current index: this is the index you're processing at the current step.
Last taken number: This is the value of the last number you included in your result subsequence so far. This is to make sure that you pick larger numbers in the following steps to keep the result subsequence increasing.
So your function definition is something like sum(current_index, last_taken_number) = the maximum increasing sum from current_index until the end, given that you have to pick elements greater than last_taken_number to keep it an increasing subsequence, where the answer that you desire is sum(0, a small value) since it calculates the result for the whole sequence. by a small value I mean smaller than any other value in the whole sequence.
sum(current_index, last_taken_number) could be calculated recursively using smaller substates. First assume the simple cases:
N = 0, result is 0 since you don't have a sequence at all.
N = 1, the sequence contains only one number, the result is either that number or 0 in case the number is negative (I'm considering an empty subsequence as a valid subsequence, so not taking any number is a valid answer).
Now to the tricky part, when N >= 2.
Assume that N = 2. In this case you have two options:
Either ignore the first number, then the problem can be reduced to the N=1 version where that number is the last one in the sequence. In this case the result is the same as sum(1,MIN_VAL), where current_index=1 since we already processed index=0 and decided to ignore it, and MIN_VAL is the small value we mentioned above
Take the first number. Assume the its value is X. Then the result is X + sum(1, X). That means the solution includes X since you decided to include it in the sequence, plus whatever the result is from sum(1,X). Note that we're calling sum with MIN_VAL=X since we decided to take X, so the following values that we pick have to be greater than X.
Both decisions are valid. The result is whatever the maximum of these two. So we can deduce the general recurrence as the following:
sum(current_index, MIN_VAL) = max(
sum(current_index + 1, MIN_VAL) // ignore,
seq[current_index] + sum(current_index + 1, seq[current_index]) // take
).
The second decision is not always valid, so you have to make sure that the current element > MIN_VAL in order to be valid to take it.
This is a pseudo code for the idea:
sum(current_index, MIN_VAL){
if(current_index == END_OF_SEQUENCE) return 0
if( state[current_index,MIN_VAL] was calculated before ) return the perviously calculated result
decision_1 = sum(current_index + 1, MIN_VAL) // ignore case
if(sequence[current_index] > MIN_VAL) // decision_2 is valid
decision_2 = sequence[current_index] + sum(current_index + 1, sequence[current_index]) // take case
else
decision_2 = INT_MIN
result = max(decision_1, decision_2)
memorize result for the state[current_index, MIN_VAL]
return result
}

How to find longest increasing sequence starting at each position within the array in O(n log n) time,

How could we find longest increasing sub-sequence starting at each position of the array in O(n log n) time, I have seen techniques to find longest increasing sequence ending at each position of the array but I am unable to find the other way round.
e.g.
for the sequence " 3 2 4 4 3 2 3 "
output must be " 2 2 1 1 1 2 1 "
I made a quick and dirty JavaScript implementation (note: it is O(n^2)):
function lis(a) {
var tmpArr = Array(),
result = Array(),
i = a.length;
while (i--) {
var theValue = a[i],
longestFound = tmpArr[theValue] || 1;
for (var j=theValue+1; j<tmpArr.length; j++) {
if (tmpArr[j] >= longestFound) {
longestFound = tmpArr[j]+1;
}
}
result[i] = tmpArr[theValue] = longestFound;
}
return result;
}
jsFiddle: http://jsfiddle.net/Bwj9s/1/
We run through the array right-to-left, keeping previous calculations in a separate temporary array for subsequent lookups.
The tmpArray contains the previously found subsequences beginning with any given value, so tmpArray[n] will represent the longest subsequence found (to the right of the current position) beginning with the value n.
The loop goes like this: For every index, we look up the value (and all higher values) in our tmpArray to see if we already found a subsequence which the value could be prepended to. If we find one, we simply add 1 to that length, update the tmpArray for the value, and move to the next index. If we don't find a working (higher) subsequence, we set the tmpArray for the value to 1 and move on.
In order to make it O(n log n) we observe that the tmpArray will always be a decreasing array -- it can and should use a binary search rather than a partial loop.
EDIT: I didn't read the post completely, sorry. I thought you needed the longest increasing sub-sequence for all sequence. Re-edited the code to make it work.
I think it is possible to do it in linear time, actually. Consider this code:
int a[10] = {4, 2, 6, 10, 5, 3, 7, 5, 4, 10};
int maxLength[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // array of zeros
int n = 10; // size of the array;
int b = 0;
while (b != n) {
int e = b;
while (++e < n && a[b] < a[e]) {} //while the sequence is increasing, ++e
while (b != e) { maxLength[b++] = e-b-1; }
}

Resources