Reduce subsequence problem complexity from exponential to polynomial?











up vote
0
down vote

favorite












I am working on the following problem:




Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set with sum divisible by m.



Input: The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. The first line of each test case contains an integer N and M where N denotes the size of the array and M is the number for which we have to check the divisibility. The second line of each test case contains N space separated integers denoting elements of the array A[ ].



Output: If there is a subset which is divisible by M print '1' else print '0'.




I have tried a recursive solution:



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(int a,int &m,int n,int sum) {
if ((sum%m)==0 && sum>0)
return true;
if (n==0)
return false;
return find_it(a,m,n-1,sum) || find_it(a,m,n-1,sum-a[n-1]);
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum += a[i];
}
bool answer = find_it(a,m,n,sum);
cout << answer << "n";
}
return 0;
}


Which works fine and get accepted, but then I tried top-down approach, and am getting TLE ("Time Limit Exceeded"). What am I doing wrong in this memoization?



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(
int a, int &m, int n, int sum,
unordered_map<int,unordered_map<int,bool>> &value,
unordered_map<int,unordered_map<int,bool>> &visited){

if ((sum%m)==0 && sum>0)
return true;
if(n==0)
return false;

if(visited[n][sum]==true)
return value[n][sum];

bool first = false,second = false;
first = find_it(a,m,n-1,su1m,value,visited);

if(sum<a[n-1])
{
second=false;
}
else
second = find_it(a,m,n-1,sum-a[n-1],value,visited);

visited[n][sum] = true;
value[n][sum] = first || second;
return value[n][sum];
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum+=a[i];
}
unordered_map<int,unordered_map<int,bool>> value;
unordered_map<int,unordered_map<int,bool>> visited;
cout << find_it(a,m,n,sum,value,visited) << "n";
}
return 0;
}









share|improve this question




















  • 3




    #include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
    – user4581301
    Nov 11 at 3:56






  • 1




    Hint: There's a solution in O(m*n).
    – aschepler
    Nov 11 at 4:20












  • @aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
    – mss
    Nov 11 at 4:29

















up vote
0
down vote

favorite












I am working on the following problem:




Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set with sum divisible by m.



Input: The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. The first line of each test case contains an integer N and M where N denotes the size of the array and M is the number for which we have to check the divisibility. The second line of each test case contains N space separated integers denoting elements of the array A[ ].



Output: If there is a subset which is divisible by M print '1' else print '0'.




I have tried a recursive solution:



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(int a,int &m,int n,int sum) {
if ((sum%m)==0 && sum>0)
return true;
if (n==0)
return false;
return find_it(a,m,n-1,sum) || find_it(a,m,n-1,sum-a[n-1]);
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum += a[i];
}
bool answer = find_it(a,m,n,sum);
cout << answer << "n";
}
return 0;
}


Which works fine and get accepted, but then I tried top-down approach, and am getting TLE ("Time Limit Exceeded"). What am I doing wrong in this memoization?



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(
int a, int &m, int n, int sum,
unordered_map<int,unordered_map<int,bool>> &value,
unordered_map<int,unordered_map<int,bool>> &visited){

if ((sum%m)==0 && sum>0)
return true;
if(n==0)
return false;

if(visited[n][sum]==true)
return value[n][sum];

bool first = false,second = false;
first = find_it(a,m,n-1,su1m,value,visited);

if(sum<a[n-1])
{
second=false;
}
else
second = find_it(a,m,n-1,sum-a[n-1],value,visited);

visited[n][sum] = true;
value[n][sum] = first || second;
return value[n][sum];
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum+=a[i];
}
unordered_map<int,unordered_map<int,bool>> value;
unordered_map<int,unordered_map<int,bool>> visited;
cout << find_it(a,m,n,sum,value,visited) << "n";
}
return 0;
}









share|improve this question




















  • 3




    #include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
    – user4581301
    Nov 11 at 3:56






  • 1




    Hint: There's a solution in O(m*n).
    – aschepler
    Nov 11 at 4:20












  • @aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
    – mss
    Nov 11 at 4:29















up vote
0
down vote

favorite









up vote
0
down vote

favorite











I am working on the following problem:




Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set with sum divisible by m.



Input: The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. The first line of each test case contains an integer N and M where N denotes the size of the array and M is the number for which we have to check the divisibility. The second line of each test case contains N space separated integers denoting elements of the array A[ ].



Output: If there is a subset which is divisible by M print '1' else print '0'.




I have tried a recursive solution:



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(int a,int &m,int n,int sum) {
if ((sum%m)==0 && sum>0)
return true;
if (n==0)
return false;
return find_it(a,m,n-1,sum) || find_it(a,m,n-1,sum-a[n-1]);
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum += a[i];
}
bool answer = find_it(a,m,n,sum);
cout << answer << "n";
}
return 0;
}


Which works fine and get accepted, but then I tried top-down approach, and am getting TLE ("Time Limit Exceeded"). What am I doing wrong in this memoization?



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(
int a, int &m, int n, int sum,
unordered_map<int,unordered_map<int,bool>> &value,
unordered_map<int,unordered_map<int,bool>> &visited){

if ((sum%m)==0 && sum>0)
return true;
if(n==0)
return false;

if(visited[n][sum]==true)
return value[n][sum];

bool first = false,second = false;
first = find_it(a,m,n-1,su1m,value,visited);

if(sum<a[n-1])
{
second=false;
}
else
second = find_it(a,m,n-1,sum-a[n-1],value,visited);

visited[n][sum] = true;
value[n][sum] = first || second;
return value[n][sum];
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum+=a[i];
}
unordered_map<int,unordered_map<int,bool>> value;
unordered_map<int,unordered_map<int,bool>> visited;
cout << find_it(a,m,n,sum,value,visited) << "n";
}
return 0;
}









share|improve this question















I am working on the following problem:




Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set with sum divisible by m.



Input: The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. The first line of each test case contains an integer N and M where N denotes the size of the array and M is the number for which we have to check the divisibility. The second line of each test case contains N space separated integers denoting elements of the array A[ ].



Output: If there is a subset which is divisible by M print '1' else print '0'.




I have tried a recursive solution:



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(int a,int &m,int n,int sum) {
if ((sum%m)==0 && sum>0)
return true;
if (n==0)
return false;
return find_it(a,m,n-1,sum) || find_it(a,m,n-1,sum-a[n-1]);
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum += a[i];
}
bool answer = find_it(a,m,n,sum);
cout << answer << "n";
}
return 0;
}


Which works fine and get accepted, but then I tried top-down approach, and am getting TLE ("Time Limit Exceeded"). What am I doing wrong in this memoization?



#include <iostream>
#include<unordered_map>
using namespace std;

bool find_it(
int a, int &m, int n, int sum,
unordered_map<int,unordered_map<int,bool>> &value,
unordered_map<int,unordered_map<int,bool>> &visited){

if ((sum%m)==0 && sum>0)
return true;
if(n==0)
return false;

if(visited[n][sum]==true)
return value[n][sum];

bool first = false,second = false;
first = find_it(a,m,n-1,su1m,value,visited);

if(sum<a[n-1])
{
second=false;
}
else
second = find_it(a,m,n-1,sum-a[n-1],value,visited);

visited[n][sum] = true;
value[n][sum] = first || second;
return value[n][sum];
}

int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum+=a[i];
}
unordered_map<int,unordered_map<int,bool>> value;
unordered_map<int,unordered_map<int,bool>> visited;
cout << find_it(a,m,n,sum,value,visited) << "n";
}
return 0;
}






c++ memoization subset-sum






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 14 at 20:43

























asked Nov 11 at 2:57









mss

13




13








  • 3




    #include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
    – user4581301
    Nov 11 at 3:56






  • 1




    Hint: There's a solution in O(m*n).
    – aschepler
    Nov 11 at 4:20












  • @aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
    – mss
    Nov 11 at 4:29
















  • 3




    #include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
    – user4581301
    Nov 11 at 3:56






  • 1




    Hint: There's a solution in O(m*n).
    – aschepler
    Nov 11 at 4:20












  • @aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
    – mss
    Nov 11 at 4:29










3




3




#include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
– user4581301
Nov 11 at 3:56




#include<bits/stdc++.h> should not be used. (why) using namespace std; should be avoided, especially at global scope. (why). Together they emphasize each other's worst aspect and can lead to some truly bizarre errors.
– user4581301
Nov 11 at 3:56




1




1




Hint: There's a solution in O(m*n).
– aschepler
Nov 11 at 4:20






Hint: There's a solution in O(m*n).
– aschepler
Nov 11 at 4:20














@aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
– mss
Nov 11 at 4:29






@aschepler, I guess you are talking about geeksforgeeks article on this problem .Thanks, but I am trying to know why the recursive code works but memoized code doesn't work(giving TLE).(learning dynamic programming right now).link-practice.geeksforgeeks.org/problems/…
– mss
Nov 11 at 4:29














2 Answers
2






active

oldest

votes

















up vote
1
down vote













There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.



Some additional remarks:




  1. You should use consistent indentation.

  2. Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.

  3. There is no reason to pass m as int& instead of int.

  4. A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.

  5. Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.


  6. bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.

  7. You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.






share|improve this answer






























    up vote
    0
    down vote













    Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.



    I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:




    • It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.

    • You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)


    The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.



    In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)



    edit



    (@mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.






    share|improve this answer























    • can you please elaborate your second paragraph more through bit of hint code.
      – mss
      Nov 14 at 20:52











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53245460%2freduce-subsequence-problem-complexity-from-exponential-to-polynomial%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.



    Some additional remarks:




    1. You should use consistent indentation.

    2. Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.

    3. There is no reason to pass m as int& instead of int.

    4. A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.

    5. Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.


    6. bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.

    7. You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.






    share|improve this answer



























      up vote
      1
      down vote













      There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.



      Some additional remarks:




      1. You should use consistent indentation.

      2. Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.

      3. There is no reason to pass m as int& instead of int.

      4. A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.

      5. Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.


      6. bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.

      7. You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.






      share|improve this answer

























        up vote
        1
        down vote










        up vote
        1
        down vote









        There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.



        Some additional remarks:




        1. You should use consistent indentation.

        2. Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.

        3. There is no reason to pass m as int& instead of int.

        4. A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.

        5. Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.


        6. bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.

        7. You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.






        share|improve this answer














        There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.



        Some additional remarks:




        1. You should use consistent indentation.

        2. Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.

        3. There is no reason to pass m as int& instead of int.

        4. A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.

        5. Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.


        6. bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.

        7. You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 11 at 3:33

























        answered Nov 11 at 3:26









        eukaryota

        1,01915




        1,01915
























            up vote
            0
            down vote













            Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.



            I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:




            • It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.

            • You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)


            The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.



            In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)



            edit



            (@mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.






            share|improve this answer























            • can you please elaborate your second paragraph more through bit of hint code.
              – mss
              Nov 14 at 20:52















            up vote
            0
            down vote













            Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.



            I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:




            • It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.

            • You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)


            The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.



            In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)



            edit



            (@mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.






            share|improve this answer























            • can you please elaborate your second paragraph more through bit of hint code.
              – mss
              Nov 14 at 20:52













            up vote
            0
            down vote










            up vote
            0
            down vote









            Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.



            I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:




            • It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.

            • You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)


            The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.



            In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)



            edit



            (@mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.






            share|improve this answer














            Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.



            I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:




            • It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.

            • You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)


            The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.



            In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)



            edit



            (@mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 15 at 6:49

























            answered Nov 13 at 12:43









            Luis Colorado

            3,9991717




            3,9991717












            • can you please elaborate your second paragraph more through bit of hint code.
              – mss
              Nov 14 at 20:52


















            • can you please elaborate your second paragraph more through bit of hint code.
              – mss
              Nov 14 at 20:52
















            can you please elaborate your second paragraph more through bit of hint code.
            – mss
            Nov 14 at 20:52




            can you please elaborate your second paragraph more through bit of hint code.
            – mss
            Nov 14 at 20:52


















             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53245460%2freduce-subsequence-problem-complexity-from-exponential-to-polynomial%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Xamarin.iOS Cant Deploy on Iphone

            Glorious Revolution

            Dulmage-Mendelsohn matrix decomposition in Python