繁体   English   中英

数组算法的组合

[英]Combinations of an array algorithm

我想找到一个大小为5的数组的组合,这些数组的总和为15。什么是最好的方法?

假设我有数组

7 8 10 5 3

在C ++中查找所有加起来为15的数字的最佳方法是什么

“最佳”方式取决于您正在优化的内容。

如果数组中没有太多元素,则有一个简单的组合算法:对于从1到n所有长度(其中n是数组中元素的数量),检查所有可能的n数字集,并打印每个总数为15的数字。

从实现时间的角度来看,这可能是最好的。 从运行时效率的角度来看,动态编程解决方案(这是一个DP问题)可能是最好的。 这里的DP解决方案是O(N³) ,其中的组合解决方案远不止于此。

DP算法的要点(我不是在编写代码)是遍历您的数组,并跟踪到目前为止您所看到的子数组可能产生的所有总和。 当您到达每个新的数组元素时,请遍历之前获得的所有部分和并将其添加到它们中(而不是删除原始的部分和)。 每当某物达到或超过15时,就从您正在跟踪的集合中丢弃该总和(如果正好达到15则将其打印出来)。

我的建议是递归。

跟踪baseindex和currentindex并尝试在每次递归中累积值

当累加值是15时返回currentindex的整数值,否则,如果currentindex达到5而累加值不是15则返回0

当return为0且baseindex仍小于5时,则将1添加到基本索引中,并重置当前索引和累加值,然后再次开始递归。

如您在评论中所述,如果10是问题中的最高数字(也是元素的最大数目)。 然后将使用蛮力(具有巧妙的位屏蔽,请参阅本教程 ):

// 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.
}

该算法的复杂度为O(N * 2 ^ N)。 注意,只要N <32,代码就是正确的。请注意,具有一定总和的子集的数量可以是指数的(大于2 ^(N / 2))。 例如,{1,1,1,1,..,1}和sum = N / 2。

但是,如果N大但N * required_sum不是很大(最多数百万),则可以使用以下重复发生(通过动态编程或存储):

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

其中f(k,S)表示获得元素S为0..k的子集的和S的可能性。 动态编程表可用于生成所有子集。 生成表的运行时间为O(N * S),其中S是所需的总和。 从表中生成子集的运行时间与此类子集的数量(可能非常大)成正比。

有关该问题的一般说明:

通常,问题是NP-Complete 因此,它没有已知的多项式时间算法。 但是它确实有一个伪多项式时间算法 ,即上面的递归。

对元素数组进行排序。 维护两个指针,一个指针位于已排序数组的开头,另一个指针位于数组的结尾。 如果两个元素的总和大于15,则减少第二个指针。 如果总和小于15,则增加第一个指针。 如果sum等于15,则记录两个元素,并增加第一个指针。

希望它能工作。

递归是我能想到的一种选择。 因为我有一些空闲时间,所以我将这个功能组合在一起(尽管它可能不必要地大,并且没有达到最佳状态)。 我只用您提供的号码进行了测试。

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;
    }
}

您的电话号码用法示例:

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;
}

最好的方法是主观的。 就像我说的那样,上面的代码可以进行很大的改进,但是应该为您提供一个起点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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