summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/multi_index/example/rearrange.cpp
blob: 31388513d048299229a919fe95f96eedce584e76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/* 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;
}