Related
I'm trying to find a fast way to solve the subset sum problem with a few modifications, I know the exact size of the subset I need to get the target number and I also know the input array to be a range from 1 to 2000. My questions is if there is any way to improve upon the base subset sum problem solution to make it even faster when knowing these conditions as the normal solutions are too slow. Basically the only changing part is the wanted target sum.
I would preferably want it to return all the possible subsets of the given size that add up to the target value if its possible without slowing the program down too much. An example code in python or a similar language would be appriciated.
I've tried many of the solutions for the base subset sum problem but they are too slow to execute due to the size of the input array.
Knowing the size of the subset is an incredibly powerful information, because you don't have to iterate through subset size.
Given N your subset size, you could just :
Sum up the N first elements of your input array (first subset of size N)
Iterate by substracting the first element of your subarray, and adding the element next to it, which translate to looking at the next subarray
Return the subarray if the sum equals your target number
This should be O(input array size) in time and O(1) in memory, regardless of the initial array content. There is probably a more optimal solution using the range property of your initial array.
Here is an example in C++ :
void subsetSum(std::vector<int>() array, int subArraySize, int targetNumber)
{
int sum = 0;
for (int i = 0; i < subArraySize; ++i) // Initial sum
{
sum += array[i];
}
for (int i = subArraySize; i < array.size(), ++i)
{
sum -= array[subArraySize-i];
sum += array[i];
if (sum == targetNumber)
std::cout << subArraySize-i; // this print the starting position of the subarray
}
}
First find the contiguous subarray that solves this, or as close to contiguous as we can get. The center of this is going to be target/width if width is odd, or (target-1)/width, (target+1)/width if width is even.
Having found the center, add the same number of neighbors on both sides until you get to the desired width. The rightmost element of the array will need to be shifted further right in cases where there is no contiguous solution.
Ruby code:
def f(target, width)
arr = []
# put in center of array
if width % 2 == 0
arr.append target / width
arr.append target / width + 1
else
arr.append target/width
end
# repeatedly prepend next smallest integer
# and append next largest integer
while arr.length < width
arr.unshift(arr[0] - 1)
arr.append(arr[-1] + 1)
end
# increase the last element of the array to match
# the target sum. This is only necessary if there is no
# contiguous solution. Because of integer division,
# where we need to adjust it will always be to increase
# the sum of the array.
arr[-1] += target - arr.sum
return arr
end
Example run:
> f(12342, 7)
=> [1760, 1761, 1762, 1763, 1764, 1765, 1767]
Note that this code doesn't do any of the work of confirming that a solution exists in the range (1, 2000), but your code should.
So far so fast, but finding all subsets that solve this will be slow because there are many. You can find them by pushing elements to the left and right. in pairs.
Final answer will be the sum over i of: (number of ways of pushing elements to the left by a cumulative i spaces) (number of ways of pushing elements to the right by a cumulative i spaces.
To give a simple example: for a target of 13, width of 3, we start with [3,4,6].
pushes: arrays
0: [3, 4, 6]
1: [2, 4, 7], [2, 5, 6]
2: [1, 4, 8], [1, 5, 7], [2, 3, 8]
3: [1, 3, 9]
4: [1, 2, 10]
... and we're done. There will be a massive number of these, peaking (I think) when the width of the array is half the width of the range, and the initial array is centered in the range.
There are two sorted arrays A and B of equal length where A is sorted in ascending order and array B is sorted in descending order.
A = {1, 3, 7, 7, 7, 7}
B = {7, 7, 5, 5, 5, 4}
My task is to find two elements, one from A and other from B, so that their sum is maximum.
There is a constraint that I can choose any element from A but I have to choose the element from B in such an order that the index of the element of array B should be greater than that of the chosen element of A.
In this case, the maximum sum that can be chosen is 12. I have done it in O(n) by simply iterating from left to right.
I want to know whether there exists any better and more efficient way to find the sum of those two elements.
We know that the best sum is the largest value among the sequence C obtained by adding A and B pairwise.
A and B are monotonic, but C can be arbitrary, so there is no shortcut, you need to compute the whole of C, and O(N) is optimal.
As Yves Daoust points out, in principle, O(n) is optimal, but you can do some simple tricks, to save time in practice.
You can make usage of the maximum value of A.
const int maxA = A[sizeA-1];
In your loop, you can check the following two conditions to savely abort your loop searching for the maximum ( i is your loop variable).
// since B will be smaller the next time and A is at its max, no need to go further
if ( A[i] >= maxA ) break;
// if there is no chance left, to find a greater sum, since even maxA with the biggest
// B we are about to see is not big enough, break.
if ( maxA + B[i] <= currentMax ) break;
I am trying to write an sorting algorithm which takes an input array and produces the sorted array. Following are the constraints for an algorithm.
Time complexity = $O(n \log n)$
Only one comparison operation throughout the algorithm.
We have a function which can provide an median for a set of three elements.
I have tried finding a solution and following is my result.
we use the median function to get the smallest and the largest pair.
Example: Give an array A[1..n], we get the median of the first three elements, lets call it a set and we receive a $Median$. In next Iteration we remove the received median from the set and add next element to the set and again call Median function. This step when repeated over the length produces a pair of the largest and the smallest element for the entire array.
We use this pair, use the comparison operation and place them at position A[0] & A[n-1].
We repeat the same operation over the array A[1..n-2] to get another pair of the largest and smallest.
We take the median with the A[0] and newly received pair.
Median Value is placed at the A[1].
We take the median with the A[n-1] and newly received pair.
Median Value is placed at the A[n-2].
Step 3~7 are repeated to get a sorted array.
This algorithm satisfies the condition 2 & 3 but not the time complexity. I hope if someone can provide some guidance how to proceed further.
Quicksort (presented in reverse order) works like this. Suppose you have an array:
[1, 4, 5, 2, 3]
Quicksort in the abstract basically works by sliding towards the middle of the array from both the left and the right side. As we slide inwards, we want to swap items such that big things get moved to the right, and small things get moved to the left. Eventually we should have an array where all the small stuff is on the left, and all the big stuff is on the right.
The upshot of this process also guarantees that one element will be placed in the correct location (because everything to the left of it will be smaller, and everything to the right will be bigger, so it must be in the right position). That value is called the pivot. The first step of quicksort is to ensure the pivot is in the right place.
The way we do this is by selecting a random element to be our pivot - the item we wan to put into it's correct place. For this simple example we'll just use the last number (3). The pivot is our comparison value.
Once we have selected our pivot/comparison value, we then monitor the left-most element (1), and the right-most element (3). We'll call these the left-pointer and the right-pointer. The left-pointers job is to slide towards the middle of the array, stopping when it finds something that is larger than the pivot. The right pointer does the same thing, but it slides inward looking for values less than the pivot. In code:
while (true) {
while (array[++left] < pivot);
while (array[--right] > pivot) if (left == right) break;
if (left >= right) break; // If the pointers meet then exit the loop
swap(array[left], array[right]); // Swap the values otherwise.
}
So in our example above, when the left-pointer hits (4) it recognizes that that is higher than our pivot element and stops moving. The right pivot does the same thing from the right side, but stops when it hits (2) because that's lower than the pivot. When both sides stop, we do a swap, so we end up with:
[1, 2, 5, 4, 3]
Notice that we are getting closer to sorted. We continue to move both pointers inward until they both point to the same element, or they cross - whichever comes first. When that happens, we make one final step, which is to replace the pivot element (3) with whatever point the left/right-pointers are pointing to, which in this case would be (5) because they would both stop right in the middle. Then we swap, so that we get:
[1, 2, 3, 4, 5]
(Notice that we swap the original pivot (3) with the value pointed to by both sides (5))
This whole process is called a partition. In code it looks like this:
int partition(int *array, int lBound, int rBound) {
int pivot = array[rBound]; // Use the last element as a pivot
int left = lBound - 1; // Point to one before the first element
int right = rBound; // Point to the last element;
// We'll break out of the loop explicity
while (true) {
while (array[++left] < pivot);
while (array[--right] > pivot) if (left == right) break;
if (left >= right) break; // If the pointers meet then exit the loop
swap(array[left], array[right]); // Swap the pointers otherwise.
}
swap(array[left], array[rBound]); // Move the pivot item into its correct place
return left; // The left element is now in the right place
}
It's important to note that although the partition step fully sorted our array in this example, that's not ordinarily the point of the partition step. The point of the paritition step is to put one element into it's correct place, and to ensure that everything left of that element is less and everything to the right is more. Or in other words, to move the pivot value into its correct location and then guarantee that everything left of the pivot is smaller than it, and everything to the right is bigger. So although in this example the array was completely sorted, in general we can only guarantee that one item and one item only is in the correct location (and everything to the left and right is bigger/smaller respectively). This is why the partition method above returns left, because it tells the calling function that this one element is in the correct location (and the array has been correctly partitioned).
That is if we start with an array like:
[1, 7, 5, 4, 2, 9, 3]
Then the partition step would return something like this:
[1, 3, 2, [4], 7, 5, 9]
Where [4] is the only value guaranteed to be in the right place, but everything to the left is smaller than [4] and everything to the right is bigger (though not necessarily sorted!).
The second step is to perform this step recursively. That is, if we can put one element into it's correct location, then we should be able to eventually put all items into their correct location. That is the quicksort function. In code:
int *quicksort(int *array, int lBound, int rBound) {
if (lBound >= rBound) return array; // If the array is size 1 or less - return.
int pivot = partition(array, lBound, rBound); // Split the array in two.
quicksort(array, lBound, pivot - 1); // Sort the left size. (Recursive)
quicksort(array, pivot + 1, rBound); // Sort the right side. (Recursive)
return array;
}
Notice that the first step is to ensure that we have an array side of at least 2. It doesn't make sense to process anything smaller than that so we return if that condition isn't met. The next step is to call our partition function which will split the array according to the process outlined above. Once we know that the array has one element that is in correct position, we simply call quicksort again, but this time on the left side of the pivot, and then again on the right side of the pivot. Notice we don't include the pivot because the partition is guaranteed to put that into the correct location!
If we continue to call quicksort recursively, eventually we'll halve the array and partition it until we get arrays of size-one (which by definition is already sorted). So we partition, then halve, partition, halve, etc. until the entire array is sorted (in place). This gives us a sort in O(n lg(n)) time. Cool!
Here's a quick example of it's use:
int main() {
int array [] {1, 0, 2, 9, 3, 8, 4, 7, 5, 6};
quicksort(array, 0, 9); // Sort from zero to 9.
// Display the results
for (int index = 0; index != 10; ++index) {
cout << array[index] << endl;
}
return 0;
}
A good visual demonstration can be found here: http://www.youtube.com/watch?v=Z5nSXTnD1I4
Steps 1 and 2 are indeed the first steps of a correct solution. Once you know the smallest and largest elements, though, the median oracle is a comparison oracle; if you want to compare a[i] with a[j], then a[i] < a[j] exactly when a[i] = median(a[i], a[j], a[0]) where a[0] is the smallest element. So you can run straight quicksort or mergesort or whathaveyou.
How to find duplicates in the array. In the case of the inverse problem when you have to find a unique element of all is clear you just xor all the elements and as a result we obtain a unique element.For example
int a[] = {2, 2, 3, 3, 4, 5, 5, 16, 16};
int res = a[0];
for(int i = 0; i < 9; ++i)
res ^= a[i];
for example given an array
int a[] = {2, 4, 7, 8, 4, 5};
Here a duplicate is 4, but it is not clear how to find a duplicate element of the array.
You are describing the Element Distinctness Problem.
Without extra space (and hashing) there is no O(n) solution to element distinctness problem, so you cannot modify the "xor algorithm for duplicate" for this problem.
The solutions for this problem are:
sort and iterate the sorted array to find dupes (easy in sorted array). This is O(nlogn) time.
Build a histogram of the data (hash-based) and iterate the histogram when done to verify if all elements have value of 1 in the histogram - O(n) average case, O(n) space.
We can find the duplicates in an array in 0(n) time by using the following algorithm.
traverse the list for i= 0 to n-1 elements
{
//check for sign of A[abs(A[i])] ;
if positive then
make it negative by A[abs(A[i])]=-A[abs(A[i])];
else // i.e., A[abs(A[i])] is negative
this element (ith element of list) is a repetition
}
Hope it helps!
One solution can be to build a Hashset. It goes as follows-
1. Initialize an empty hashset.
2. For each element in array,
a. Check if it is present in the hashset.
If yes, you found the duplicate
If not, add it to the hashset.
This way you can find all the duplicates in the array.
Space complexity: O(n) ; Time complexity: O(n)
Consider an array of any given unique integers e.g. [1,3,2,4,6,5] how would one determine
the level of "sortedness", ranging from 0.0 to 1.0 ?
One way would be to evaluate the number of items that would have to be moved to make it sorted and then divide that by the total number of items.
As a first approach, I would detect the former as just the number of times a transition occurs from higher to lower value. In your list, that would be:
3 -> 2
6 -> 5
for a total of two movements. Dividing that by six elements gives you 33%.
In a way, this makes sense since you can simply move the 2 to between 1 and 3, and the 5 to between 4 and 6.
Now there may be edge cases where it's more efficient to move things differently but then you're likely going to have to write really complicated search algorithms to find the best solution.
Personally, I'd start with the simplest option that gave you what you wanted and only bother expanding if it turns out to be inadequate.
I would say the number of swaps is not a very good way to determine this. Most importantly because you can sort the array using a different number of swaps. In your case, you could switch 2<-->3 and 6<-->5, but you could also do a lot more switches.
How would you sort, say:
1 4 3 2 5
Would you directly switch 2 and 4, or would you switch 3 and 4, then 4 and 2, and then 3 and 2.
I would say a more correct method would be the number of elements in the right place divided by the total number of elements.
In your case, that would be 2/6.
Ok this is just an idea, but what if you can actually sort the array, i.e.
1,2,3,4,5,6
then get it as a string
123456
now get your original array in string
132465
and compare the Levenshtein distance between the two
I'll propose a different approach: let's count the number of non-descending sequences k in the array, then take its reversal: 1/k. For perfectly sorted array there's only one such sequence, 1/k = 1/1 = 1. This "unsortedness" level is the lowest when the array is sorted descendingly.
0 level is approached only asymptotically when the size of the array approaches infinity.
This simple approach can be computed in O(n) time.
In practice, one would measure unsortedness by the amount of work it needs to get sorted. That depends on what you consider "work". If only swaps are allowed, you could count the number op swaps needed. That has a nice upper bound of (n-1). For a mergesort kind of view you are mostly interested in the number of runs, since you'll need about log (nrun) merge steps. Statistically, you would probably take "sum(abs((rank - intended_rank))" as a measure, similar to a K-S test. But at eyesight, sequences like "HABCDEFG" (7 swaps, 2 runs, submean distance) and "HGFEDCBA" (4 swaps, 8 runs, maximal distance) are always showstoppers.
You could sum up the distances to their sorted position, for each item, and divide with the maximum such number.
public static <T extends Comparable<T>> double sortedMeasure(final T[] items) {
int n = items.length;
// Find the sorted positions
Integer[] sorted = new Integer[n];
for (int i = 0; i < n; i++) {
sorted[i] = i;
}
Arrays.sort(sorted, new Comparator<Integer>() {
public int compare(Integer i1, Integer i2) {
T o1 = items[i1];
T o2 = items[i2];
return o1.compareTo(o2);
}
public boolean equals(Object other) {
return this == other;
}
});
// Sum up the distances
int sum = 0;
for (int i = 0; i < n; i++) {
sum += Math.abs(sorted[i] - i);
}
// Calculate the maximum
int maximum = n*n/2;
// Return the ratio
return (double) sum / maximum;
}
Example:
sortedMeasure(new Integer[] {1, 2, 3, 4, 5}) // -> 0.000
sortedMeasure(new Integer[] {1, 5, 2, 4, 3}) // -> 0.500
sortedMeasure(new Integer[] {5, 1, 4, 2, 3}) // -> 0.833
sortedMeasure(new Integer[] {5, 4, 3, 2, 1}) // -> 1.000
One relevant measurement of sortedness would be "number of permutations needed to be sorted". In your case that would be 2, switching the 3,2 and 6,5. Then remains how to map this to [0,1]. You could calculate the maximum number of permutations needed for the length of the array, some sort of a "maximum unsortedness", which should yield a sortedness value of 0. Then take the number of permutations for the actual array, subtract it from the max and divide by max.