动态迭代编程以生成组合

时间:2011-07-27 18:19:54

标签: c++ dynamic vector combinations

更新了我自己的程序版本:

我正在尝试进行迭代动态编程以生成n choose k组合。

说我有4个值向量

v1 : 1 1 1
v2 : 2 2 2
v3 : 3 3 3
v4 : 4 4 4

现在我使用addition作为我的聚合函数,我想生成4 choose 2向量组合,如下所示:

v1v2 : 3 3 3
v1v3 : 4 4 4
v1v4 : 5 5 5
v2v3 : 5 5 5
v2v4 : 6 6 6
v3v4 : 7 7 7

一种天真的方法是通过每一对并找到结果。如果Nk非常大,则效率非常低。因此,另一种方法是递归/迭代动态编程。非常大的N和k的递归将占用大量内存,因此理想的方法是迭代动态编程,可以按如下方式进行:

请考虑以下表格行标题为N,列标题为k,我们的目标是查找N choose kTable1

我们可以通过以下方式使用动态程序找到N choose k组合:

Table2

方法如下:

  1. 块[0,1]和块[0,2]始终返回[]。 {[]表示空值,因为没有值}。
  2. Block [1,1]收到[],计算{v1} + [](v1本身),将其保存到Block [1,1]。
  3. Block [1,2]收到[],{v1} + []& {v2} + [],将其保存到Block [1,2]。
  4. Block [1,3]收到[],{v1} + [],{v2} + [] + {v3} U [],Block [1,3]。
  5. Block [2,4]收到:
      来自[1,1]的
    • [{v1}]并计算{v1} + {v2},
    • 来自[1,2]的
    • [{v1} {v2}]并计算{v1} + {v3}和{v2} + {v3}
    • 来自[1,3]的
    • [{v1},{v2},{v3}]并计算{v4} + {v1},{v4} + {v2}和{v4} + {v3},保存阻止[2,4]。
  6. 现在我们在Block [2,4]中拥有了我们需要的所有值。如何在C ++中有效地编写这个概念?

    非常感谢任何帮助。感谢。

    这是我的想法:

    我不知道这是不对的。遗憾

    =============================================== ==========

    //Block [0...k][0...n]; 
    //Block[i][j] contains i-set groups (for eg :if i = 2 it will have v1v2, v1v3, etc..)
    
    //initially, Block[0][i] = [ ] for 0 <= i <= n and all other Block [i][j] = [ $ ]
    // "$" is just a symbol to indicate that no computation is done on that block
    
    algorithm(int k, int n) //k-point skyline groups from n number of points.
    {
       if( Block[k][n] != [ $ ] ) return memory[k][n];
    
       Group = [ ]; //G indicate a collection of vectors
       for( int i = k; i <= n; i++ )
       {
          Group` = algorithm(k-1, i-1);
          for( each v` in Group` )
          {
             Group = Group + (group` + v_i);
          }
       }
    memory[k][n] = Group;
    return Group;
    }
    

    =============================================== ==========

    以下是我上述算法的程序:

    #include <iostream>
    #include <iterator>
    #include <set>
    #include <vector>
    #include <map>
    #define DIMENSIONS 5 // No. of elements in the vector. eg. v1: 1 1 1 1 1 
    using namespace std;
    
    typedef std::set < int > my_set;  // To hold vector id's
    typedef std::vector < int > my_vector; // To hold values of the vector's
    typedef std::vector < std::pair < my_set, my_vector > > my_vector_pair;
    typedef std::map < my_set, my_vector > my_map;
    typedef std::vector < vector < std::pair < int,my_map > > > my_pair;
    typedef my_map::iterator m_it;
    
    my_vector_pair bases;  // To hold all the initial <id,vector_values> pair
    my_map data, G;
    my_pair memory;
    
    void print(my_map& data)
    {
        for( m_it it(data.begin()) ; it!=data.end(); ++it) 
        {   
            cout << "Id : ";
            copy(it->first.begin(), it->first.end(), ostream_iterator<int>(cout, " "));
            cout << " => value : ";
            copy (it->second.begin(),it->second.end(),ostream_iterator<int>(cout," "));
            cout << endl;
        }
        cout << "---------------------------------------------------------------\n";
    }
    
    my_map union_(my_map& G, int p) 
    {
        static my_map result;
        my_set id;
        my_vector scores;
        result.clear();
        for (m_it it(G.begin()); it != G.end(); ++it) 
        {
            id = it->first;
            scores = it->second;
            id.insert( bases.at(p-1).first.begin(),bases.at(p-1).first.end() );
    
                for (int j = 0; j < DIMENSIONS; j++) 
                {
                    scores.at(j) += bases.at(p - 1).second.at(j);
                }
                result.insert(make_pair(id, scores));
        }
        return result;
    }
    
    my_map algorithm_(int k, int n) {
    
        unsigned long size = memory.at(n).size();
        for (unsigned long i = 0; i < size; i++) {
            if (memory.at(n).at(i).first == k) {
                return memory.at(n).at(i).second; //if exists in hash table then no need to calculate again
            }
        }
        my_map G_k_1;
    
        if (k != n)
        {
            G_k_1 = algorithm_(k, n - 1);
            if(G_k_1.size() == 0)
                {
                    return G_k_1;
                }
        }
        G_k_1 = algorithm_(k - 1, n - 1);
        if(G_k_1.size() == 0)
        {
            return G_k_1;
        }
    
        G_k_1 = union_(G_k_1, n);
    
        if (k != n) {
            for (unsigned long i = 0; i < memory.at(n - 1).size(); i++) {
                if (memory.at(n - 1).at(i).first == k) {
                    G_k_1.insert(memory.at(n - 1).at(i).second.begin(), memory.at(n - 1).at(i).second.end());
                    memory.at(n - 1).at(i).second.clear();
                    break;
                }
            }
        }
        std::pair<int,my_map> temp;
        temp.first = k ;
        temp.second = G_k_1;
        memory.at(n).push_back( temp ); //storing in hash table for further use
        return memory.at(n).back().second;
    }
    
    
    int main()
    {
       my_vector v1,v2,v3,v4,v5;
       my_set s1,s2,s3,s4,s5;
       for(int i = 1; i<=5; ++i)
       {
          v1.push_back(1);
          v2.push_back(2);
          v3.push_back(3);
          v4.push_back(4);
          v5.push_back(5);
       }
    
    
       s1.insert(1);
       s2.insert(2);
       s3.insert(3);
       s4.insert(4);
       s5.insert(5);
    
       bases.insert(bases.end(),make_pair(s1,v1));
       bases.insert(bases.end(),make_pair(s2,v2));
       bases.insert(bases.end(),make_pair(s3,v3));
       bases.insert(bases.end(),make_pair(s4,v4));
       bases.insert(bases.end(),make_pair(s5,v5));
    
       my_set empty_set;
       my_vector empty_group(DIMENSIONS);
       G.insert(make_pair(empty_set,empty_group));
    
       vector<std::pair<int,my_map> > empty_element;
       empty_element.push_back(make_pair(0,G));
       for (int i = 0; i <= 5; i++) {  // 5 is the total number od vectors : v1,v2,v3,v4,v5
           memory.push_back(empty_element);
       }
    
    
    
       data.insert(bases.begin(),bases.end());
       cout << endl << "The intial set of vectors are : " << endl;
       print ( data );
    
       int k;
       cout << "N = 5 " << endl << "Enter the value of k : ";
       cin >> k;
    
       cout << "The values for N choose k are : " << endl;
       data = algorithm_(k,5); 
    
       print ( data ); 
    
    }
    

    如果您运行该程序,您就知道我想要实现什么以及以何种方式实现。这种算法(不是程序)对于较少数量的向量可能不是有效的,但是当N> 1时,它将是有效的。 50k和k~10。我知道算法(我的程序)的实现是非常低效的。有没有办法改善它?我认为可以以更优雅的方式实现相同的算法。任何帮助深表感谢。感谢。

1 个答案:

答案 0 :(得分:1)

我为以前误解你的答案而道歉,我真的不明白你在帖子中想要做什么,我以为你只是在寻找一种非递归的计算方法nCk:P

我创建了一个类CombinationGenerator来生成向量组合,我相信这就是你想要的。它的工作原理是生成一个int的向量,表示要聚合的元素的索引(我已经包含了一个main函数,下面应该有助于以编程方式解释它。)

以下是标题文件:http://pastebin.com/F5x4WKD9

源文件:http://pastebin.com/CTV1PLRb

这是一个示例主要功能:

typedef std::vector<int> vecInt;

int main() {

    // We have a deque containing 3 elements (try using experimenting with data
    // types to test space complexity, std::set or std::unordered_set might be an option)
    vecInt vec1;
    for( int i = 0; i < 3; i++ )
    {
        vec1.push_back(1);
    }
    vecInt vec2;
    for( int i = 0; i < 3; i++ )
    {
        vec2.push_back(2);
    }
    vecInt vec3;
    for( int i = 0; i < 3; i++ )
    {
        vec3.push_back(3);
    }
    vecInt vec4;
    for( int i = 0; i < 3; i++ )
    {
        vec4.push_back(4);
    }
    vecInt vec5;
    for( int i = 0; i < 3; i++ )
    {
        vec5.push_back(5);
    }

    std::deque<std::vector<int>> dequeVecs;
    dequeVecs.push_back( vec1 );
    dequeVecs.push_back( vec2 );
    dequeVecs.push_back( vec3 );
    dequeVecs.push_back( vec4 );
    dequeVecs.push_back( vec5 );

    // Create our CombinationGenerator:
    CombinationGenerator* gen = new CombinationGenerator();

    g_pCombinationGen = gen;

    gen = NULL;

    unsigned long long size = g_pCombinationGen->ComputeBinomialCoefficient( dequeVecs.size(), 2 );

    std::vector<int> currCombination;

    g_pCombinationGen->Initialize( dequeVecs.size(), 2, size );

    while( !g_pCombinationGen->IsFinished() )
    {
        currCombination = g_pCombinationGen->NextCombination();

        std::vector<int> result;
        for( int i = 0; i < dequeVecs[0].size(); i++ )
        {
            result.push_back( dequeVecs[currCombination[0]][i] + dequeVecs[currCombination[1]][i] );
        }

        std::cout << "(";
        for( int i = 0; i < result.size(); i++ )
        {
            std::cout << result[i];
        }
        std::cout << ")" << std::endl;

    }

    return 0;

}

虽然这可能看起来相当大,但如果你分析它的空间使用量(我们假设你使用的是n = 50,000和k = 1000:

有50,000个向量,每个向量包含3个整数(让我们假设每个32字节向量的开销相当苛刻,在大多数实现中通常大约为20):所以,(50,000 * 3 * 4) + (50,000 * 32) = 2,200,000 Bytes

然后你在deque中包含它,我们也假设它有32字节的开销:2,200,000 + 32 = 2,200,032 Bytes

我们还有一个运行组合生成器的实例,它有5个成员变量,两个整数,两个long long,以及一个包含k int的向量(在本例中为1000),所以:2,200,032 + (2*4) + (2*8) + (1000*4) + 32 = 2,204,056 Bytes < / p>

我们还有一个包含k ints每次迭代结果的向量:2,204,056 + (1000*4) + 32 = 2,208,088 Bytes

如您所见,这远低于您的4GB内存。注意:无论您使用什么实现,都不可能将这些向量中的每一个存储在内存中,因为会有超过9.94 x 10^2126个包含结果的向量。即使您选择了较小的k值(例如10),您仍然会超过2.69 x 10^40

我希望这次我明白你要求的是什么!如果没有,我会再次尝试了解你想要实现的目标。 :)