blob: 31388513d048299229a919fe95f96eedce584e76 [file] [log] [blame]
Brian Silverman3cbbaca2018-08-04 23:38:07 -07001/* Boost.MultiIndex example of use of rearrange facilities.
2 *
3 * Copyright 2003-2015 Joaquin M Lopez Munoz.
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or copy at
6 * http://www.boost.org/LICENSE_1_0.txt)
7 *
8 * See http://www.boost.org/libs/multi_index for library home page.
9 */
10
11#if !defined(NDEBUG)
12#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
13#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
14#endif
15
16#include <boost/config.hpp>
17#include <boost/detail/iterator.hpp>
18#include <boost/multi_index_container.hpp>
19#include <boost/multi_index/random_access_index.hpp>
20#include <boost/random/binomial_distribution.hpp>
21#include <boost/random/uniform_real.hpp>
22#include <boost/random/mersenne_twister.hpp>
23#include <algorithm>
24#include <iostream>
25#include <iterator>
26#include <vector>
27
28#if !defined(BOOST_NO_CXX11_HDR_RANDOM)
29#include <random>
30#endif
31
32using boost::multi_index_container;
33using namespace boost::multi_index;
34
35/* We model a card deck with a random access array containing
36 * card numbers (from 0 to 51), supplemented with an additional
37 * index which retains the start ordering.
38 */
39
40class deck
41{
42 BOOST_STATIC_CONSTANT(std::size_t,num_cards=52);
43
44 typedef multi_index_container<
45 int,
46 indexed_by<
47 random_access<>, /* base index */
48 random_access<> /* "start" index */
49 >
50 > container_type;
51 container_type cont;
52
53public:
54 deck()
55 {
56 cont.reserve(num_cards);
57 get<1>(cont).reserve(num_cards);
58 for(std::size_t i=0;i<num_cards;++i)cont.push_back(i);
59 }
60
61 typedef container_type::iterator iterator;
62 typedef container_type::size_type size_type;
63
64 iterator begin()const{return cont.begin();}
65 iterator end()const{return cont.end();}
66 size_type size()const{return cont.size();}
67
68 template<typename InputIterator>
69 void rearrange(InputIterator it)
70 {
71 cont.rearrange(it);
72 }
73
74 void reset()
75 {
76 /* simply rearrange the base index like the start index */
77
78 cont.rearrange(get<1>(cont).begin());
79 }
80
81 std::size_t position(int i)const
82 {
83 /* The position of a card in the deck is calculated by locating
84 * the card through the start index (which is ordered), projecting
85 * to the base index and diffing with the begin position.
86 * Resulting complexity: constant.
87 */
88
89 return project<0>(cont,get<1>(cont).begin()+i)-cont.begin();
90 }
91
92 std::size_t rising_sequences()const
93 {
94 /* Iterate through all cards and increment the sequence count
95 * when the current position is left to the previous.
96 * Resulting complexity: O(n), n=num_cards.
97 */
98
99 std::size_t s=1;
100 std::size_t last_pos=0;
101
102 for(std::size_t i=0;i<num_cards;++i){
103 std::size_t pos=position(i);
104 if(pos<last_pos)++s;
105 last_pos=pos;
106 }
107
108 return s;
109 }
110};
111
112/* A vector of reference_wrappers to deck elements can be used
113 * as a view to the deck container.
114 * We use a special implicit_reference_wrapper having implicit
115 * ctor from its base type, as this simplifies the use of generic
116 * techniques on the resulting data structures.
117 */
118
119template<typename T>
120class implicit_reference_wrapper:public boost::reference_wrapper<T>
121{
122private:
123 typedef boost::reference_wrapper<T> super;
124public:
125 implicit_reference_wrapper(T& t):super(t){}
126};
127
128typedef std::vector<implicit_reference_wrapper<const int> > deck_view;
129
130/* Riffle shuffle is modeled like this: A cut is selected in the deck
131 * following a binomial distribution. Then, cards are randomly selected
132 * from one packet or the other with probability proportional to
133 * packet size.
134 */
135
136template<typename RandomAccessIterator,typename OutputIterator>
137void riffle_shuffle(
138 RandomAccessIterator first,RandomAccessIterator last,
139 OutputIterator out)
140{
141 static boost::mt19937 rnd_gen;
142
143 typedef typename boost::detail::iterator_traits<
144 RandomAccessIterator>::difference_type difference_type;
145 typedef boost::binomial_distribution<
146 difference_type> rnd_cut_select_type;
147 typedef boost::uniform_real<> rnd_deck_select_type;
148
149 rnd_cut_select_type cut_select(last-first);
150 RandomAccessIterator middle=first+cut_select(rnd_gen);
151 difference_type s0=middle-first;
152 difference_type s1=last-middle;
153 rnd_deck_select_type deck_select;
154
155 while(s0!=0&&s1!=0){
156 if(deck_select(rnd_gen)<(double)s0/(s0+s1)){
157 *out++=*first++;
158 --s0;
159 }
160 else{
161 *out++=*middle++;
162 --s1;
163 }
164 }
165 std::copy(first,first+s0,out);
166 std::copy(middle,middle+s1,out);
167}
168
169struct riffle_shuffler
170{
171 void operator()(deck& d)const
172 {
173 dv.clear();
174 dv.reserve(d.size());
175 riffle_shuffle(
176 d.begin(),d.end(),std::back_inserter(dv)); /* do the shuffling */
177 d.rearrange(dv.begin()); /* apply to the deck */
178 }
179
180private:
181 mutable deck_view dv;
182};
183
184/* A truly random shuffle (up to stdlib implementation quality) using
185 * std::shuffle.
186 */
187
188struct random_shuffler
189{
190 void operator()(deck& d)
191 {
192 dv.clear();
193 dv.reserve(d.size());
194 std::copy(d.begin(),d.end(),std::back_inserter(dv));
195 shuffle_view();
196 d.rearrange(dv.begin()); /* apply to the deck */
197 }
198
199private:
200 deck_view dv;
201
202#if !defined(BOOST_NO_CXX11_HDR_RANDOM)
203 std::mt19937 e;
204
205 void shuffle_view()
206 {
207 std::shuffle(dv.begin(),dv.end(),e);
208 }
209#else
210 /* for pre-C++11 compilers we use std::random_shuffle */
211
212 void shuffle_view()
213 {
214 std::random_shuffle(dv.begin(),dv.end());
215 }
216#endif
217};
218
219/* Repeat a given shuffling algorithm repeats_num times
220 * and obtain the resulting rising sequences number. Average
221 * for tests_num trials.
222 */
223
224template<typename Shuffler>
225double shuffle_test(
226 unsigned int repeats_num,unsigned int tests_num
227 BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Shuffler))
228{
229 deck d;
230 Shuffler sh;
231 unsigned long total=0;
232
233 for(unsigned int n=0;n<tests_num;++n){
234 for(unsigned m=0;m<repeats_num;++m)sh(d);
235 total+=d.rising_sequences();
236 d.reset();
237 }
238
239 return (double)total/tests_num;
240}
241
242int main()
243{
244 unsigned rifs_num=0;
245 unsigned tests_num=0;
246
247 std::cout<<"number of riffle shuffles (vg 5):";
248 std::cin>>rifs_num;
249 std::cout<<"number of tests (vg 1000):";
250 std::cin>>tests_num;
251
252 std::cout<<"shuffling..."<<std::endl;
253
254 std::cout<<"riffle shuffling\n"
255 " avg number of rising sequences: "
256 <<shuffle_test<riffle_shuffler>(rifs_num,tests_num)
257 <<std::endl;
258
259 std::cout<<"random shuffling\n"
260 " avg number of rising sequences: "
261 <<shuffle_test<random_shuffler>(1,tests_num)
262 <<std::endl;
263
264 return 0;
265}