Knapsack with max element count in subsets - algorithm

i have an integer array, and i need to find a subset of this array with max 3 elements which is equal to W.
Can i solve this problem using knapsack? or i need to calculate every 1-2-3 element combination of the array?
Thanks already

To look for 3 elements that sum to W, this is exactly the 3SUM problem.
It can be solved in O(n2) time by either:
Inserting each number into a hash table, then, for each combination of two numbers a and b, checking whether W-a-b exists in the hash table.
Sorting the array, then, for each element a, looking right for two elements that sums to W-a using 2 iterators from either side.
If the integer range is in the range [-u, u], you can solve your problem using a Fast Fourier transform in O(n + u log u). See the above link for more details on either this or one of the above approaches.
Since your problem is dependent on solving 3SUM, which is a well-known problem, you're very unlikely to find a solution with better running time than the above well-known solutions for 3SUM.
To look for 1 element:
You can do a simple linear search (O(n)).
To look for 2 elements:
This can be solved by simply checking each combination of 2 elements in O(n2) (you needn't do something more complex as the asymptotic running time of 3 elements will result in O(n2) total time regardless of how efficient this is).
It can also be solved in O(n) or O(n log n) using methods identical to those described above for 3SUM.
Overall running time:
O(n2) or O(n + u log u), depending on which method was used to solve the 3SUM part.

Related

A linear algorithm for this specification?

This is my question I have got somewhere.
Given a list of numbers in random order write a linear time algorithm to find the ๐‘˜th smallest number in the list. Explain why your algorithm is linear.
I have searched almost half the web and what I got to know is a linear-time algorithm is whose time complexity must be O(n). (I may be wrong somewhere)
We can solve the above question by different algorithms eg.
Sort the array and select k-1 element [O(n log n)]
Using min-heap [O(n + klog n)]
etc.
Now the problem is I couldn't find any algorithm which has O(n) time complexity and satisfies that algorithm is linear.
What can be the solution for this problem?
This is std::nth_element
From cppreference:
Notes
The algorithm used is typically introselect although other selection algorithms with suitable average-case complexity are allowed.
Given a list of numbers
although it is not compatible with std::list, only std::vector, std::deque and std::array, as it requires RandomAccessIterator.
linear search remembering k smallest values is O(n*k) but if k is considered constant then its O(n) time.
However if k is not considered as constant then Using histogram leads to O(n+m.log(m)) time and O(m) space complexity where m is number of possible distinct values/range in your input data. The algo is like this:
create histogram counters for each possible value and set it to zero O(m)
process all data and count the values O(m)
sort the histogram O(m.log(m))
pick k-th element from histogram O(1)
in case we are talking about unsigned integers from 0 to m-1 then histogram is computed like this:
int data[n]={your data},cnt[m],i;
for (i=0;i<m;i++) cnt[i]=0;
for (i=0;i<n;i++) cnt[data[i]]++;
However if your input data values does not comply above condition you need to change the range by interpolation or hashing. However if m is huge (or contains huge gaps) is this a no go as such histogram is either using buckets (which is not usable for your problem) or need list of values which lead to no longer linear complexity.
So when put all this together is your problem solvable with linear complexity when:
n >= m.log(m)

Finding the m Largest Numbers

This is a problem from the Cormen text, but I'd like to see if there are any other solutions.
Given an array with n distinct numbers, you need to find the m largest ones in the array, and have
them in sorted order. Assume n and m are large, but grow differently. In particular, you need
to consider below the situations where m = t*n, where t is a small number, say 0.1, and then the
possibility m = โˆšn.
The solution given in the book offers 3 options:
Sort the array and return the top m-long segment
Convert the array to a max-heap and extract the m elements
Select the m-th largest number, partition the array about it, and sort the segment of larger entries.
These all make sense, and they all have their pros and cons, but I'm wondering, is there another way to do it? It doesn't have to be better or faster, I'm just curious to see if this is a common problem with more solutions, or if we are limited to those 3 choices.
The time complexities of the three approaches you have mentioned are as follows.
O(n log n)
O(n + m log n)
O(n + m log m)
So option (3) is definitely better than the others in terms of asymptotic complexity, since m <= n. When m is small, the difference between (2) and (3) is so small it would have little practical impact.
As for other ways to solve the problem, there are infinitely many ways you could, so the question is somewhat poor in this regard. Another approach I can think of as being practically simple and performant is the following.
Extract the first m numbers from your list of n into an array, and sort it.
Repeatedly grab the next number from your list and insert it into the correct location in the array, shifting all the lesser numbers over by one and pushing one out.
I would only do this if m was very small though. Option (2) from your original list is also extremely easy to implement if you have a max-heap implementation and will work great.
A different approach.
Take the first m numbers, and turn them into a min heap. Run through the array, if its value exceeds the min of the top m then you extract the min value and insert the new one. When you reach the end of the array you can then extract the elements into an array and reverse it.
The worst case performance of this version is O(n log(m)) placing it between the first and second methods for efficiency.
The average case is more interesting. On average only O(m log(n/m)) of the elements are going to pass the first comparison test, each time incurring O(log(m)) work so you get O(n + m log(n/m) log(m)) work, which puts it between the second and third methods. But if n is many orders of magnitude greater than m then the O(n) piece dominates, and the O(n) median select in the third approach has worse constants than the one comparison per element in this approach, so in this case this is actually the fastest!

Finding a k element subset in a set of real numbers (Programming Pearls book)

I am solving problems from Column2 of Programming Pearls. I came across this problem:
"Given a set of n real numbers, a real number t, and an integer k, how quickly can you determine whether there exists a k-element subset of the set that sums to at most t?"
My solution is to sort the set of real numbers and then look at the sum for the first k elements. If this sum is less than or equal to t, then we know there exists at least one
set that satisfies the condition.
Is the solution correct?
Is there a better or different solution?
Note: Just to make it clear, do not assume the input to be already sorted.
Because you need only first k elements sorted as per your problem , I suggest following:-
Select the kth element in array using randomised select O(N)
Take sum of first k elements in array and check if its less than t
Time complexity O(N + k) = O(N) as k is O(N)
Randomized Selection
Note:- when k is very small as compared to N then max heap can be very efficient as the storage does not cost that much and it can solve problem in worst case O(Nlogk).

6SUM: Given a set S of n integers, is there a subset of S with exactly 6 elements that sum to 0? How to do better than O(n^3)?

I've thought of this simple algorithm to solve the 6SUM problem which uses O(n^3) time and space:
Generate all sets of triples and put them in a hash table where the key is the sum of the triples. Then iterate through the keys of the hash table: for each key k1, see if another key k2 exists such that k2 = S-k1
What's a more efficient algorithm? This is not a homework problem.
Your algorithm is Omega(n^6) in the worst case, it is only O(n^3) in the average case. You are ignoring the possibility of hash table collisions. You can make it O(n^3 logn) by using a balanced tree instead, though.
Also, this is in P, as there is a trivial polynomial time algorithm to check every possible combination of 6 numbers, so mention of knapsack etc is irrelevant.
Like the 3-SUM problem, I believe the r-sum problem having an algorithm which is o(n^[r/2]), (note: smallOH and [x] = greatest integer >= x, e.g. [5/2] = 3) is still open.
There is a brief mention of this in the 3-SUM page here, where there is a claim that the above bounds have been proven in restricted models of computation.
So getting better than O(n^3) (i.e. o(n^3)) might be an open problem.

Is it possible to find two numbers whose difference is minimum in O(n) time

Given an unsorted integer array, and without making any assumptions on
the numbers in the array:
Is it possible to find two numbers whose
difference is minimum in O(n) time?
Edit: Difference between two numbers a, b is defined as abs(a-b)
Find smallest and largest element in the list. The difference smallest-largest will be minimum.
If you're looking for nonnegative difference, then this is of course at least as hard as checking if the array has two same elements. This is called element uniqueness problem and without any additional assumptions (like limiting size of integers, allowing other operations than comparison) requires >= n log n time. It is the 1-dimensional case of finding the closest pair of points.
I don't think you can to it in O(n). The best I can come up with off the top of my head is to sort them (which is O(n * log n)) and find the minimum difference of adjacent pairs in the sorted list (which adds another O(n)).
I think it is possible. The secret is that you don't actually have to sort the list, you just need to create a tally of which numbers exist. This may count as "making an assumption" from an algorithmic perspective, but not from a practical perspective. We know the ints are bounded by a min and a max.
So, create an array of 2 bit elements, 1 pair for each int from INT_MIN to INT_MAX inclusive, set all of them to 00.
Iterate through the entire list of numbers. For each number in the list, if the corresponding 2 bits are 00 set them to 01. If they're 01 set them to 10. Otherwise ignore. This is obviously O(n).
Next, if any of the 2 bits is set to 10, that is your answer. The minimum distance is 0 because the list contains a repeated number. If not, scan through the list and find the minimum distance. Many people have already pointed out there are simple O(n) algorithms for this.
So O(n) + O(n) = O(n).
Edit: responding to comments.
Interesting points. I think you could achieve the same results without making any assumptions by finding the min/max of the list first and using a sparse array ranging from min to max to hold the data. Takes care of the INT_MIN/MAX assumption, the space complexity and the O(m) time complexity of scanning the array.
The best I can think of is to counting sort the array (possibly combining equal values) and then do the sorted comparisons -- bin sort is O(n + M) (M being the number of distinct values). This has a heavy memory requirement, however. Some form of bucket or radix sort would be intermediate in time and more efficient in space.
Sort the list with radixsort (which is O(n) for integers), then iterate and keep track of the smallest distance so far.
(I assume your integer is a fixed-bit type. If they can hold arbitrarily large mathematical integers, radixsort will be O(n log n) as well.)
It seems to be possible to sort unbounded set of integers in O(n*sqrt(log(log(n))) time. After sorting it is of course trivial to find the minimal difference in linear time.
But I can't think of any algorithm to make it faster than this.
No, not without making assumptions about the numbers/ordering.
It would be possible given a sorted list though.
I think the answer is no and the proof is similar to the proof that you can not sort faster than n lg n: you have to compare all of the elements, i.e create a comparison tree, which implies omega(n lg n) algorithm.
EDIT. OK, if you really want to argue, then the question does not say whether it should be a Turing machine or not. With quantum computers, you can do it in linear time :)

Resources