Efficient way to count subsets with given sum - algorithm

Given N numbers I need to count subsets whose sum is S.
Note : Numbers in array need not to be distinct.
My current code is :
int countSubsets(vector<int> numbers,int sum)
{
vector<int> DP(sum+1);
DP[0]=1;
int currentSum=0;
for(int i=0;i<numbers.size();i++)
{
currentSum+=numbers[i];
for (int j=min(sum,currentSum);j>=numbers[i];j--)
DP[j]+=DP[j - numbers[i]];
}
return DP[sum];
}
Can their be any efficient way than this ?
Constraints are :
1 ≤ N ≤ 14
1 ≤ S ≤ 100000
1 ≤ A[i] ≤ 10000
Also their are 100 test cases in a single file. So please help if their exist better solution than this one

N is small (2^20 - is about 1 milion - 2^14 is really small value) - just iterate over all subsets, below I wrote pretty fast way to do that (bithacking). Treat integers as sets (that's enumerating subsets in Lexicographical order)
int length = array.Length;
int subsetCount = 0;
for (int i=0; i<(1<<length); ++i)
{
int currentSet = i;
int tempIndex = length-1;
int currentSum = 0;
while (currentSet > 0) // iterate over bits "from the right side"
{
if (currentSet & 1 == 1) // if current bit is "1"
currentSum += array[tempIndex];
currentSet >>= 1;
tempIndex--;
}
subsetCount += (currentSum == targetSum) ? 1 : 0;
}

You can use the fact that N is small: it is possible to generate all possible subsets of the given array and check if its sum is S for each of them. The time complexity is O(N * 2 ** N) or O(2 ** N)(it depends on the way of the generation). This solution should be fast enough for the given constraints.
Here is a pseudo code of an O(2 ** N) solution:
result = 0
void generate(int curPos, int curSum):
if curPos == N:
if curSum == S:
result++
return
// Do not take the current element.
generate(curPos + 1, curSum)
// Take it.
generate(curPos + 1, curSum + numbers[curPos])
generate(0, 0)
A faster solution based on the meet in the middle technique:
Let's generate all subsets for the first half of the array using the algorithm described above and put their sums into a map(which maps a sum to the number of subsets that have it. It can be either a hash table or just an array because S is relatively small). This step takes O(2 ** (N / 2)) time.
Now let's generate all subsets for the second half and for each of them add the number of subset that sum up to S - currentSum e in the first half(using the map constructed in 1.), where the currentSum is the sum of all elements in the current subseta. Again, we have O(2 ** (N / 2)) subsets and each of them is processed in O(1).
The total time complexity is O(2 ** (N / 2)).
A pseudo code for this solution:
Map<int, int> count = new HashMap<int, int>() // or an array of size S + 1.
result = 0
void generate1(int[] numbers, int pos, int currentSum):
if pos == numbers.length:
count[currentSum]++
return
generate1(numbers, pos + 1, currentSum)
generate1(numbers, pos + 1, currentSum + numbers[pos])
void generate2(int[] numbers, int pos, int currentSum):
if pos == numbers.length:
result += count[S - currentSum]
return
generate2(numbers, pos + 1, currentSum)
generate2(numbers, pos + 1, currentSum + numbers[pos])
generate1(the first half of numbers, 0, 0)
generate2(the second half of numbers, 0, 0)
If N is odd, the middle element can go to either the first half or to the second one. It doesn't matter where it goes as long as it goes to exactly one of them.

Related

Time and Space algorithm complexity

I am coding brute force approach for one coding problem - I need to count the maximum score path in the array with maximum step k.
Input: nums = [1,-1,-2,4,-7,3], k = 2
Output: 7
Explanation: You can choose your jumps forming the subsequence [1,-1,4,3] (underlined above). The sum is 7.
And I encountered a problem with calculating complexity. My thought was that on each elemnt we may call function k times, so time and space are O(k^n), where n is length of the array. My second guess: for first element we call function at most 1 time, for second 2 times (that is if k > i) and so on. So we have sum 1 + 2 + ... + k + k + ... + k = ((1 + k) / 2)k + ((k + k) / 2) / (n-k) = O(k^2). I think the first one is correct, but I can't tell for sure why :/
Here's my Java code:
public int maxResult(int[] nums, int k) {
return maxResult(nums, k, nums.length - 1);
}
private int maxResult(int[] nums, int k, int index) {
if (index == 0)
return nums[0];
int max = Integer.MIN_VALUE;
int start = index - k < 0 ? 0 : index - k;
for ( int i = start; i < index; i++ ) {
int res = maxResult(nums, k, i);
System.out.println(i);
max = Math.max(res, max);
}
return max + nums[index];
}
The recurrence relation for your code for a particular k is
C(n) = sum(C(n-i) for i = 1...k) for n>k
C(n) = C(1) + C(2) + ... + C(n-1) for n <= k
C(1) = 1
These are the recurrence relations for the higher-order Fibonacci numbers, shifted by k-1 places. That is, C(n) = kFib(k, n+k-1). The k-Fibonacci numbers grow as Theta(alpha^n) where alpha is some constant based on k -- for k=2, alpha is the golden ratio, and as k increases, alpha gets closer and closer to 2. (Specifically, alpha is is the positive root of (x^k - x^(k-1) - ... - x - 1)).
Therefore C(n) = kFib(k, n+k-1) = Theta(alpha^(n+k)).
Because alpha is always less than 2, O(2^(n+k)) is a simple correct bound, although not a tight one.

Finding kth element in the nth order of Farey Sequence

Farey sequence of order n is the sequence of completely reduced fractions, between 0 and 1 which when in lowest terms have denominators less than or equal to n, arranged in order of increasing size. Detailed explanation here.
Problem
The problem is, given n and k, where n = order of seq and k = element index, can we find the particular element from the sequence. For examples answer for (n=5, k =6) is 1/2.
Lead
There are many less than optimal solution available, but am looking for a near-optimal one. One such algorithm is discussed here, for which I am unable to understand the logic hence unable to apply the examples.
Question
Can some please explain the solution with more detail, preferably with an example.
Thank you.
I've read the method provided in your link, and the accepted C++ solution to it. Let me post them, for reference:
Editorial Explanation
Several less-than-optimal solutions exist. Using a priority queue, one
can iterate through the fractions (generating them one by one) in O(K
log N) time. Using a fancier math relation, this can be reduced to
O(K). However, neither of these solution obtains many points, because
the number of fractions (and thus K) is quadratic in N.
The “good” solution is based on meta-binary search. To construct this
solution, we need the following subroutine: given a fraction A/B
(which is not necessarily irreducible), find how many fractions from
the Farey sequence are less than this fraction. Suppose we had this
subroutine; then the algorithm works as follows:
Determine a number X such that the answer is between X/N and (X+1)/N; such a number can be determined by binary searching the range
1...N, thus calling the subroutine O(log N) times.
Make a list of all fractions A/B in the range X/N...(X+1)/N. For any given B, there is at most one A in this range, and it can be
determined trivially in O(1).
Determine the appropriate order statistic in this list (doing this in O(N log N) by sorting is good enough).
It remains to show how we can construct the desired subroutine. We
will show how it can be implemented in O(N log N), thus giving a O(N
log^2 N) algorithm overall. Let us denote by C[j] the number of
irreducible fractions i/j which are less than X/N. The algorithm is
based on the following observation: C[j] = floor(X*B/N) – Sum(C[D],
where D divides j). A direct implementation, which tests whether any D
is a divisor, yields a quadratic algorithm. A better approach,
inspired by Eratosthene’s sieve, is the following: at step j, we know
C[j], and we subtract it from all multiples of j. The running time of
the subroutine becomes O(N log N).
Relevant Code
#include <cassert>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
const int kMaxN = 2e5;
typedef int int32;
typedef long long int64_x;
// #define int __int128_t
// #define int64 __int128_t
typedef long long int64;
int64 count_less(int a, int n) {
vector<int> counter(n + 1, 0);
for (int i = 2; i <= n; i += 1) {
counter[i] = min(1LL * (i - 1), 1LL * i * a / n);
}
int64 result = 0;
for (int i = 2; i <= n; i += 1) {
for (int j = 2 * i; j <= n; j += i) {
counter[j] -= counter[i];
}
result += counter[i];
}
return result;
}
int32 main() {
// ifstream cin("farey.in");
// ofstream cout("farey.out");
int64_x n, k; cin >> n >> k;
assert(1 <= n);
assert(n <= kMaxN);
assert(1 <= k);
assert(k <= count_less(n, n));
int up = 0;
for (int p = 29; p >= 0; p -= 1) {
if ((1 << p) + up > n)
continue;
if (count_less((1 << p) + up, n) < k) {
up += (1 << p);
}
}
k -= count_less(up, n);
vector<pair<int, int>> elements;
for (int i = 1; i <= n; i += 1) {
int b = i;
// find a such that up/n < a / b and a / b <= (up+1) / n
int a = 1LL * (up + 1) * b / n;
if (1LL * up * b < 1LL * a * n) {
} else {
continue;
}
if (1LL * a * n <= 1LL * (up + 1) * b) {
} else {
continue;
}
if (__gcd(a, b) != 1) {
continue;
}
elements.push_back({a, b});
}
sort(elements.begin(), elements.end(),
[](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
return 1LL * lhs.first * rhs.second < 1LL * rhs.first * lhs.second;
});
cout << (int64_x)elements[k - 1].first << ' ' << (int64_x)elements[k - 1].second << '\n';
return 0;
}
Basic Methodology
The above editorial explanation results in the following simplified version. Let me start with an example.
Let's say, we want to find 7th element of Farey Sequence with N = 5.
We start with writing a subroutine, as said in the explanation, that gives us the "k" value (how many Farey Sequence reduced fractions there exist before a given fraction - the given number may or may not be reduced)
So, take your F5 sequence:
k = 0, 0/1
k = 1, 1/5
k = 2, 1/4
k = 3, 1/3
k = 4, 2/5
k = 5, 1/2
k = 6, 3/5
k = 7, 2/3
k = 8, 3/4
k = 9, 4/5
k = 10, 1/1
If we can find a function that finds the count of the previous reduced fractions in Farey Sequence, we can do the following:
int64 k_count_2 = count_less(2, 5); // result = 4
int64 k_count_3 = count_less(3, 5); // result = 6
int64 k_count_4 = count_less(4, 5); // result = 9
This function is written in the accepted solution. It uses the exact methodology explained in the last paragraph of the editorial.
As you can see, the count_less() function generates the same k values as in our hand written list.
We know the values of the reduced fractions for k = 4, 6, 9 using that function. What about k = 7? As explained in the editorial, we will list all the reduced fractions in range X/N and (X+1)/N, here X = 3 and N = 5.
Using the function in the accepted solution (its near bottom), we list and sort the reduced fractions.
After that we will rearrange our k values, as in to fit in our new array as such:
k = -, 0/1
k = -, 1/5
k = -, 1/4
k = -, 1/3
k = -, 2/5
k = -, 1/2
k = -, 3/5 <-|
k = 0, 2/3 | We list and sort the possible reduced fractions
k = 1, 3/4 | in between these numbers
k = -, 4/5 <-|
k = -, 1/1
(That's why there is this piece of code: k -= count_less(up, n);, it basically remaps the k values)
(And we also subtract one more during indexing, i.e.: cout << (int64_x)elements[k - 1].first << ' ' << (int64_x)elements[k - 1].second << '\n';. This is just to basically call the right position in the generated array.)
So, for our new re-mapped k values, for N = 5 and k = 7 (original k), our result is 2/3.
(We select the value k = 0, in our new map)
If you compile and run the accepted solution, it will give you this:
Input: 5 7 (Enter)
Output: 2 3
I believe this is the basic point of the editorial and accepted solution.

Number of ways to write n as sum of k numbers with restrictions on each part

Title says it all.
I need to split n as sum of k parts where each part ki should be in the range of
1 <= ki <= ri for given array r.
for example -
n = 4, k = 3 and r = [2, 2, 1]
ans = 2
#[2, 1, 1], [1, 2, 1]
Order matters. (2, 1, 1) and (1, 2, 1) are different.
I taught of solving it using stars and bars method, but be because of upper bound ri i dont know to to approach it.
i implemented a direct recursion function and it works fine for small values only.
Constraints of original problem are
1 <= n <= 107
1 <= k <= 105
1 <= ri <= 51
All calculations will be done under prime Modulo.
i found a similar problem here but i don't know how to implement in program. HERE
My brute-force recursive function -
#define MAX 1000
const int md = 1e9 + 7;
vector <int> k;
vector <map<int, int>> mapper;
vector <int> hold;
int solve(int sum, int cur){
if(cur == (k.size() - 1) && sum >= 1 && sum <= k[cur]) return 1;
if(cur == (k.size() - 1) && (sum < 1 || sum > k[cur])) return 0;
if(mapper[cur].find(sum) != mapper[cur].end())
return mapper[cur][sum];
int ans = 0;
int start = 1;
for(int i=start; i<=k[cur]; ++i){
int remain = sum - i;
int seg = (k.size() - cur) - 1;
if(remain < seg) break;
int res = solve(sum - i, cur + 1);
ans = (1LL * ans + res) % md;
}
mapper[cur][sum] = ans;
return ans;
}
int main(){
for(int i=0; i<MAX; ++i) k.push_back(51); // restriction for each part default 51
mapper.resize(MAX);
cout << solve(MAX + MAX, 0) << endl;
}
Instead of using a map for storing result of computation i used a two dimensional array and it gave very good performance boost but i cannot use it because of large n and k values.
How could i improve my recursive function or what are other ways of solving this problem.
That's interesting problem.
First lets say r_i = r_i - 1, n = n - k, numbers in [0, r_i] just for convenience. Now it's possible to add some fictitious numbers to make m the power of 2 without changing answer.
Now let's represent each interval of [0, r_i] as polynomial 1 * x ^ 0 + 1 * x ^ 1 + ... + 1 * x & r_i. Now if we multiply all these polynomials, coefficient at x ^ n will be answer.
Here is structure called Number Theoretic Transform (NTT) which allows to multiply two polynomials modulo p in O(size * log(size)).
If you will just multiply it using NTT, code will work in something like O(n * k * log (k * max(r))). It's very slow.
But now our fictive numbers help. Let's use divide and conquer technics. We'll make O(log m) steps, on each step multiply 2 * i-th and 2 * i + 1-th polynomials. In the next step we'll multiply resulting polynomials of this step.
Each step works in O(k * log(k)) and there is O(log(k)) steps, so algorhitm works in O(k * log^2 (k)). It's fast asymptotically, but I'm not sure if it fits TL for this problem. I think it will work about 20 seconds on max test.

Variant of Subset-Sum

Given 3 positive integers n, k, and sum, find exactly k number of distinct elements a_i, where
a_i \in S, 1 <= i <= k, and a_i \neq a_j for i \neq j
and, S is the set
S = {1, 2, 3, ..., n}
such that
\sum_{i=1}^{k}{a_i} = sum
I don't want to apply brute force (checking all possible combinations) to solve the problem due to exponential complexity. Can someone give me a hint towards another approach in solving this problem? Also, how can we exploit the fact the set S is sorted?
Is it possible to have complexity of O(k) in this problem?
An idea how to exploit 1..n set properties:
Sum of k continuous members of natural row starting from a is
sum = k*(2*a + (k-1))/2
To get sum of such subsequence about needed s, we can solve
a >= s/k - k/2 + 1/2
or
a <= s/k - k/2 + 1/2
compare s and sum values and make corrections.
For example, having s=173, n=40 and k=5, we can find
a <= 173/5 - 5/2 + 1/2 = 32.6
for starting number 32 we have sequence 32,33,34,35,36 with sum = 170, and for correction by 3 we can just change 36 with 39, or 34,35,36 with 35,36,37 and so on.
Seems that using this approach we get O(1) complexity (of course, there might exist some subtleties that I did miss)
It's possible to modify the pseudo-polynomial algorithm for subset sum.
Prepare a matrix P with dimension k X sum, and initialize all elements to 0. The meaning of P[p, q] == 1 is that there is a subset of p numbers summing to q, and P[p, q] == 0 means that such a subset has not yet been found.
Now iterate over i = 1, ..., n. In each iteration:
If i ≤ sum, set P[1, i] = 1 (there is a subset of size 1 that achieves i).
For any entry P[p, q] == 1, you now know that P[p + 1, q + i] should now be 1 too. If (p + 1, q + i) is within the boundaries of the matrix, set P[p + 1, q + i] = 1.
Finally, check if P[k, sum] == 1.
The complexity, assuming that all integer math operations is constant, is Θ(n2 sum).
There is a O(1) (so to speak) solution. What follows is a formal enough (I hope) development of the idea by #MBo.
It is sufficient to assume that S is a set of all integers and find a minimal solution. Solution K is smaller than K' iff max(K) < max(K'). If max(K) <= n, then K is also a solution to the original problem; otherwise, the original problem has no solution.
So we disregard n and find K, a minimal solution. Let g = max(K) = ceil(sum/k + (k - 1)/2) and s = g + (g-1) + (g-2) + ... (g-k+1) and s' = (g-1) + (g-2) + ... + (g-k). That is, s' is s shifted down by 1. Note s' = s - k.
Obviously s >= sum and (because K is minimal) s' < sum.
If s == sum the solution is K and we're done. Otherwise consider the set K+ = {g, g-1, ..., g-k}. We know that \sum(K+ \setminus {g}) < sum and \sum(K+ \setminus {g-k}) > sum, therefore, there's a single element g_i of K+ such that \sum (K+ \setminus {g_i}) = sum. The solution isK+ \setminus {\sum(K+)-sum}.
The solution in the form of 4 integers a, b, c, d where the actual set is understood to be [a..b] \setunion [c..d] can be computed in O(1).
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
unsigned long int arithmeticSum(unsigned long int a, unsigned long int k, unsigned long int n, unsigned long int *A);
void printSubset(unsigned long int k, unsigned long int *A);
int main(void)
{
unsigned long int n, k, sum;
// scan the respective values of sum, n, and k
scanf("%lu %lu %lu", &sum, &n, &k);
// find the starting element using the formula for the sum of an A.P. having 'k' terms
// starting at 'a', common difference 'd' ( = 1 in this problem), having 'sum' = sum
// sum = [k/2][2*a + (k-1)*d]
unsigned long startElement = (long double)sum/k - (long double)k/2 + (long double)1/2;
// exit if the arithmetic progression formed at the startElement is not within the required bounds
if(startElement < 1 || startElement + k - 1 > n)
{
printf("-1\n");
return 0;
}
// we now work on the k-element set [startElement, startElement + k - 1]
// create an array to store the k elements
unsigned long int *A = malloc(k * sizeof(unsigned long int));
// calculate the sum of k elements in the arithmetic progression [a, a + 1, a + 2, ..., a + (k - 1)]
unsigned long int currentSum = arithmeticSum(startElement, k, n, A);
// if the currentSum is equal to the required sum, then print the array A, and we are done
if(currentSum == sum)
{
printSubset(k, A);
}
// we enter into this block only if currentSum < sum
// i.e. we need to add 'something' to the currentSum in order to make it equal to sum
// i.e. we need to remove an element from the k-element set [startElement, startElement + k - 1]
// and replace it with an element of higher magnitude
// i.e. we need to replace an element in the set [startElement, startElement + k - 1] and replace
// it with an element in the range [startElement + k, n]
else
{
long int j;
bool done;
// calculate the amount which we need to add to the currentSum
unsigned long int difference = sum - currentSum;
// starting from A[k-1] upto A[0] do the following...
for(j = k - 1, done = false; j >= 0; j--)
{
// check if adding the "difference" to A[j] results in a number in the range [startElement + k, n]
// if it does then replace A[j] with that element, and we are done
if(A[j] + difference <= n && A[j] + difference > A[k-1])
{
A[j] += difference;
printSubset(k, A);
done = true;
break;
}
}
// if no such A[j] is found then, exit with fail
if(done == false)
{
printf("-1\n");
}
}
return 0;
}
unsigned long int arithmeticSum(unsigned long int a, unsigned long int k, unsigned long int n, unsigned long int *A)
{
unsigned long int currentSum;
long int j;
// calculate the sum of the arithmetic progression and store the each member in the array A
for(j = 0, currentSum = 0; j < k; j++)
{
A[j] = a + j;
currentSum += A[j];
}
return currentSum;
}
void printSubset(unsigned long int k, unsigned long int *A)
{
long int j;
for(j = 0; j < k; j++)
{
printf("%lu ", A[j]);
}
printf("\n");
}

Give a pseudocode for an algorithm that, given a list of n integers from the set {0, 1,

Problem statement:
Give a pseudocode for an algorithm that, given a list of n integers from the set {0, 1, . . . , k−1},
preprocesses its input to extract and store information that makes it possible to answer any query asking
how many of the n integers fall in the range [a..b] (with a and b being input parameters to the query) in
O(1) time. Explain how your algorithm works.
The preprocessing time should be O(n + k) in the worst case. Provide an argument showing that your
preprocessing algorithm meets that bound.
My attempt:
Counting Sort Pseudo Code
function countingSort(array, min, max)
count: array of (max – min + 1) elements //max is highest number, min is lowest
initialize count with 0 //set count = 0
for each number in array do
count[number – min] := count[number-min] + 1 //element i – min element = pos.
//pos + 1
done
z:= 0
for i from min to max do
while(count[ i – min] >0) do
array[z] := i
z := z + 1
count[i – min] := count [i – min] – 1
done
done
Find Pseudo Code
find(a, b)
??
Time Complexity Analysis:
We find that the total time complexity of Counting Sort takes O(k) time to initialize the array, O(n) time to read in the numbers and increment the appropriate element of counts. Another O(k) to create the array z, and another O(n) to scan and read through the list of numbers for a toal runtime of O(n+k).
Question:
The only problem I am having is that I do not know how I will report back to the user the number of integers that lie in between the range they have chosen [a..b] in O(1) time.. The only way I can think of retrieving that information is by looping through my array of sorted integers and having a counter to increment each time we find a number such that some some element is >= a && some element is <= b. Also should I include the actual numbers they have inputted in my search or rather should I just count the numbers in between them? The problem with looping through the array and having a counter to count the numbers between [a..b] is that this requires a for loop and is O(n). Any help would be greatly appreciated
The answer was trivial, just didn't think about it. After I use counting sort it resorts my list so that all I have to do is take the difference of the range asked of from the user. So for example
find(a,b)
numberofIntegersBetweenAandB = count[b] - count[a]
Working C++ example. Since the goal here is psuedo code, there are no error checks.
int * GenerateSums(int a[], size_t n, int min, int max)
{
size_t k = max + 2 - min;
int *sums = new int[k];
for(size_t i = 0; i < k; i++) // clear sums
sums[i] = 0;
for(size_t i = 0; i < n; i++) // set number of instances
sums[1+a[i]-min]++;
for(size_t i = 1; i < k; i++) // convert to cumulative sums
sums[i] += sums[i-1];
return sums;
}
int CountInRange(int sums[], int a, int b)
{
return sums[b+1] - sums[a];
}
int main()
{
int a[] = {4,0,3,4,2,4,1,4,3,4,3,2,4,2,3,1};
int *sums = GenerateSums(a, sizeof(a)/sizeof(a[0]), 0, 4);
int cnt;
cnt = CountInRange(sums, 0, 0); // returns 1
cnt = CountInRange(sums, 3, 4); // returns 10
cnt = CountInRange(sums, 0, 4); // returns 16
delete[] sums;
return 0;
}

Resources