266 lines
6.5 KiB
C++
266 lines
6.5 KiB
C++
/* Boost.MultiIndex example of use of rearrange facilities.
|
|
*
|
|
* Copyright 2003-2015 Joaquin M Lopez Munoz.
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
* See http://www.boost.org/libs/multi_index for library home page.
|
|
*/
|
|
|
|
#if !defined(NDEBUG)
|
|
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
|
|
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
|
|
#endif
|
|
|
|
#include <boost/config.hpp>
|
|
#include <boost/detail/iterator.hpp>
|
|
#include <boost/multi_index_container.hpp>
|
|
#include <boost/multi_index/random_access_index.hpp>
|
|
#include <boost/random/binomial_distribution.hpp>
|
|
#include <boost/random/uniform_real.hpp>
|
|
#include <boost/random/mersenne_twister.hpp>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <vector>
|
|
|
|
#if !defined(BOOST_NO_CXX11_HDR_RANDOM)
|
|
#include <random>
|
|
#endif
|
|
|
|
using boost::multi_index_container;
|
|
using namespace boost::multi_index;
|
|
|
|
/* We model a card deck with a random access array containing
|
|
* card numbers (from 0 to 51), supplemented with an additional
|
|
* index which retains the start ordering.
|
|
*/
|
|
|
|
class deck
|
|
{
|
|
BOOST_STATIC_CONSTANT(std::size_t,num_cards=52);
|
|
|
|
typedef multi_index_container<
|
|
int,
|
|
indexed_by<
|
|
random_access<>, /* base index */
|
|
random_access<> /* "start" index */
|
|
>
|
|
> container_type;
|
|
container_type cont;
|
|
|
|
public:
|
|
deck()
|
|
{
|
|
cont.reserve(num_cards);
|
|
get<1>(cont).reserve(num_cards);
|
|
for(std::size_t i=0;i<num_cards;++i)cont.push_back(i);
|
|
}
|
|
|
|
typedef container_type::iterator iterator;
|
|
typedef container_type::size_type size_type;
|
|
|
|
iterator begin()const{return cont.begin();}
|
|
iterator end()const{return cont.end();}
|
|
size_type size()const{return cont.size();}
|
|
|
|
template<typename InputIterator>
|
|
void rearrange(InputIterator it)
|
|
{
|
|
cont.rearrange(it);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
/* simply rearrange the base index like the start index */
|
|
|
|
cont.rearrange(get<1>(cont).begin());
|
|
}
|
|
|
|
std::size_t position(int i)const
|
|
{
|
|
/* The position of a card in the deck is calculated by locating
|
|
* the card through the start index (which is ordered), projecting
|
|
* to the base index and diffing with the begin position.
|
|
* Resulting complexity: constant.
|
|
*/
|
|
|
|
return project<0>(cont,get<1>(cont).begin()+i)-cont.begin();
|
|
}
|
|
|
|
std::size_t rising_sequences()const
|
|
{
|
|
/* Iterate through all cards and increment the sequence count
|
|
* when the current position is left to the previous.
|
|
* Resulting complexity: O(n), n=num_cards.
|
|
*/
|
|
|
|
std::size_t s=1;
|
|
std::size_t last_pos=0;
|
|
|
|
for(std::size_t i=0;i<num_cards;++i){
|
|
std::size_t pos=position(i);
|
|
if(pos<last_pos)++s;
|
|
last_pos=pos;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
};
|
|
|
|
/* A vector of reference_wrappers to deck elements can be used
|
|
* as a view to the deck container.
|
|
* We use a special implicit_reference_wrapper having implicit
|
|
* ctor from its base type, as this simplifies the use of generic
|
|
* techniques on the resulting data structures.
|
|
*/
|
|
|
|
template<typename T>
|
|
class implicit_reference_wrapper:public boost::reference_wrapper<T>
|
|
{
|
|
private:
|
|
typedef boost::reference_wrapper<T> super;
|
|
public:
|
|
implicit_reference_wrapper(T& t):super(t){}
|
|
};
|
|
|
|
typedef std::vector<implicit_reference_wrapper<const int> > deck_view;
|
|
|
|
/* Riffle shuffle is modeled like this: A cut is selected in the deck
|
|
* following a binomial distribution. Then, cards are randomly selected
|
|
* from one packet or the other with probability proportional to
|
|
* packet size.
|
|
*/
|
|
|
|
template<typename RandomAccessIterator,typename OutputIterator>
|
|
void riffle_shuffle(
|
|
RandomAccessIterator first,RandomAccessIterator last,
|
|
OutputIterator out)
|
|
{
|
|
static boost::mt19937 rnd_gen;
|
|
|
|
typedef typename boost::detail::iterator_traits<
|
|
RandomAccessIterator>::difference_type difference_type;
|
|
typedef boost::binomial_distribution<
|
|
difference_type> rnd_cut_select_type;
|
|
typedef boost::uniform_real<> rnd_deck_select_type;
|
|
|
|
rnd_cut_select_type cut_select(last-first);
|
|
RandomAccessIterator middle=first+cut_select(rnd_gen);
|
|
difference_type s0=middle-first;
|
|
difference_type s1=last-middle;
|
|
rnd_deck_select_type deck_select;
|
|
|
|
while(s0!=0&&s1!=0){
|
|
if(deck_select(rnd_gen)<(double)s0/(s0+s1)){
|
|
*out++=*first++;
|
|
--s0;
|
|
}
|
|
else{
|
|
*out++=*middle++;
|
|
--s1;
|
|
}
|
|
}
|
|
std::copy(first,first+s0,out);
|
|
std::copy(middle,middle+s1,out);
|
|
}
|
|
|
|
struct riffle_shuffler
|
|
{
|
|
void operator()(deck& d)const
|
|
{
|
|
dv.clear();
|
|
dv.reserve(d.size());
|
|
riffle_shuffle(
|
|
d.begin(),d.end(),std::back_inserter(dv)); /* do the shuffling */
|
|
d.rearrange(dv.begin()); /* apply to the deck */
|
|
}
|
|
|
|
private:
|
|
mutable deck_view dv;
|
|
};
|
|
|
|
/* A truly random shuffle (up to stdlib implementation quality) using
|
|
* std::shuffle.
|
|
*/
|
|
|
|
struct random_shuffler
|
|
{
|
|
void operator()(deck& d)
|
|
{
|
|
dv.clear();
|
|
dv.reserve(d.size());
|
|
std::copy(d.begin(),d.end(),std::back_inserter(dv));
|
|
shuffle_view();
|
|
d.rearrange(dv.begin()); /* apply to the deck */
|
|
}
|
|
|
|
private:
|
|
deck_view dv;
|
|
|
|
#if !defined(BOOST_NO_CXX11_HDR_RANDOM)
|
|
std::mt19937 e;
|
|
|
|
void shuffle_view()
|
|
{
|
|
std::shuffle(dv.begin(),dv.end(),e);
|
|
}
|
|
#else
|
|
/* for pre-C++11 compilers we use std::random_shuffle */
|
|
|
|
void shuffle_view()
|
|
{
|
|
std::random_shuffle(dv.begin(),dv.end());
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/* Repeat a given shuffling algorithm repeats_num times
|
|
* and obtain the resulting rising sequences number. Average
|
|
* for tests_num trials.
|
|
*/
|
|
|
|
template<typename Shuffler>
|
|
double shuffle_test(
|
|
unsigned int repeats_num,unsigned int tests_num
|
|
BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Shuffler))
|
|
{
|
|
deck d;
|
|
Shuffler sh;
|
|
unsigned long total=0;
|
|
|
|
for(unsigned int n=0;n<tests_num;++n){
|
|
for(unsigned m=0;m<repeats_num;++m)sh(d);
|
|
total+=d.rising_sequences();
|
|
d.reset();
|
|
}
|
|
|
|
return (double)total/tests_num;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
unsigned rifs_num=0;
|
|
unsigned tests_num=0;
|
|
|
|
std::cout<<"number of riffle shuffles (vg 5):";
|
|
std::cin>>rifs_num;
|
|
std::cout<<"number of tests (vg 1000):";
|
|
std::cin>>tests_num;
|
|
|
|
std::cout<<"shuffling..."<<std::endl;
|
|
|
|
std::cout<<"riffle shuffling\n"
|
|
" avg number of rising sequences: "
|
|
<<shuffle_test<riffle_shuffler>(rifs_num,tests_num)
|
|
<<std::endl;
|
|
|
|
std::cout<<"random shuffling\n"
|
|
" avg number of rising sequences: "
|
|
<<shuffle_test<random_shuffler>(1,tests_num)
|
|
<<std::endl;
|
|
|
|
return 0;
|
|
}
|