Partition a set into K subsets with equal sum - algorithm

I'm going through an exercise to partition a set into K subsets with equal sum.
Let's say
Input : arr = [2, 1, 4, 5, 6], K = 3
Output : Yes
we can divide above array into 3 parts with equal
sum as [[2, 4], [1, 5], [6]]
I found a solution here,
http://www.geeksforgeeks.org/partition-set-k-subsets-equal-sum/
// C++ program to check whether an array can be
// partitioned into K subsets of equal sum
#include <bits/stdc++.h>
using namespace std;
// Recursive Utility method to check K equal sum
// subsetition of array
/**
array - given input array
subsetSum array - sum to store each subset of the array
taken - boolean array to check whether element
is taken into sum partition or not
K - number of partitions needed
N - total number of element in array
curIdx - current subsetSum index
limitIdx - lastIdx from where array element should
be taken */
bool isKPartitionPossibleRec(int arr[], int subsetSum[], bool taken[],
int subset, int K, int N, int curIdx, int limitIdx)
{
if (subsetSum[curIdx] == subset)
{
/* current index (K - 2) represents (K - 1) subsets of equal
sum last partition will already remain with sum 'subset'*/
if (curIdx == K - 2)
return true;
// recursive call for next subsetition
return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
K, N, curIdx + 1, N - 1);
}
// start from limitIdx and include elements into current partition
for (int i = limitIdx; i >= 0; i--)
{
// if already taken, continue
if (taken[i])
continue;
int tmp = subsetSum[curIdx] + arr[i];
// if temp is less than subset then only include the element
// and call recursively
if (tmp <= subset)
{
// mark the element and include into current partition sum
taken[i] = true;
subsetSum[curIdx] += arr[i];
bool nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, curIdx, i - 1);
// after recursive call unmark the element and remove from
// subsetition sum
taken[i] = false;
subsetSum[curIdx] -= arr[i];
if (nxt)
return true;
}
}
return false;
}
// Method returns true if arr can be partitioned into K subsets
// with equal sum
bool isKPartitionPossible(int arr[], int N, int K)
{
// If K is 1, then complete array will be our answer
if (K == 1)
return true;
// If total number of partitions are more than N, then
// division is not possible
if (N < K)
return false;
// if array sum is not divisible by K then we can't divide
// array into K partitions
int sum = 0;
for (int i = 0; i < N; i++)
sum += arr[i];
if (sum % K != 0)
return false;
// the sum of each subset should be subset (= sum / K)
int subset = sum / K;
int subsetSum[K];
bool taken[N];
// Initialize sum of each subset from 0
for (int i = 0; i < K; i++)
subsetSum[i] = 0;
// mark all elements as not taken
for (int i = 0; i < N; i++)
taken[i] = false;
// initialize first subsubset sum as last element of
// array and mark that as taken
subsetSum[0] = arr[N - 1];
taken[N - 1] = true;
// call recursive method to check K-substitution condition
return isKPartitionPossibleRec(arr, subsetSum, taken,
subset, K, N, 0, N - 1);
}
// Driver code to test above methods
int main()
{
int arr[] = {2, 1, 4, 5, 3, 3};
int N = sizeof(arr) / sizeof(arr[0]);
int K = 3;
if (isKPartitionPossible(arr, N, K))
cout << "Partitions into equal sum is possible.\n";
else
cout << "Partitions into equal sum is not possible.\n";
}
This works in all scenarios.
Question
Let's say if I pass arr = [4, 4, 1, 3, 2, 3, 2, 1] and k = 4,the algorithm tried to solve it by adding 1+2+2 and then, 3+3 or 3+1 and so on. It doesn't gets the partition and finally solves it to [[4,1], [4,1], [3,2], [3,2]]. I am not sure how does this algorithm finds the alternative? I'm not able to follow up with the recursion.
What are the ways to solve it? Is the backtracking the only way?
Thanks!

Related

sum of array elements(any order) equal to k not continuous elements [duplicate]

This question already has an answer here:
subset sum find all subsets that add up to a number
(1 answer)
Closed 2 years ago.
I have 2 input
array: {3,6,9,0,2,1,3} // positive number and can repeat also
Sum = 9
Need to find a combination(order not mandatory) of array element which has total to Sum(here for example it's 9).
Output expected :
{3,6}
{9}
{6,3}
{3,2,1,3}
I am not able to solve it. So, please don't ask for my solution. Please help by solving in java.
This problem can be solved by printing all the subsets with given sum.
Have a look at the following implementation:
// A Java program to count all subsets with given sum.
import java.util.ArrayList;
public class SubSet_sum_problem
{
// dp[i][j] is going to store true if sum j is
// possible with array elements from 0 to i.
static boolean[][] dp;
static void display(ArrayList<Integer> v)
{
System.out.println(v);
}
// A recursive function to print all subsets with the
// help of dp[][]. Vector p[] stores current subset.
static void printSubsetsRec(int arr[], int i, int sum,
ArrayList<Integer> p)
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if (i == 0 && sum != 0 && dp[0][sum])
{
p.add(arr[i]);
display(p);
p.clear();
return;
}
// If sum becomes 0
if (i == 0 && sum == 0)
{
display(p);
p.clear();
return;
}
// If given sum can be achieved after ignoring
// current element.
if (dp[i-1][sum])
{
// Create a new vector to store path
ArrayList<Integer> b = new ArrayList<>();
b.addAll(p);
printSubsetsRec(arr, i-1, sum, b);
}
// If given sum can be achieved after considering
// current element.
if (sum >= arr[i] && dp[i-1][sum-arr[i]])
{
p.add(arr[i]);
printSubsetsRec(arr, i-1, sum-arr[i], p);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
static void printAllSubsets(int arr[], int n, int sum)
{
if (n == 0 || sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
dp = new boolean[n][sum + 1];
for (int i=0; i<n; ++i)
{
dp[i][0] = true;
}
// Sum arr[0] can be achieved with single element
if (arr[0] <= sum)
dp[0][arr[0]] = true;
// Fill rest of the entries in dp[][]
for (int i = 1; i < n; ++i)
for (int j = 0; j < sum + 1; ++j)
dp[i][j] = (arr[i] <= j) ? (dp[i-1][j] ||
dp[i-1][j-arr[i]])
: dp[i - 1][j];
if (dp[n-1][sum] == false)
{
System.out.println("There are no subsets with" +
" sum "+ sum);
return;
}
// Now recursively traverse dp[][] to find all
// paths from dp[n-1][sum]
ArrayList<Integer> p = new ArrayList<>();
printSubsetsRec(arr, n-1, sum, p);
}
//Driver Program to test above functions
public static void main(String args[])
{
int arr[] = {3, 6, 9, 0, 2, 1, 3};
int n = arr.length;
int sum = 9;
printAllSubsets(arr, n, sum);
}
}
Output:
[6, 3]
[9]
[0, 6, 3]
[0, 9]
[1, 2, 6]
[1, 2, 0, 6]
[3, 6]
[3, 0, 6]
[3, 1, 2, 3]
[3, 1, 2, 0, 3]

Finding contiguos subarray of equal sum

Given array : 8 3 5 2 10 6 7 9 5 2
So the o/p will be Yes.
as: {8,3,5} {10,6} {9,5,2} they all have same sum value i.e. 16.
But for this array : 1 4 9 6 2 12
o/p will be No.
as: No contiguous slide have same sum value
I was thinking to go with SubSetSum Algorithm / Kadane Maximum SubArray Algorithm but later I end up as all of the algorithms requires a target sum which is predefined.
But here we don't know the target sum
If desired sum is given, and all subarrays should be contiguous, then it's easily can be done in O(n).
Run a loop over array and maintain boundaries of slices (left and right indexes) and currentSum.
Start with first element as a 0. Boundaries will be [0, 0] (for simplicity we include right). Then in a loop you have three conditions.
If sum is less than desired, add right element to the sum and advance right index
If sum is greater than desired, remove left element from the sum and advance left index
If sum is equal to given, print the slice. To avoid this slice in next iteration, advance left index and adjust the sum.
Translated to code
public static void main(String[] args) {
int givenSum = 16;
int[] a = new int[] {8, 3, 5, 2, 10, 6, 7, 9, 5, 2};
// boundaries of slice
int left = 0; // defines position of slice
int right = 0; // exclusive
int currentSum = 0;
while (right < a.length) {
if (currentSum < givenSum) { // sum is not enough, add from the right
currentSum += a[right];
right++;
}
if (currentSum > givenSum) { // sum exceeds given, remove from the left
currentSum -= a[left];
left++;
}
if (currentSum == givenSum) { // boundaries of given sum found, print it
System.out.println(Arrays.toString(Arrays.copyOfRange(a, left, right)));
// remove the left element, so we can process next sums
currentSum -= a[left];
left++;
}
}
}
For your case it prints 4 slices which yields sum 16
[8, 3, 5]
[10, 6]
[7, 9]
[9, 5, 2]
EDIT:
As OP clarified, no given sum available, the goal is to check if there are at least two different contiguous subarrays present which yields equal sum.
The most straightforward algorithm is to generate all possible sums and check if there are duplicates
int[] a = new int[] {1, 4, 9, 6, 2, 12};
HashSet<Integer> sums = new HashSet<>();
int numOfSums = 0;
for (int left = 0; left < a.length - 1; left++) {
for (int right = left; right < a.length; right++) {
// sum from left to right
int sum = 0;
for (int k = left; k <= right; k++) {
sum += a[k];
}
numOfSums++;
sums.add(sum);
}
}
System.out.println(sums.size() == numOfSums);
Complexity of this is O(n^3), not a good one, but works.
Hint: One trick could be explored to boost it to O(n^2), you don't need to calculate sum for every pair of slices!
You can do it in the following way
You have the total sum = 48
Now the each subset would have a sum which would be equal to a factor of 48. The smaller the factor the more number of subsets you can break it into
For all factors of the sum, check if the answer is possible for that factor or not. This can be done in O(n) by simply traversing the array.
Time Complexity would be O(n * factors(sum))
Use dynamic programming to find all sub-sums of the array, then find the sub array with same sum. The complexity should be O(n2).
void subsum(int n, int* arr, int** sum) {
for (int i = 0; i < n; ++i) {
sum[i][i] = arr[i];
}
for (int l = 2; l <= n; ++l) {
for (int i = 0; i < n - l + 1; ++i) {
sum[i][i + l - 1] = sum[i][i + l - 2] + arr[i + l -1];
}
}
}

SUM exactly using K elements solution

Problem: On a given array with N numbers, find subset of size M (exactly M elements) that equal to SUM.
I am looking for a Dynamic Programming(DP) solution for this problem. Basically looking to understand the matrix filled approach. I wrote below program but didn't add memoization as i am still wondering how to do that.
#include <stdio.h>
#define SIZE(a) sizeof(a)/sizeof(a[0])
int binary[100];
int a[] = {1, 2, 5, 5, 100};
void show(int* p, int size) {
int j;
for (j = 0; j < size; j++)
if (p[j])
printf("%d\n", a[j]);
}
void subset_sum(int target, int i, int sum, int *a, int size, int K) {
if (sum == target && !K) {
show(binary, size);
} else if (sum < target && i < size) {
binary[i] = 1;
foo(target, i + 1, sum + a[i], a, size, K-1);
binary[i] = 0;
foo(target, i + 1, sum, a, size, K);
}
}
int main() {
int target = 10;
int K = 2;
subset_sum(target, 0, 0, a, SIZE(a), K);
}
Is the below recurrence solution makes sense?
Let DP[SUM][j][k] sum up to SUM with exactly K elements picked from 0 to j elements.
DP[i][j][k] = DP[i][j-1][k] || DP[i-a[j]][j-1][k-1] { input array a[0....j] }
Base cases are:
DP[0][0][0] = DP[0][j][0] = DP[0][0][k] = 1
DP[i][0][0] = DP[i][j][0] = 0
It means we can either consider this element ( DP[i-a[j]][j-1][k-1] ) or we don't consider the current element (DP[i][j-1][k]). If we consider current element, k is reduced by 1 which reduces the elements that needs to be considered and same goes when current element is not considered i.e. K is not reduced by 1.
Your solution looks right to me.
Right now, you're basically backtracking over all possibilities and printing each solution. If you only want one solution, you could add a flag that you set when one solution was found and check before continuing with recursive calls.
For memoization, you should first get rid of the binary array, after which you can do something like this:
int memo[NUM_ELEMENTS][MAX_SUM][MAX_K];
bool subset_sum(int target, int i, int sum, int *a, int size, int K) {
if (sum == target && !K) {
memo[i][sum][K] = true;
return memo[i][sum][K];
} else if (sum < target && i < size) {
if (memo[i][sum][K] != -1)
return memo[i][sum][K];
memo[i][sum][K] = foo(target, i + 1, sum + a[i], a, size, K-1) ||
foo(target, i + 1, sum, a, size, K);
return memo[i][sum][K]
}
return false;
}
Then, look at memo[_all indexes_][target][K]. If this is true, there exists at least one solution. You can store addition information to get you that next solution, or you can iterate with an i from found_index - 1 to 0 and check for which i you have memo[i][sum - a[i]][K - 1] == true. Then recurse on that, and so on. This will allow you to reconstruct the solution using just the memo array.
To my understanding, if only the feasibility of the input has to be checked, the problem can be solved with a two-dimensional state space
bool[][] IsFeasible = new bool[n][k]
where IsFeasible[i][j] is true if and only if there is a subset of the elements 1 to i which sum up to exactly j for every
1 <= i <= n
1 <= j <= k
and for this state space, the recurrence relation
IsFeasible[i][j] = IsFeasible[i-1][k-a[i]] || IsFeasible[i-1][k]
can be used, where the left-hand side of the or-operator || corresponds to selecting the i-th item and the right-hand side corresponds to to not selecting the i-th item. The actual choice of items could be obtained by backtracking or auxiliary information saved during evaluation.

Recurrence relation for total number of ways to subset sum

I am trying to find out total number of ways to reach a target in subset sum problem. Below is my approach.
Let DP[i, j] be 1 if sum of 'j' elements sum up to 'i' else it is 0 where 'a' be the input. So,
DP[i, j] = DP[i, j-1] + DP[i - a[j], j-1]
For input [10, 13, 15, 18, 20, 15] and target = 30
; we are looking for DP[30, 6] as the answer.
I am able to get it working with recursion (http://ideone.com/0sHhDL) but i need to do it with DP.
Once you have a recursive function written, all we need to add to make it efficient is to cache the return values. The modification is: Before making a recursive call, check that the computation with those arguments was not already done.
int NMAX = 100;
int cache[NMAX][NMAX];
int answer;
// Returns true if there is a subset of set[] with sun equal to given sum
int isSubsetSum(int set[], int n, int sum)
{
// Base Cases
if (sum == 0) {
answer++;
return 0; //return false here as we are after all the sums
}
if (n == 0 && sum != 0)
return 0;
if(cache[n][sum] == -1) {
// If last element is greater than sum, then ignore it
if (set[n-1] > sum)
return isSubsetSum(set, n-1, sum);
/* else, check if sum can be obtained by any of the following
(a) including the last element
(b) excluding the last element */
cache[n][sum] = isSubsetSum(set, n-1, sum) || isSubsetSum(set, n-1, sum-set[n-1]);
}
return cache[n][sum];
}
// Driver program to test above function
int main()
{
int set[] = {3, 34, 4, 12, 5, 2};
int sum = 9;
int n = sizeof(set)/sizeof(set[0]);
for(int i=0; i<NMAX; i++) for(int j=0; j<NMAX; j++)
cache[i][j] = -1;
isSubsetSum(set, n, sum);
printf("total %d\n", answer);
return 0;
}
In the code, the line
cache[n][sum] = isSubsetSum(set, n-1, sum) || isSubsetSum(set, n-1, sum-set[n-1]);
is equivalent to the recursive formula
DP[i, j] = DP[i, j-1] + DP[i - a[j], j-1]
The difference is that one is top-bottom, the other is bottom-top.

Sub array that produces a given sum and product

Given an array of length N. How will you find the minimum length
contiguous sub-array of whose sum is S and whose product is P.
For eg 5 6 1 4 6 2 9 7 for S = 17, Ans = [6, 2, 9] for P = 24, Ans = [4 6].
Just go from left to right, and sum all the numbers, if the sum > S, then throw away left ones.
import java.util.Arrays;
public class test {
public static void main (String[] args) {
int[] array = {5, 6, 1, 4, 6, 2, 9, 7};
int length = array.length;
int S = 17;
int sum = 0; // current sum of sub array, assume all positive
int start = 0; // current start of sub array
int minLength = array.length + 1; // length of minimum sub array found
int minStart = 0; // start of of minimum sub array found
for (int index = 0; index < length; index++) {
sum = sum + array[index];
// Find by add to right
if (sum == S && index - start + 1 < minLength) {
minLength = index - start + 1;
minStart = start;
}
while (sum >= S) {
sum = sum - array[start];
start++;
// Find by minus from left
if (sum == S && index - start + 1 < minLength) {
minLength = index - start + 1;
minStart = start;
}
}
}
// Found
if (minLength != length + 1) {
System.out.println(Arrays.toString(Arrays.copyOfRange(array, minStart, minStart + minLength)));
}
}
}
For your example, I think it is OR.
Product is nothing different from sum, except for calculation.
pseudocode:
subStart = 0;
Sum = 0
for (i = 0; i< array.Length; i++)
Sum = Sum + array[i];
if (Sum < targetSum) continue;
if (Sum == targetSum) result = min(result, i - subStart +1);
while (Sum >= targetSum)
Sum = Sum - array[subStart];
subStart++;
I think that'll find the result with one pass through the array. There's a bit of detail missing there in the result value. Needs a bit more complexity there to be able to return the actual subarray if needed.
To find the Product sub-array just substitute multiplication/division for addition/subtraction in the above algorithm
Put two indices on the array. Lets call them i and j. Initially j = 1 and i =0. If the product between i and j is less than P, increment j. If it is greater than P, increment i. If we get something equal to p, sum up the elements (instead of summing up everytime, maintain an array where S(i) is the sum of everything to the left of it. Compute sum from i to j as S(i) - S(j)) and see whether you get S. Stop when j falls out of the array length.
This is O(n).
You can use a hashmap to find the answer for product in O(N) time with extra space.

Resources