динамическое итеративное программирование для генерации комбинаций - PullRequest
2 голосов
/ 27 июля 2011

Обновлено с моей собственной версией программы:

Я пытаюсь выполнить итеративное динамическое программирование для генерации комбинации n choose k.

Скажем, у меня есть 4 вектора значений

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

Теперь я использую сложение в качестве агрегатной функции и хочу создать 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

Наивный способ сделать это - просмотреть каждую пару и найти результат.если N и k очень велики, это очень и очень неэффективно.Таким образом, альтернативным способом было бы рекурсивное / итеративное динамическое программирование.Рекурсия для очень больших N и k заняла бы много памяти, поэтому идеальным способом для этого было бы итеративное динамическое программирование, которое можно выполнить следующим образом:

Рассмотрим следующий заголовок строки таблицы: N изаголовок столбца равен k, и наша цель - найти N choose k: Table1

Мы можем найти комбинацию N choose k с использованием динамической программы следующим образом:

Table2

Подход заключается в следующем:

  1. Блок [0,1] и Блок [0,2] всегда возвращают [].{[] обозначает пустое значение, так как там нет значений}.
  2. Блок [1,1] получает [], вычисляет {v1} + [] (который сам является v1), сохраняет его в блоке [1,1].
  3. Блок [1,2] получает [], делает {v1} + [] & {v2} + [], сохраняет его в блоке [1,2].
  4. Блок [1,3] получает [], делает {v1} + [], {v2} + [] + {v3} U [], Блок [1,3].
  5. Блок [2, 4] получает:
    • [{v1}] от [1,1] и вычисляет {v1} + {v2},
    • [{v1} {v2}] из [1,2] и вычисляет {v1} + {v3} и {v2} + {v3}
    • [{v1}, {v2}, {v3}] из [1,3] и вычисляет {v4} +{v1}, {v4} + {v2} и {v4} + {v3}, сохраняет его в блоке [2,4].

Теперь у нас есть всезначения нам нужны в блоке [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> 50k и k ~ 10. Я знаю, что реализация алгоритма (моя программа) очень неэффективна.Есть ли способ улучшить это?Я думаю, что тот же алгоритм может быть реализован гораздо более элегантно.Буду признателен за любую оказанную помощь.Спасибо.

1 Ответ

1 голос
/ 27 июля 2011

Приношу свои извинения за неправильное понимание вашего ответа ранее, я не совсем понял, что вы пытались сделать в своем посте, я подумал, что вы просто ищете нерекурсивный способ вычисления nCk: P

Я создал класс CombinationGenerator для создания комбинаций векторов, которые, я полагаю, вам нужны.Он работает путем создания вектора целых чисел, представляющих индексы элементов для агрегации (ниже я включил функцию 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 = 50000 и k = 1000:

. Имеется 50000 векторов, каждый из которых содержит 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 переменных-членов, два целых, два длинных длинных и вектор, содержащий k целых (в данном случае 1000), поэтому: 2,200,032 + (2*4) + (2*8) + (1000*4) + 32 = 2,204,056 Bytes

У нас также есть векторпродолжениеснова получим результат для каждой итерации с помощью k ints: 2,204,056 + (1000*4) + 32 = 2,208,088 Bytes

Как вы можете видеть, это намного ниже ваших 4 ГБ памяти.ПРИМЕЧАНИЕ. Невозможно сохранить каждый из этих векторов в памяти независимо от того, какую реализацию вы использовали, так как было бы более 1030 * векторов, содержащих результаты.Даже если вы выберете небольшое значение k, например 10, у вас все равно будет больше 2.69 x 10^40.

Надеюсь, на этот раз я понял, о чем вы просите!Если нет, я попытаюсь снова понять, чего вы пытаетесь достичь.:)

...