简体   繁体   中英

Combinations of an array algorithm

I would like to find the combinations of an array of size 5 that adds up to 15. What would be the best way to go about doing this.

Suppose I had the array

7 8 10 5 3

What would be the best way to find all numbers that add up to 15 in C++

"the best" way depends on what you're optimizing.

If there are not many elements in the array, there's an easy combinatoric algorithm: for all lengths from 1 to n (where n is the number of elements in the array), check all possible sets of n numbers and print each which sums to fifteen.

That would likely be the best from a time-to-implement standpoint. A dynamic-programming solution (this is a DP problem) would likely be the best from a runtime efficiency standpoint; a DP solution here is O(N³) , where the combinatoric solution is much much more than that.

The gist of the DP algorithm (I'm not writing the code) is to go through your array, and keep track of all the possible sums that can be made with the sub-array you've seen so far. As you reach each new array element, go through all the partial sums you got before and add it to them (not removing the original partial sum). Whenever something hits 15 or passes it, discard that sum from the set you're tracking (print it if it hits 15 exactly).

my suggestion is go for a recursion.

keeping track of the baseindex and currentindex and try to accumulate values every recursion

return the integer value of the currentindex when accumulated value is 15 else if currentindex reaches 5 and accumulated value is not 15 return 0

when return is 0 and baseindex is still less than 5 then add 1 to base index and reset the current index and accumulated value and start recursion again.

If, as you mention in your comment, 10 is the highest number in the problem (also the maximum number of elements). Then a brute force (with clever bitmasking, see this tutorial ) will do:

// N is the number of elements and arr is the array.
for (int i = 0; i < (1 << N); ++i) {
    int sum = 0;
    for (int j = 0; j < N; ++j) if (i & (1 << j)) sum += arr[j];
    if (sum == required_sum); // Do something with the subset represented by i.
}

This algorithm has complexity O(N * 2^N). Note, the code is correct as long as N < 32. Notice the number of subsets with a certain sum can be exponential (more than 2^(N/2)). Example, {1, 1, 1, 1, .., 1} and sum = N/2.

If, however, N is large but N * required_sum is not very large (up to millions), one can use the following recurrence (with dynamic programming or memoization):

f(0, 0) = 1
f(0, n) = 0 where n > 0
f(k, n) = 0 where k < 0
f(k + 1, S) = f(k, S - arr[k]) + f(k, S) where k >= 0

where f(k, S) denotes the possibility of getting a sum S with a subset of elements 0..k. The dynamic programming table can be used to generate all the subsets. The running time of generating the table is O(N * S) where S is the required sum. The running time of generating the subsets from the table is proportional to the number of such subsets (which can be very large).

General notes about the problem:

The problem in general is NP-Complete . Therefore, it has no known polynomial time algorithm. It does have however a pseudo-polynomial time algorithm , namely the recurrence above.

Sort the array of the elements. maintain two pointers, one on the beginning of the sorted array, and the other on the end of it. if the sum of the two elements is greater than 15, decrease the 2nd pointer. if the sum is less than 15, increase the 1st pointer. if sum is equal to 15, record the two elements, and increase the 1st pointer.

Hope it works.

Recursion is one option I can think of. Because I had some spare time on my hands I threw together this function (although it's probably unnecessarily large, and unoptimised to the extreme). I only tested it with the numbers you provided.

void getCombinations( std::vector<int>& _list, std::vector<std::vector<int>>& _output, 
                      std::vector<int>& _cSumList = std::vector<int>(), int _sum = 0 )
{
    for ( std::vector<int>::iterator _it = _list.begin(); _it < _list.end(); ++_it)
    {
        _sum += *_it;
        _cSumList.push_back( *_it );
        std::vector<int> _newList;
        for ( std::vector<int>::iterator _itn = _list.begin(); _itn < _list.end(); ++_itn )
            if ( *_itn != *_it )
                _newList.push_back( *_itn );
        if ( _sum < 15 )
            getCombinations( _newList, _output, _cSumList, _sum );
        else if ( _sum == 15 )
        {
            bool _t = false;
            for ( std::vector<std::vector<int>>::iterator _itCOutputList = _output.begin(); _itCOutputList < _output.end(); ++_itCOutputList )
            {
                unsigned _count = 0;
                for ( std::vector<int>::iterator _ita = _itCOutputList->begin(); _ita < _itCOutputList->end(); ++_ita )
                    for ( std::vector<int>::iterator _itb = _cSumList.begin(); _itb < _cSumList.end(); ++_itb )
                        if ( *_itb == *_ita )
                            ++_count;
                if ( _count == _cSumList.size() )
                    _t = true;
            }
            if ( _t == false )
                _output.push_back( _cSumList );
        }
        _cSumList.pop_back();
        _sum -= *_it;
    }
}

Example usage with your numbers:

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<int> list;
    list.push_back( 7 );
    list.push_back( 8 );
    list.push_back( 10 );
    list.push_back( 5 );
    list.push_back( 3 );
    std::vector<std::vector<int>> output;
    getCombinations( list, output );
    for ( std::vector<std::vector<int>>::iterator _it = output.begin(); _it < output.end(); ++_it)
    {
        for ( std::vector<int>::iterator _it2 = (*_it).begin(); _it2 < (*_it).end(); ++_it2)
            std::cout << *(_it2) << ",";
        std::cout << "\n";
    }
    std::cin.get();
    return 0;
}

Best way is subjective. As I said, the code above could be improved tremendously, but should give you a starting point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM