recursive function for a partition problem - c++

The problem is to find the number of times a subset of the numbers in an array add up to a specific target number.
For example, there are two ways to partition the set {1, 3, 4, 5} so that the remaining elements add up to 5:
Select the 1 and the 4
Select just the 5
By contrast, there is no way to partition the set {1, 3, 4, 5} to get 11.
#include "genlib.h"
#include <iostream>
void RecursePart(int[], int, int, int&);
int Wrapper(int[], int, int);
int main() {
int arr[8] = {1,2,3,4,5,6,7,8};
cout << Wrapper(arr, 8, 11);
}
void RecursePart(int arr[], int len, int target, int& ctr) {
if (len == 1) {
if (arr[0] == target) {
ctr++;
}
return;
}
int sum, temp;
sum = temp = arr[0];
for (int j = 1; j < len; j++) {
if (sum == target) {
ctr++;
break;
}
sum = sum + arr[j];
if (sum == target) {
ctr++;
sum = temp;
continue;
}
if (sum > target) {
sum = temp;
continue;
}
if (sum < target) {
temp = sum;
continue;
}
}
RecursePart(arr + 1, len - 1, target, ctr);
}
int Wrapper(int arr[], int len, int target) {
int n = 0;
RecursePart(arr, len, target, n);
return n;
}
The problem is that the output I get is 1 but the number of times a subset of the numbers in the array that add up to 11 is greater than just 1. I have tried to trace the algorithm and I know that the problem must be in the for loop. There the algorithm skips some sums. How could I override this problem?

Like others stated, this is the Subset Sum problem (which is NP-complete), meaning you need an exponential time algorithm to solve it.
Just by looking at your function, You call RecursePart once, each time with len-1, and then have a for-loop of length n, which means your computation is O(n^2). This obviously won't solve an O(2^n) problem.
The following is a Recursive solution that creates the sum of subsets, and tries to see if they reached the target. If there is no option for the current subset to equal target, the "creation" of the current subset is stopped.
int RecursePart(int arr[], int len, int idx, int curr_sum, int target)
{
int count = 0;
// this subset is good
if (curr_sum == target)
return 1;
// the sum of the current subset exceeds target, no point in continuing
if (curr_sum > target || idx == len)
return 0;
count += RecursePart(arr, len, idx+1, curr_sum + arr[idx], target);
count += RecursePart(arr, len, idx+1, curr_sum, target);
return count;
}
This is my previous solution, which creates all possible subsets, and the ones that match the target are counted.
#include <iostream>
int Wrapper(int[], int, int);
int main() {
int arr[8] = {1,2,3,4,5,6,7,8};
std::cout << Wrapper(arr, 8, 11);
}
// counts the sum of a subset
int CountSet(int* arr, int* mask, int len)
{
int sum = 0;
for (int i=0; i < len; ++i)
{
if (mask[i])
{
sum += arr[i];
}
}
return sum;
}
int RecursePart(int arr[], int idx, int len, int* subset_mask, int target)
{
int count = 0;
if (idx == len)
{
if (CountSet(arr, subset_mask, len) == target)
return 1;
else
return 0;
}
// create the subset "without" me
subset_mask[idx] = 0;
count += RecursePart(arr, idx+1, len, subset_mask, target);
// now create the subset "with" me
subset_mask[idx] = 1;
count += RecursePart(arr, idx+1, len, subset_mask, target);
return count;
}
int Wrapper(int arr[], int len, int target) {
int* subset_mask = (int*)malloc(len*sizeof(int));
int res = RecursePart(arr, 0, len, subset_mask, target);
free(subset_mask);
return res;
}

Your function is simply wrong. You can see the same error with Wrapper(arr,4,5). You sum up elements from the left as long as they don't exceed the target, so that you can't find a solution which involves only some of those elements. You must rethink the algorithm.

Since you're using C++, you an also use a vector<int> to store are the intermediate solutions like so:
#include <iostream>
#include <vector>
using namespace std;
vector<int> RecursePart(int[], int, int, int&);
int Wrapper(int[], int, int);
int main() {
int arr[8] = {8,2,3,4,5,6,7,1};
cout << Wrapper(arr, 8, 11);
}
vector<int> RecursePart(int arr[], int len, int target, int& ctr)
{
vector<int> return_vec;
if (len == 1)
{
if (arr[0] == target)
{
ctr++;
return return_vec;
}
return_vec.push_back(arr[0]);
return return_vec;
}
int current = arr[0];
if (current == target)
ctr++;
if (current < target)
return_vec.push_back(current);
vector<int> temp;
temp = RecursePart(arr + 1, len - 1, target, ctr);
for (int i = 0; i < temp.size(); i ++)
{
if (temp[i] + current == target)
{
ctr++;
}
if (temp[i] + current < target)
return_vec.push_back(temp[i] + current);
if (temp[i] < target)
return_vec.push_back(temp[i]);
}
/*Debug Print
cout << "Current: " << current << endl;
for (int i = 0 ; i < return_vec.size(); i++)
{
cout << return_vec[i] << ", ";
}
cout << endl;
*/
return return_vec;
}
int Wrapper(int arr[], int len, int target) {
int n = 0;
RecursePart(arr, len, target, n);
return n;
}
To elaborate more, what's happening is we are using recursion to decompose the list into it's simplest part, which is a single number (the last element of the list), and make our return type all the possible sums that are less than our target. In the base-case, which is basically the last element of the array, if that number is equal to the target, we increment the counter, and return an empty vector, since the return-vector, which is all the possible sums less than our target, shouldn't have an element equal to or greater than our target. In all the previous recursive steps to the base-case, we get the returned vector, and then compare the current value, to all the elements in the return vector, which again, represent all the possible sums up to that point that are less than the target. We check to see if there are any sums that match the target, and then copy over any sums to our return vector that are less than the target. We then move back to the previous recursive step.
The complexity of this algorithm is still exponential ... if you enable the debug print section, you will see how the growth of the length of the vector for each iteration will grow in an exponential manner, and therefore for a given set of size N, the number of solutions you will have to check for will be in-line with O(2^N). This version of the solution though pairs that back a bit though by making sure that we only move valid solutions onto the next iteration, avoiding the need to calculate every possible subset.

Related

How can I use a variable from a previous recursive call?

The general problem:
Given an array of integers nums and an integer k. A continuous subarray is called nice if there are k odd numbers on it.
Return the number of nice sub-arrays.
My question:
I want to solve this problem recursively. The part I'm struggling to implement is how do I use the count from every previous recursive call and sum up all the counts, and return one last count like it is shown in the attempt.
Example:
Input: nums = [1,1,2,1,1], k = 3
Output: 2
Explanation: The only sub-arrays with 3 odd numbers are [1,1,2,1] and [1,2,1,1].
My attempt:
int helperFunction(vector<int> &nums, int k, int starter, int count)
{
int sum=0;
if (starter >= nums.size())
{
return count;
}
for (int i = starter; i < nums.size(); i++)
{
if (nums[i] % 2 == 1)
{
if (++sum == k)
{
count += nums.size() - i;
sum = 0;
}
}
}
return helperFunction(nums, k, starter + 1, count);
}
int numberOfSubarrays(vector<int> &nums, int k)
{
return helperFunction(nums, k, 0, 0);
}
int main()
{
vector<int> mine;
int myints[] = {1, 1, 2, 1, 1};
mine.assign(myints, myints + 5);
cout << "Output : " << numberOfSubarrays(mine, 3);
return 0;
}
Return Value:
The return value of this actual attempt is 0 means the program is at least not wrong syntactically.
it's not particularly good candidate for recursion. might be possible to solve with just one pass on the array.
That said, small adjustments to your code could make it work. There is no reason to pass count into the recursive method.
Your method calculates the number of subarrays that are 'nice' starting with the given index. Add that to the number that start at the next index and return it.
int helperFunction(vector<int> &nums, int k, int starter)
{
int sum=0, count=0;
if (starter >= nums.size())
{
return 0;
}
for (int i = starter; i < nums.size() && sum <= k; i++)
{
if (nums[i] % 2 == 1)
{
sum++;
}
if (sum == k)
{
count++;
}
}
return helperFunction(nums, k, starter + 1) + count;
}
I'm not sure your counting was correct. This could be optimized a lot, but this should demostrate the recursive approach.

How does this recursive function "end" rather than loop indefinitely (or throw an error)?

I came across this subset_sum coding problem/solution and am trying to understand in depth how it works.
To quickly summarize the problem: given an array of length n, find the largest number in the array and then find if there is any combination of numbers inside that array that, when summed, equal the largest number.
The setup of this problem is simple enough, but the actual function of the recursive function that checks all of the array combinations is where I'm a little confused. I've been reading about recursive functions and understand it conceptually, however, I'm having difficult following the exact flow of this particular program and am hoping someone could provide some assistance here.
string flag = "false";
void checkSubsets(int *arr, int i, int length, int *subset, int j, int max) {
if (i == length) {
int index = 0;
int setSum = 0;
while (index<j) {
setSum = setSum + subset[index];
++index;
if (setSum == max) {
flag = "true";
}
}
return;
}
checkSubsets(arr,i+1,length,subset,j, max);
subset[j] = arr[i];
checkSubsets(arr,i+1,length,subset,j+1, max);
}
string ArrayAddition(int arr[], int arrLength) {
int subset[100];
int max = arr[0];
int maxIndex = 0;
for (int i = 0; i<arrLength;i++) {
if (arr[i] > max) {
max = arr[i];
maxIndex = i;
}
}
for (int j = maxIndex; j<arrLength-1;j++) {
arr[j] = arr[j+1];
}
arr[arrLength-1] = 0;
checkSubsets(arr, 0, arrLength, subset, 0, max);
return flag;
}
The recursion ends when i reaches length:
void checkSubsets(int *arr, int i, int length, int *subset, int j, int max) {
if (i == length) {
// stuff
return; // ends here.
}
// else recurse:
checkSubsets(arr,i+1,length,subset,j, max); // i+1
subset[j] = arr[i];
checkSubsets(arr,i+1,length,subset,j+1, max); // i+1
}
For each call, you call it with i+1 and when i == length, it does not recurse deeper but does instead return.

Appears K times in an array C++

So I have an algorithm that is suppose to return the int that appears K times in an array. If more than 1 int appears K times, the higher value should be returned. My following algorithm is not working correctly. In the example below, it returns 1 when it should be returning 5.
#include <iostream>
#include <algorithm>
int appearsKTimes (int size, int inputArray[], int k) {
std::sort(inputArray, inputArray + size);
int i = 1, count = 1;
int element = inputArray[0];
int res = -1;
while (i < size) {
if (element == inputArray[i]) {
count++;
} else {
if (count == k) {
res = element;
}
element = inputArray[i];
count = 1;
}
i++;
}
std::cout << res << std::endl;
return res;
}
int main() {
const int size = 7;
int array[size] = {1, 1, 2, 2, 2, 5, 5};
int occurences = 2;
appearsKTimes(size, array, occurences);
}
if (count == k) {
res = element;
}
std::cout << res << std::endl;
check the count of the last element.
I also think it will be better to count from the end.You need to check the all array all the time if you count from the first element.
Your approach with sorting is good and requires only O(NlogN) time complexity, but consider an another one with hash table as well. It requires O(N) in time and O(N) in space. It is shorter and has only a few extra variables, so there is less chance of making mistake:
1) Compute number frequencies with unordered_map (hash table): O(N)
2) Find the largest number with exactly k occurences: O(N)
int appearsKTimes (int size, int inputArray[], int k) {
unordered_map<int, int> freqs;
for (int i = 0; i < size; i++) {
freqs[inputArray[i]]++;
}
int res = - 1;
for (int i = 0; i < size; i++) {
if (freqs[inputArray[i]] == k)
res = max(res, inputArray[i]);
}
return res;
}

Enumerate subsets with DFS

I want to enumerate all possible subsets of a given array, which should be 2^N in total. Here I use sum to represent all subsets since sums are distinct in this example. I use a auxiliary array called taken to mark if the current item is taken and do a DFS. Here is the code:
void dfs(vector<int>& res, vector<int>& nums, vector<bool>& taken, int sum, int pos) {
for (int i = pos; i < nums.size(); ++i) {
if (!taken[i]) {
taken[i] = true;
res.push_back(nums[i] + sum);
dfs(res, nums, taken, sum + nums[i], pos + 1);
taken[i] = false;
}
}
return;
}
int main() {
vector<int> test = {1, 10, 100, 1000};
vector<bool> t(4, false);
vector<int> res;
dfs(res, test, t, 0, 0);
return 0;
}
But this code does not return 2^n results in result.
I have fixed your code
void dfs(vector<int>& res, vector<int>& nums, int sum, int pos) {
for (int i = pos; i < nums.size(); ++i) {
res.push_back(sum + nums[i]);
dfs(res, nums, sum + nums[i], i + 1); // i + 1 instead of pos + 1
}
return;
}
int main() {
vector<int> test = {1, 2, 3};
vector<int> res;
dfs(res,test,0, 0);
cout << res.size() << endl;
copy(res.begin(), res.end(), ostream_iterator<int>(cout , " "));
cout <<endl;
return 0;
}
And since you do not visit the elements backwards (i.e. already added indexes) you do not have to maintain an array to check for visited.
void enumerate_all_subsets(vector <int> &res, const vector <int> &nums) {
if(nums.empty()) return;
for(auto i = 0ull; i < (1 << nums.size()); ++ i) {
int sum = 0;
for(auto k = 0; k < nums.size(); ++ k)
if((i >> k) & 1)
sum += nums[k];
res.push_back(sum);
}
}
This iterates over a vector of a size up to size 63 (if your unsigned long longs are 64 bit). Any larger vectors should probably be rethought as that would take a while as this is O(2^n), as you noted.
Basically each bit of the unsigned long long i represents a position in nums and if it should be added to the sum or not.

C++ Sorting Array Recursion

I am creating a small program that is supposed to sort integers in an array in ascending order, but I am extremely stuck on the algorithm that I am supposed to use. I cannot iterate through the array, I must instead use a recursion function. I am allowed to have an auxiliary function that can find the smallest index in the array, which I have done successfully, but I am having the hardest time figuring out how to use that function to sort my array in the recursion function. Here is the code that I have so far, I understand that my sortIntegers function is way off.
int main()
{
int numbers[] = {8, 2, 5, 1, 3};
sortingIntegers(numbers, 5);
return 0;
}
void sortingIntegers(int *list, int size) {
if (size == 1) {
for (int i = 0; i < size; i++) {
cout << list[i] << ", ";
}
} else {
for (int z = 0; z < size; z++) {
if (list[size - 1] == smallestIndex(list)) {
for (int y = 0; y < size; y++) {
swap(list[z], list[y]);
}
}
}
sortingIntegers(list, size - 1);
}
}
int smallestIndex(int *array) {
int smallest = array[0];
for (int i = 1; i < sizeof(array); i++) {
if (array[i] < smallest) {
smallest = array[i];
}
}
return smallest;
}
int main()
{
int numbers[] = {8, 2, 5, 1, 0};
sortingIntegers(numbers, 0, 5);
for (int i=0;i<5;i++)
cout << numbers[i] << ' ';
return 0;
}
void sortingIntegers(int *list, int left, int size) {
if (left == size)
return;
int smallest = smallestIndex(list, left, size);
int c = list[smallest];
list[smallest] = list[left];
list[left] = c;
sortingIntegers(list, left+1 ,size);
}
int smallestIndex(int *array, int left, int size) {
int smallest = array[left];
int smIndex = left;
for (int i = left+1; i < size; i++) {
if (array[i] < smallest) {
smallest = array[i];
smIndex = i;
}
}
return smIndex;
}
That's my solution based on yours. First of all sizeof(array) returns the size of pointer. Second I return the index of the smallest item, not it's value, then I swap it with the first element in list. And then I call the sorting for the list starting with another element (the left parameter), because I know that the list up to left-1 is already sorted.
A fully recursive solution:
To sort an array, find the smallest element and swap it to the first position. Then sort the rest of the array, until there is a single element left.
To find the smallest element in an array, take the smallest of -the first element and -the smallest element in the rest of the array, until there is a single element left.
int SmallestIndex(int Array[], int From, int To)
{
if (From == To-1)
return From; // Single element left
// Index of the smallest in the rest
int Index= SmallestIndex(Array, From + 1, To);
// Index of the smallest
return Array[From] < Array[Index] ? From : Index;
}
void Sort(int Array[], int From, int To)
{
if (From == To-1)
return; // Single element left
// Locate the smallest element
int Index= SmallestIndex(Array, From, To);
// Swap it to the first place
int Swap= Array[Index]; Array[Index]= Array[From]; Array[From]= Swap;
// Sort the rest
Sort(Array, From + 1, To);
}
Call Sort with (Array, 0, N).
You can use this snippet to sort an array using recursion. It's not difficult you need to think like this way if your array length is N recursion sort N - 1 length array and you have to sort only one element that is N or you can say the last element. I give you an example it will help you suppose you have to sort this array = [50, 40, 10, 30, 20] if recursion gives you array = [10,30,40,50,20] this array you just need to place 20 in a correct position to sort the array.
private static void sort(int[] array, int length) {
if (length == 1) return;
sort(array, length - 1);
var lastIndex = length - 1;
for (int index = lastIndex; index > 0; index --){
if (array[index] < array[index - 1]){
int temp = array[index];
array[index] = array[index - 1];
array[index - 1] = temp;
}
}
}