next_permutation для комбинаций или подмножеств в powerset

Есть ли какая-то эквивалентная библиотека или функция, которая даст мне следующую комбинацию набора значений, например next_permutation для меня?


person bobber205    schedule 21.04.2010    source источник
comment
вам нужно быть более конкретным   -  person    schedule 21.04.2010
comment
Возможно, даже немного конкретнее.   -  person sblom    schedule 21.04.2010


Ответы (6)


Комбинации: из статьи Марка Нельсона на ту же тему у нас есть next_combination http://marknelson.us/2002/03/01/next-permutation
Перестановки: из STL у нас есть std::next_permutation

 template <typename Iterator>
 inline bool next_combination(const Iterator first, Iterator k, const Iterator last)
 {
    if ((first == last) || (first == k) || (last == k))
       return false;
    Iterator itr1 = first;
    Iterator itr2 = last;
    ++itr1;
    if (last == itr1)
       return false;
    itr1 = last;
    --itr1;
    itr1 = k;
    --itr2;
    while (first != itr1)
    {
       if (*--itr1 < *itr2)
       {
          Iterator j = k;
          while (!(*itr1 < *j)) ++j;
          std::iter_swap(itr1,j);
          ++itr1;
          ++j;
          itr2 = k;
          std::rotate(itr1,j,last);
          while (last != j)
          {
             ++j;
             ++itr2;
          }
          std::rotate(k,itr2,last);
          return true;
       }
    }
    std::rotate(first,k,last);
    return false;
 }
person Community    schedule 02.07.2010
comment
Мне трудно доверять этому алгоритму с его ужасными именами переменных, очевидным мертвым кодом и т. д. - person sehe; 20.02.2018
comment
Я все еще не уверен в правильности, но, по крайней мере, вот более чистая версия paste.ubuntu.com /p/yXgtdjTyfD - person sehe; 20.02.2018
comment
Lazy-Web вернулся с этой реализацией, которая выглядит намного более мощной. howardhinnant.github.io/combinations.html /cc @howard-hinnant - person sehe; 21.02.2018

Я не знаю ни одного. Основная идея состоит в том, чтобы представить ваши элементы в виде битового массива. Так, например, у вас есть набор S:

S = {a, b, c}
[i, j, k] // a is the first bit, b is the second bit, c is the third bit

Чтобы сгенерировать Power Set of S (просто сгенерируйте все числа размером == 3 бита, используя простое сложение):

000 // {}
001 // {c}
010 // {b}
011 // {b, c}
100 // {a}
101 // {a, c}
110 // {a, b}
111 // {a, b, c}

Все, что вам нужно сделать, это найти, какие биты установлены, и связать их с элементами вашего набора.

И последнее замечание: есть одна комбинация, которую вы можете создать, когда хотите использовать все элементы, и эта комбинация является самим набором, потому что в комбинациях порядок не имеет значения, поэтому мы наверняка говорим о количестве элементов n, где 0 <= n <= size(S)

person AraK    schedule 21.04.2010
comment
Действительно очень нравится эта идея! - person bobber205; 22.04.2010
comment
К сожалению, у меня проблемы с реализацией этой идеи. - person bobber205; 22.04.2010
comment
@ bobber205 Не могли бы вы объяснить, с какими проблемами вы столкнулись? - person AraK; 22.04.2010
comment
Преобразование целого числа в битовый массив. Я бы поклялся, что раньше использовал для этого встроенную функцию, поэтому я пытаюсь написать ее сам. хД - person bobber205; 22.04.2010
comment
@bobber: концепция здесь - набор мощности. Это отличается от того, к чему обычно относятся комбинации. Вместо этого ищите это. Также см. stackoverflow.com/questions/2506119/combinations-algorithm . - person Potatoswatter; 22.04.2010
comment
@Potatoswatter Не могли бы вы объяснить, что вы имеете в виду. Потому что набор мощности набора - это набор всех возможных комбинаций набора. Я думаю, вы имеете в виду, что есть лучшие способы создания комбинаций для определенного количества элементов из набора. - person AraK; 22.04.2010
comment
@AraK: powerset — это набор всех возможных подмножеств набора. Обычное определение комбинации: en.wikipedia.org/wiki/Combination. - person Potatoswatter; 22.04.2010
comment
@Potatoswatter Поправьте меня, если я ошибаюсь. Подмножество множества — это просто комбинация этого множества с r элементами из n. По сути, Power Set представляет собой следующее: S — это набор из n элементов, тогда P(s) равен: C(n, 0) + C(n, 1) + C(n, 2) +...+ C(n, r) +...+ C(n, n) - person AraK; 22.04.2010
comment
C(n,k) — количество комбинаций. ∑[k=0..n](C(n,k)) = 2^n — известное тождество Треугольник Паскаля. Возможно, из-за того, что функция C принимает аргумент k, некоторые склонны считать, что вопрос о комбинациях основывается на определенном количестве выбираемых элементов. - person Potatoswatter; 22.04.2010

Я использовал эту библиотеку, когда мне нужно было это сделать. Он имеет интерфейс, очень похожий на std::next_permutation, поэтому его будет легко использовать, если вы использовали его раньше.

person Greg Rogers    schedule 21.04.2010
comment
Похоже, это не настоящая библиотека Boost… - person Potatoswatter; 22.04.2010
comment
Нет, но он работает и распространяется по лицензии Boost Software, так что просто вставьте заголовочный файл вместе с исходным кодом... - person Greg Rogers; 23.04.2010

Перечисление набора мощности (то есть всех комбинаций всех размеров) может использовать адаптацию алгоритма двоичного приращения.

template< class I, class O > // I forward, O bidirectional iterator
O next_subset( I uni_first, I uni_last, // set universe in a range
    O sub_first, O sub_last ) { // current subset in a range
    std::pair< O, I > mis = std::mismatch( sub_first, sub_last, uni_first );
    if ( mis.second == uni_last ) return sub_first; // finished cycle

    O ret;
    if ( mis.first == sub_first ) { // copy elements following mismatch
        std::copy_backward( mis.first, sub_last, ++ (ret = sub_last) ); 
    } else ret = std::copy( mis.first, sub_last, ++ O(sub_first) ); 
    * sub_first = * mis.second; // add first element not yet in result
    return ret; // return end of new subset. (Output range must accommodate.)
}

Требование двунаправленного итератора является неудачным, и его можно обойти.

Я собирался заставить его обрабатывать одинаковые элементы (мультимножества), но мне нужно идти спать :v( .

Использование:

#include <iostream>
#include <vector>
using namespace std;

char const *fruits_a[] = { "apples", "beans", "cherries", "durian" };
vector< string > fruits( fruits_a, fruits_a + sizeof fruits_a/sizeof *fruits_a );

int main() {
    vector< string > sub_fruits( fruits.size() );
    vector< string >::iterator last_fruit = sub_fruits.begin();

    while ( 
        ( last_fruit = next_subset( fruits.begin(), fruits.end(),
                     sub_fruits.begin(), last_fruit ) )
            != sub_fruits.begin() ) {
        cerr << "size " << last_fruit - sub_fruits.begin() << ": ";
        for ( vector<string>::iterator fit = sub_fruits.begin(); fit != last_fruit; ++ fit ) {
            cerr << * fit << " ";
        }
        cerr << endl;
    }
}

EDIT: Вот версия для мультимножеств. Набор не должен быть отсортирован, но идентичные элементы должны быть сгруппированы вместе.

#include <iterator>
#include <algorithm>
#include <functional>

template< class I, class O > // I forward, O bidirectional iterator
I next_subset( I uni_first, I uni_last, // set universe in a range
    O sub_first, O sub_last ) { // current subset in a range
    std::pair< O, I > mis = std::mismatch( sub_first, sub_last, uni_first );
    if ( mis.second == uni_last ) return sub_first; // finished cycle

    typedef std::reverse_iterator<O> RO;
    mis.first = std::find_if( RO(mis.first), RO(sub_first), std::bind1st(
        std::not_equal_to< typename std::iterator_traits<O>::value_type >(),
        * mis.second ) ).base(); // move mis.first before identical grouping

    O ret;
    if ( mis.first != sub_first ) { // copy elements after mismatch
        ret = std::copy( mis.first, sub_last, ++ O(sub_first) );
    } else std::copy_backward( mis.first, sub_last, ++ (ret = sub_last) );

    * sub_first = * mis.second; // add first element not yet in result
    return ret;
}

#include <vector>
#include <iostream>
using namespace std;

char const *fruits_a[] = { "apples", "apples", "beans", "beans", "cherries" };
vector< string > fruits( fruits_a, fruits_a + sizeof fruits_a/sizeof *fruits_a );

int main() {
    vector< string > sub_fruits( fruits.size() );
    vector< string >::iterator last_fruit = sub_fruits.begin();

    while (
        ( last_fruit = next_subset( fruits.begin(), fruits.end(),
                                    sub_fruits.begin(), last_fruit )
        ) != sub_fruits.begin() ) {
        cerr << "size " << last_fruit - sub_fruits.begin() << ": ";
        for ( vector<string>::iterator fit = sub_fruits.begin(); fit != last_fruit; ++ fit ) {
            cerr << * fit << " ";
        }
        cerr << endl;
    }
}

Вывод:

size 1: apples 
size 2: apples apples 
size 1: beans 
size 2: apples beans 
size 3: apples apples beans 
size 2: beans beans 
size 3: apples beans beans 
size 4: apples apples beans beans 
size 1: cherries 
size 2: apples cherries 
size 3: apples apples cherries 
size 2: beans cherries 
size 3: apples beans cherries 
size 4: apples apples beans cherries 
size 3: beans beans cherries 
size 4: apples beans beans cherries 
size 5: apples apples beans beans cherries 
person Community    schedule 22.04.2010

Поиск в Google по запросу C++ "next_combination" оказался это.

  • ищите от «середины» назад, пока не найдете элемент, меньший, чем * (конец - 1). Это элемент, который мы должны увеличить. Назовите это "head_pos".
  • ищите от «конца» назад, пока не найдете последний элемент, который все еще больше, чем *head_pos. Назовите его "tail_pos".
  • поменять местами head_pos и tail_pos. Переупорядочите элементы из [head_pos + 1, mid[ и [tail_pos + 1, end[] в порядке возрастания.
person Potatoswatter    schedule 21.04.2010
comment
@bobber: можно поконкретнее? - person Potatoswatter; 22.04.2010

В случае, если у Вас нет выбора, кроме как реализовать свою собственную функцию, возможно, этот ужас может немного помочь, а может быть, другие ужасы среди ответов на этот вопрос.

Алгоритм возврата всех комбинаций k элементов из н

Я написал это некоторое время назад, и теперь полная картина ускользает от меня :), но основная идея такова: у вас есть исходный набор, а текущая комбинация - это вектор итераторов к выбранным элементам. Чтобы получить следующую комбинацию, вы просматриваете свой набор справа налево в поисках «пузыря». Под «пузырьком» я подразумеваю один или несколько невыбранных соседних элементов. «Пузырь» может быть сразу справа. Затем в Вашей комбинации Вы заменяете первый элемент слева от «пузыря» и все остальные элементы из комбинации, находящиеся справа в наборе, на подмножество соседних элементов, которое начинается в начале « пузырь".

person Maciej Hehl    schedule 21.04.2010