簡體   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