| 1 | /* statistic_tests.hpp header file | 
|---|
| 2 |  * | 
|---|
| 3 |  * Copyright Jens Maurer 2000 | 
|---|
| 4 |  * Distributed under the Boost Software License, Version 1.0. (See | 
|---|
| 5 |  * accompanying file LICENSE_1_0.txt or copy at | 
|---|
| 6 |  * http://www.boost.org/LICENSE_1_0.txt) | 
|---|
| 7 |  * | 
|---|
| 8 |  * $Id: statistic_tests.hpp,v 1.13 2004/07/27 03:43:34 dgregor Exp $ | 
|---|
| 9 |  * | 
|---|
| 10 |  */ | 
|---|
| 11 |  | 
|---|
| 12 | #ifndef STATISTIC_TESTS_HPP | 
|---|
| 13 | #define STATISTIC_TESTS_HPP | 
|---|
| 14 |  | 
|---|
| 15 | #include <stdexcept> | 
|---|
| 16 | #include <iterator> | 
|---|
| 17 | #include <vector> | 
|---|
| 18 | #include <boost/limits.hpp> | 
|---|
| 19 | #include <algorithm> | 
|---|
| 20 | #include <cmath> | 
|---|
| 21 |  | 
|---|
| 22 | #include <boost/random.hpp> | 
|---|
| 23 | #include <boost/config.hpp> | 
|---|
| 24 |  | 
|---|
| 25 |  | 
|---|
| 26 | #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 | 
|---|
| 27 | namespace std | 
|---|
| 28 | { | 
|---|
| 29 |   inline double pow(double a, double b) { return ::pow(a,b); } | 
|---|
| 30 |   inline double ceil(double x) { return ::ceil(x); } | 
|---|
| 31 | } // namespace std | 
|---|
| 32 | #endif | 
|---|
| 33 |  | 
|---|
| 34 |  | 
|---|
| 35 | template<class T> | 
|---|
| 36 | inline T fac(int k) | 
|---|
| 37 | { | 
|---|
| 38 |   T result = 1; | 
|---|
| 39 |   for(T i = 2; i <= k; ++i) | 
|---|
| 40 |     result *= i; | 
|---|
| 41 |   return result; | 
|---|
| 42 | } | 
|---|
| 43 |  | 
|---|
| 44 | template<class T> | 
|---|
| 45 | T binomial(int n, int k) | 
|---|
| 46 | { | 
|---|
| 47 |   if(k < n/2) | 
|---|
| 48 |     k = n-k; | 
|---|
| 49 |   T result = 1; | 
|---|
| 50 |   for(int i = k+1; i<= n; ++i) | 
|---|
| 51 |     result *= i; | 
|---|
| 52 |   return result / fac<T>(n-k); | 
|---|
| 53 | } | 
|---|
| 54 |  | 
|---|
| 55 | template<class T> | 
|---|
| 56 | T stirling2(int n, int m) | 
|---|
| 57 | { | 
|---|
| 58 |   T sum = 0; | 
|---|
| 59 |   for(int k = 0; k <= m; ++k) | 
|---|
| 60 |     sum += binomial<T>(m, k) * std::pow(double(k), n) * | 
|---|
| 61 |       ( (m-k)%2 == 0 ? 1 : -1); | 
|---|
| 62 |   return sum / fac<T>(m); | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | /* | 
|---|
| 66 |  * Experiments which create an empirical distribution in classes, | 
|---|
| 67 |  * suitable for the chi-square test. | 
|---|
| 68 |  */ | 
|---|
| 69 | // std::floor(gen() * classes) | 
|---|
| 70 |  | 
|---|
| 71 | class experiment_base | 
|---|
| 72 | { | 
|---|
| 73 | public: | 
|---|
| 74 |   experiment_base(int cls) : _classes(cls) { } | 
|---|
| 75 |   unsigned int classes() const { return _classes; } | 
|---|
| 76 | protected: | 
|---|
| 77 |   unsigned int _classes; | 
|---|
| 78 | }; | 
|---|
| 79 |  | 
|---|
| 80 | class equidistribution_experiment : public experiment_base | 
|---|
| 81 | { | 
|---|
| 82 | public: | 
|---|
| 83 |   explicit equidistribution_experiment(unsigned int classes)  | 
|---|
| 84 |     : experiment_base(classes) { } | 
|---|
| 85 |    | 
|---|
| 86 |   template<class NumberGenerator, class Counter> | 
|---|
| 87 |   void run(NumberGenerator f, Counter & count, int n) const | 
|---|
| 88 |   { | 
|---|
| 89 |     assert((f.min)() == 0 && | 
|---|
| 90 |            static_cast<unsigned int>((f.max)()) == classes()-1); | 
|---|
| 91 |     for(int i = 0; i < n; ++i) | 
|---|
| 92 |       count(f()); | 
|---|
| 93 |   } | 
|---|
| 94 |   double probability(int i) const { return 1.0/classes(); } | 
|---|
| 95 | }; | 
|---|
| 96 |  | 
|---|
| 97 | // two-dimensional equidistribution experiment | 
|---|
| 98 | class equidistribution_2d_experiment : public equidistribution_experiment | 
|---|
| 99 | { | 
|---|
| 100 | public: | 
|---|
| 101 |   explicit equidistribution_2d_experiment(unsigned int classes)  | 
|---|
| 102 |     : equidistribution_experiment(classes) { } | 
|---|
| 103 |  | 
|---|
| 104 |   template<class NumberGenerator, class Counter> | 
|---|
| 105 |   void run(NumberGenerator f, Counter & count, int n) const | 
|---|
| 106 |   { | 
|---|
| 107 |     unsigned int range = (f.max)()+1; | 
|---|
| 108 |     assert((f.min)() == 0 && range*range == classes()); | 
|---|
| 109 |     for(int i = 0; i < n; ++i) { | 
|---|
| 110 |       int y1 = f(); | 
|---|
| 111 |       int y2 = f(); | 
|---|
| 112 |       count(y1 + range * y2); | 
|---|
| 113 |     } | 
|---|
| 114 |   } | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | // distribution experiment: assume a probability density and  | 
|---|
| 118 | // count events so that an equidistribution results. | 
|---|
| 119 | class distribution_experiment : public equidistribution_experiment | 
|---|
| 120 | { | 
|---|
| 121 | public: | 
|---|
| 122 |   template<class UnaryFunction> | 
|---|
| 123 |   distribution_experiment(UnaryFunction probability , unsigned int classes) | 
|---|
| 124 |     : equidistribution_experiment(classes), limit(classes) | 
|---|
| 125 |   { | 
|---|
| 126 |     for(unsigned int i = 0; i < classes-1; ++i) | 
|---|
| 127 |       limit[i] = invert_monotone_inc(probability, (i+1)*0.05, 0, 1000); | 
|---|
| 128 |     limit[classes-1] = std::numeric_limits<double>::infinity(); | 
|---|
| 129 |     if(limit[classes-1] < (std::numeric_limits<double>::max)()) | 
|---|
| 130 |       limit[classes-1] = (std::numeric_limits<double>::max)(); | 
|---|
| 131 | #if 0 | 
|---|
| 132 |     std::cout << __PRETTY_FUNCTION__ << ": "; | 
|---|
| 133 |     for(unsigned int i = 0; i < classes; ++i) | 
|---|
| 134 |       std::cout << limit[i] << " "; | 
|---|
| 135 |     std::cout << std::endl; | 
|---|
| 136 | #endif | 
|---|
| 137 |   } | 
|---|
| 138 |  | 
|---|
| 139 |   template<class NumberGenerator, class Counter> | 
|---|
| 140 |   void run(NumberGenerator f, Counter & count, int n) const | 
|---|
| 141 |   { | 
|---|
| 142 |     for(int i = 0; i < n; ++i) { | 
|---|
| 143 |       limits_type::const_iterator it = | 
|---|
| 144 |         std::lower_bound(limit.begin(), limit.end(), f()); | 
|---|
| 145 |       count(it-limit.begin()); | 
|---|
| 146 |     } | 
|---|
| 147 |   } | 
|---|
| 148 | private: | 
|---|
| 149 |   typedef std::vector<double> limits_type; | 
|---|
| 150 |   limits_type limit; | 
|---|
| 151 | }; | 
|---|
| 152 |  | 
|---|
| 153 | // runs-up/runs-down experiment | 
|---|
| 154 | template<bool up> | 
|---|
| 155 | class runs_experiment : public experiment_base | 
|---|
| 156 | { | 
|---|
| 157 | public: | 
|---|
| 158 |   explicit runs_experiment(unsigned int classes) : experiment_base(classes) { } | 
|---|
| 159 |    | 
|---|
| 160 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 161 |   void run(UniformRandomNumberGenerator f, Counter & count, int n) const | 
|---|
| 162 |   { | 
|---|
| 163 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 164 |     result_type init = (up ? (f.min)() : (f.max)()); | 
|---|
| 165 |     result_type previous = init; | 
|---|
| 166 |     unsigned int length = 0; | 
|---|
| 167 |     for(int i = 0; i < n; ++i) { | 
|---|
| 168 |       result_type val = f(); | 
|---|
| 169 |       if(up ? previous <= val : previous >= val) { | 
|---|
| 170 |         previous = val; | 
|---|
| 171 |         ++length; | 
|---|
| 172 |       } else { | 
|---|
| 173 |         count((std::min)(length, classes())-1); | 
|---|
| 174 |         length = 0; | 
|---|
| 175 |         previous = init; | 
|---|
| 176 |         // don't use this value, so that runs are independent | 
|---|
| 177 |       } | 
|---|
| 178 |     } | 
|---|
| 179 |   } | 
|---|
| 180 |   double probability(unsigned int r) const | 
|---|
| 181 |   { | 
|---|
| 182 |     if(r == classes()-1) | 
|---|
| 183 |       return 1.0/fac<double>(classes()); | 
|---|
| 184 |     else | 
|---|
| 185 |       return static_cast<double>(r+1)/fac<double>(r+2); | 
|---|
| 186 |   } | 
|---|
| 187 | }; | 
|---|
| 188 |  | 
|---|
| 189 | // gap length experiment | 
|---|
| 190 | class gap_experiment : public experiment_base | 
|---|
| 191 | { | 
|---|
| 192 | public: | 
|---|
| 193 |   gap_experiment(unsigned int classes, double alpha, double beta) | 
|---|
| 194 |     : experiment_base(classes), alpha(alpha), beta(beta) { } | 
|---|
| 195 |    | 
|---|
| 196 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 197 |   void run(UniformRandomNumberGenerator f, Counter & count, int n) const | 
|---|
| 198 |   { | 
|---|
| 199 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 200 |     double range = (f.max)() - (f.min)() + 1.0; | 
|---|
| 201 |     result_type low = static_cast<result_type>(alpha * range); | 
|---|
| 202 |     result_type high = static_cast<result_type>(beta * range); | 
|---|
| 203 |     unsigned int length = 0; | 
|---|
| 204 |     for(int i = 0; i < n; ) { | 
|---|
| 205 |       result_type value = f() - (f.min)(); | 
|---|
| 206 |       if(value < low || value > high) | 
|---|
| 207 |         ++length; | 
|---|
| 208 |       else { | 
|---|
| 209 |         count((std::min)(length, classes()-1)); | 
|---|
| 210 |         length = 0; | 
|---|
| 211 |         ++i; | 
|---|
| 212 |       } | 
|---|
| 213 |     } | 
|---|
| 214 |   } | 
|---|
| 215 |   double probability(unsigned int r) const | 
|---|
| 216 |   { | 
|---|
| 217 |     double p = beta-alpha; | 
|---|
| 218 |     if(r == classes()-1) | 
|---|
| 219 |       return std::pow(1-p, static_cast<double>(r)); | 
|---|
| 220 |     else | 
|---|
| 221 |       return p * std::pow(1-p, static_cast<double>(r)); | 
|---|
| 222 |   } | 
|---|
| 223 | private: | 
|---|
| 224 |   double alpha, beta; | 
|---|
| 225 | }; | 
|---|
| 226 |  | 
|---|
| 227 | // poker experiment | 
|---|
| 228 | class poker_experiment : public experiment_base | 
|---|
| 229 | { | 
|---|
| 230 | public: | 
|---|
| 231 |   poker_experiment(unsigned int d, unsigned int k) | 
|---|
| 232 |     : experiment_base(k), range(d) | 
|---|
| 233 |   { | 
|---|
| 234 |     assert(range > 1); | 
|---|
| 235 |   } | 
|---|
| 236 |  | 
|---|
| 237 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 238 |   void run(UniformRandomNumberGenerator f, Counter & count, int n) const | 
|---|
| 239 |   { | 
|---|
| 240 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 241 |     assert(std::numeric_limits<result_type>::is_integer); | 
|---|
| 242 |     assert((f.min)() == 0); | 
|---|
| 243 |     assert((f.max)() == static_cast<result_type>(range-1)); | 
|---|
| 244 |     std::vector<result_type> v(classes()); | 
|---|
| 245 |     for(int i = 0; i < n; ++i) { | 
|---|
| 246 |       for(unsigned int j = 0; j < classes(); ++j) | 
|---|
| 247 |         v[j] = f(); | 
|---|
| 248 |       std::sort(v.begin(), v.end()); | 
|---|
| 249 |       result_type prev = v[0]; | 
|---|
| 250 |       int r = 1;     // count different values in v | 
|---|
| 251 |       for(unsigned int i = 1; i < classes(); ++i) { | 
|---|
| 252 |         if(prev != v[i]) { | 
|---|
| 253 |           prev = v[i]; | 
|---|
| 254 |           ++r; | 
|---|
| 255 |         } | 
|---|
| 256 |       } | 
|---|
| 257 |       count(r-1); | 
|---|
| 258 |     } | 
|---|
| 259 |   } | 
|---|
| 260 |  | 
|---|
| 261 |   double probability(unsigned int r) const | 
|---|
| 262 |   { | 
|---|
| 263 |     ++r;       // transform to 1 <= r <= 5 | 
|---|
| 264 |     double result = range; | 
|---|
| 265 |     for(unsigned int i = 1; i < r; ++i) | 
|---|
| 266 |       result *= range-i; | 
|---|
| 267 |     return result / std::pow(range, static_cast<double>(classes())) * | 
|---|
| 268 |       stirling2<double>(classes(), r); | 
|---|
| 269 |   } | 
|---|
| 270 | private: | 
|---|
| 271 |   unsigned int range; | 
|---|
| 272 | }; | 
|---|
| 273 |  | 
|---|
| 274 | // coupon collector experiment | 
|---|
| 275 | class coupon_collector_experiment : public experiment_base | 
|---|
| 276 | { | 
|---|
| 277 | public: | 
|---|
| 278 |   coupon_collector_experiment(unsigned int d, unsigned int cls) | 
|---|
| 279 |     : experiment_base(cls), d(d) | 
|---|
| 280 |   { | 
|---|
| 281 |     assert(d > 1); | 
|---|
| 282 |   } | 
|---|
| 283 |  | 
|---|
| 284 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 285 |   void run(UniformRandomNumberGenerator f, Counter & count, int n) const | 
|---|
| 286 |   { | 
|---|
| 287 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 288 |     assert(std::numeric_limits<result_type>::is_integer); | 
|---|
| 289 |     assert((f.min)() == 0); | 
|---|
| 290 |     assert((f.max)() == static_cast<result_type>(d-1)); | 
|---|
| 291 |     std::vector<bool> occurs(d); | 
|---|
| 292 |     for(int i = 0; i < n; ++i) { | 
|---|
| 293 |       occurs.assign(d, false); | 
|---|
| 294 |       unsigned int r = 0;            // length of current sequence | 
|---|
| 295 |       int q = 0;                     // number of non-duplicates in current set | 
|---|
| 296 |       for(;;) { | 
|---|
| 297 |         result_type val = f(); | 
|---|
| 298 |         ++r; | 
|---|
| 299 |         if(!occurs[val]) {       // new set element | 
|---|
| 300 |           occurs[val] = true; | 
|---|
| 301 |           ++q; | 
|---|
| 302 |           if(q == d) | 
|---|
| 303 |             break;     // one complete set | 
|---|
| 304 |         } | 
|---|
| 305 |       } | 
|---|
| 306 |       count((std::min)(r-d, classes()-1)); | 
|---|
| 307 |     } | 
|---|
| 308 |   } | 
|---|
| 309 |   double probability(unsigned int r) const | 
|---|
| 310 |   { | 
|---|
| 311 |     if(r == classes()-1) | 
|---|
| 312 |       return 1-fac<double>(d)/std::pow(d, static_cast<double>(d+classes()-2))*  | 
|---|
| 313 |         stirling2<double>(d+classes()-2, d); | 
|---|
| 314 |     else | 
|---|
| 315 |       return fac<double>(d)/std::pow(d, static_cast<double>(d+r)) *  | 
|---|
| 316 |         stirling2<double>(d+r-1, d-1); | 
|---|
| 317 |   } | 
|---|
| 318 | private: | 
|---|
| 319 |   int d; | 
|---|
| 320 | }; | 
|---|
| 321 |  | 
|---|
| 322 | // permutation test | 
|---|
| 323 | class permutation_experiment : public equidistribution_experiment | 
|---|
| 324 | { | 
|---|
| 325 | public: | 
|---|
| 326 |   permutation_experiment(unsigned int t) | 
|---|
| 327 |     : equidistribution_experiment(fac<int>(t)), t(t) | 
|---|
| 328 |   { | 
|---|
| 329 |     assert(t > 1); | 
|---|
| 330 |   } | 
|---|
| 331 |  | 
|---|
| 332 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 333 |   void run(UniformRandomNumberGenerator f, Counter & count, int n) const | 
|---|
| 334 |   { | 
|---|
| 335 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 336 |     std::vector<result_type> v(t); | 
|---|
| 337 |     for(int i = 0; i < n; ++i) { | 
|---|
| 338 |       std::generate_n(v.begin(), t, f); | 
|---|
| 339 |       int x = 0; | 
|---|
| 340 |       for(int r = t-1; r > 0; r--) { | 
|---|
| 341 |         typename std::vector<result_type>::iterator it =  | 
|---|
| 342 |           std::max_element(v.begin(), v.begin()+r+1); | 
|---|
| 343 |         x = (r+1)*x + (it-v.begin()); | 
|---|
| 344 |         std::iter_swap(it, v.begin()+r); | 
|---|
| 345 |       } | 
|---|
| 346 |       count(x); | 
|---|
| 347 |     } | 
|---|
| 348 |   } | 
|---|
| 349 | private: | 
|---|
| 350 |   int t; | 
|---|
| 351 | }; | 
|---|
| 352 |  | 
|---|
| 353 | // birthday spacing experiment test | 
|---|
| 354 | class birthday_spacing_experiment : public experiment_base | 
|---|
| 355 | { | 
|---|
| 356 | public: | 
|---|
| 357 |   birthday_spacing_experiment(unsigned int d, int n, int m) | 
|---|
| 358 |     : experiment_base(d), n(n), m(m) | 
|---|
| 359 |   { | 
|---|
| 360 |   } | 
|---|
| 361 |  | 
|---|
| 362 |   template<class UniformRandomNumberGenerator, class Counter> | 
|---|
| 363 |   void run(UniformRandomNumberGenerator f, Counter & count, int n_total) const | 
|---|
| 364 |   { | 
|---|
| 365 |     typedef typename UniformRandomNumberGenerator::result_type result_type; | 
|---|
| 366 |     assert(std::numeric_limits<result_type>::is_integer); | 
|---|
| 367 |     assert((f.min)() == 0); | 
|---|
| 368 |     assert((f.max)() == static_cast<result_type>(m-1)); | 
|---|
| 369 |     | 
|---|
| 370 |     for(int j = 0; j < n_total; j++) { | 
|---|
| 371 |       std::vector<result_type> v(n); | 
|---|
| 372 |       std::generate_n(v.begin(), n, f); | 
|---|
| 373 |       std::sort(v.begin(), v.end()); | 
|---|
| 374 |       std::vector<result_type> spacing(n); | 
|---|
| 375 |       for(int i = 0; i < n-1; i++) | 
|---|
| 376 |         spacing[i] = v[i+1]-v[i]; | 
|---|
| 377 |       spacing[n-1] = v[0] + m - v[n-1]; | 
|---|
| 378 |       std::sort(spacing.begin(), spacing.end()); | 
|---|
| 379 |       unsigned int k = 0; | 
|---|
| 380 |       for(int i = 0; i < n-1; ++i) { | 
|---|
| 381 |         if(spacing[i] == spacing[i+1]) | 
|---|
| 382 |           ++k; | 
|---|
| 383 |       } | 
|---|
| 384 |       count((std::min)(k, classes()-1)); | 
|---|
| 385 |     } | 
|---|
| 386 |   } | 
|---|
| 387 |  | 
|---|
| 388 |   double probability(unsigned int r) const | 
|---|
| 389 |   { | 
|---|
| 390 |     assert(classes() == 4); | 
|---|
| 391 |     assert(m == (1<<25)); | 
|---|
| 392 |     assert(n == 512); | 
|---|
| 393 |     static const double prob[] = { 0.368801577, 0.369035243, 0.183471182, | 
|---|
| 394 |                                    0.078691997 }; | 
|---|
| 395 |     return prob[r]; | 
|---|
| 396 |   } | 
|---|
| 397 | private: | 
|---|
| 398 |   int n, m; | 
|---|
| 399 | }; | 
|---|
| 400 | /* | 
|---|
| 401 |  * Misc. helper functions. | 
|---|
| 402 |  */ | 
|---|
| 403 |  | 
|---|
| 404 | template<class Float> | 
|---|
| 405 | struct distribution_function | 
|---|
| 406 | { | 
|---|
| 407 |   typedef Float result_type; | 
|---|
| 408 |   typedef Float argument_type; | 
|---|
| 409 |   typedef Float first_argument_type; | 
|---|
| 410 |   typedef Float second_argument_type; | 
|---|
| 411 | }; | 
|---|
| 412 |  | 
|---|
| 413 | // computes P(K_n <= t) or P(t1 <= K_n <= t2).  See Knuth, 3.3.1 | 
|---|
| 414 | class kolmogorov_smirnov_probability : public distribution_function<double> | 
|---|
| 415 | { | 
|---|
| 416 | public: | 
|---|
| 417 |   kolmogorov_smirnov_probability(int n)  | 
|---|
| 418 |     : approx(n > 50), n(n), sqrt_n(std::sqrt(double(n))) | 
|---|
| 419 |   { | 
|---|
| 420 |     if(!approx) | 
|---|
| 421 |       n_n = std::pow(static_cast<double>(n), n); | 
|---|
| 422 |   } | 
|---|
| 423 |    | 
|---|
| 424 |   double operator()(double t) const | 
|---|
| 425 |   { | 
|---|
| 426 |     if(approx) { | 
|---|
| 427 |       return 1-std::exp(-2*t*t)*(1-2.0/3.0*t/sqrt_n); | 
|---|
| 428 |     } else { | 
|---|
| 429 |       t *= sqrt_n; | 
|---|
| 430 |       double sum = 0; | 
|---|
| 431 |       for(int k = static_cast<int>(std::ceil(t)); k <= n; k++) | 
|---|
| 432 |         sum += binomial<double>(n, k) * std::pow(k-t, k) *  | 
|---|
| 433 |           std::pow(t+n-k, n-k-1); | 
|---|
| 434 |       return 1 - t/n_n * sum; | 
|---|
| 435 |     } | 
|---|
| 436 |   } | 
|---|
| 437 |   double operator()(double t1, double t2) const | 
|---|
| 438 |   { return operator()(t2) - operator()(t1); } | 
|---|
| 439 |  | 
|---|
| 440 | private: | 
|---|
| 441 |   bool approx; | 
|---|
| 442 |   int n; | 
|---|
| 443 |   double sqrt_n; | 
|---|
| 444 |   double n_n; | 
|---|
| 445 | }; | 
|---|
| 446 |  | 
|---|
| 447 | /* | 
|---|
| 448 |  * Experiments for generators with continuous distribution functions | 
|---|
| 449 |  */ | 
|---|
| 450 | class kolmogorov_experiment | 
|---|
| 451 | { | 
|---|
| 452 | public: | 
|---|
| 453 |   kolmogorov_experiment(int n) : n(n), ksp(n) { } | 
|---|
| 454 |   template<class NumberGenerator, class Distribution> | 
|---|
| 455 |   double run(NumberGenerator gen, Distribution distrib) const | 
|---|
| 456 |   { | 
|---|
| 457 |     const int m = n; | 
|---|
| 458 |     typedef std::vector<double> saved_temp; | 
|---|
| 459 |     saved_temp a(m,1.0), b(m,0); | 
|---|
| 460 |     std::vector<int> c(m,0); | 
|---|
| 461 |     for(int i = 0; i < n; ++i) { | 
|---|
| 462 |       double val = gen(); | 
|---|
| 463 |       double y = distrib(val); | 
|---|
| 464 |       int k = static_cast<int>(std::floor(m*y)); | 
|---|
| 465 |       if(k >= m) | 
|---|
| 466 |         --k;    // should not happen | 
|---|
| 467 |       a[k] = (std::min)(a[k], y); | 
|---|
| 468 |       b[k] = (std::max)(b[k], y); | 
|---|
| 469 |       ++c[k]; | 
|---|
| 470 |     } | 
|---|
| 471 |     double kplus = 0, kminus = 0; | 
|---|
| 472 |     int j = 0; | 
|---|
| 473 |     for(int k = 0; k < m; ++k) { | 
|---|
| 474 |       if(c[k] > 0) { | 
|---|
| 475 |         kminus = (std::max)(kminus, a[k]-j/static_cast<double>(n)); | 
|---|
| 476 |         j += c[k]; | 
|---|
| 477 |         kplus = (std::max)(kplus, j/static_cast<double>(n) - b[k]); | 
|---|
| 478 |       } | 
|---|
| 479 |     } | 
|---|
| 480 |     kplus *= std::sqrt(double(n)); | 
|---|
| 481 |     kminus *= std::sqrt(double(n)); | 
|---|
| 482 |     // std::cout << "k+ " << kplus << "   k- " << kminus << std::endl; | 
|---|
| 483 |     return kplus; | 
|---|
| 484 |   } | 
|---|
| 485 |   double probability(double x) const | 
|---|
| 486 |   { | 
|---|
| 487 |     return ksp(x); | 
|---|
| 488 |   } | 
|---|
| 489 | private: | 
|---|
| 490 |   int n; | 
|---|
| 491 |   kolmogorov_smirnov_probability ksp; | 
|---|
| 492 | }; | 
|---|
| 493 |  | 
|---|
| 494 | // maximum-of-t test (KS-based) | 
|---|
| 495 | template<class UniformRandomNumberGenerator> | 
|---|
| 496 | class maximum_experiment | 
|---|
| 497 | { | 
|---|
| 498 | public: | 
|---|
| 499 |   typedef UniformRandomNumberGenerator base_type; | 
|---|
| 500 |   maximum_experiment(base_type & f, int n, int t) : f(f), ke(n), t(t) | 
|---|
| 501 |   { } | 
|---|
| 502 |  | 
|---|
| 503 |   double operator()() const | 
|---|
| 504 |   { | 
|---|
| 505 |     double res = ke.run(generator(f, t),  | 
|---|
| 506 |                   std::bind2nd(std::ptr_fun(static_cast<double (*)(double, double)>(&std::pow)), t)); | 
|---|
| 507 |     return res; | 
|---|
| 508 |   } | 
|---|
| 509 |  | 
|---|
| 510 | private: | 
|---|
| 511 |   struct generator { | 
|---|
| 512 |     generator(base_type & f, int t) : f(f), t(t) { } | 
|---|
| 513 |     double operator()() | 
|---|
| 514 |     { | 
|---|
| 515 |       double mx = f(); | 
|---|
| 516 |       for(int i = 1; i < t; ++i) | 
|---|
| 517 |         mx = (std::max)(mx, f()); | 
|---|
| 518 |       return mx; | 
|---|
| 519 |     } | 
|---|
| 520 |   private: | 
|---|
| 521 |     boost::uniform_01<base_type> f; | 
|---|
| 522 |     int t; | 
|---|
| 523 |   }; | 
|---|
| 524 |   base_type & f; | 
|---|
| 525 |   kolmogorov_experiment ke; | 
|---|
| 526 |   int t; | 
|---|
| 527 | }; | 
|---|
| 528 |  | 
|---|
| 529 | // compute a chi-square value for the distribution approximation error | 
|---|
| 530 | template<class ForwardIterator, class UnaryFunction> | 
|---|
| 531 | typename UnaryFunction::result_type | 
|---|
| 532 | chi_square_value(ForwardIterator first, ForwardIterator last, | 
|---|
| 533 |                  UnaryFunction probability) | 
|---|
| 534 | { | 
|---|
| 535 |   typedef std::iterator_traits<ForwardIterator> iter_traits; | 
|---|
| 536 |   typedef typename iter_traits::value_type counter_type; | 
|---|
| 537 |   typedef typename UnaryFunction::result_type result_type; | 
|---|
| 538 |   unsigned int classes = std::distance(first, last); | 
|---|
| 539 |   result_type sum = 0; | 
|---|
| 540 |   counter_type n = 0; | 
|---|
| 541 |   for(unsigned int i = 0; i < classes; ++first, ++i) { | 
|---|
| 542 |     counter_type count = *first; | 
|---|
| 543 |     n += count; | 
|---|
| 544 |     sum += (count/probability(i)) * count;  // avoid overflow | 
|---|
| 545 |   } | 
|---|
| 546 | #if 0 | 
|---|
| 547 |   for(unsigned int i = 0; i < classes; ++i) { | 
|---|
| 548 |     // std::cout << (n*probability(i)) << " "; | 
|---|
| 549 |     if(n * probability(i) < 5) | 
|---|
| 550 |       std::cerr << "Not enough test runs for slot " << i | 
|---|
| 551 |                 << " p=" << probability(i) << ", n=" << n | 
|---|
| 552 |                 << std::endl; | 
|---|
| 553 |   } | 
|---|
| 554 | #endif | 
|---|
| 555 |   // std::cout << std::endl; | 
|---|
| 556 |   // throw std::invalid_argument("not enough test runs"); | 
|---|
| 557 |  | 
|---|
| 558 |   return sum/n - n; | 
|---|
| 559 | } | 
|---|
| 560 | template<class RandomAccessContainer> | 
|---|
| 561 | class generic_counter | 
|---|
| 562 | { | 
|---|
| 563 | public: | 
|---|
| 564 |   explicit generic_counter(unsigned int classes) : container(classes, 0) { } | 
|---|
| 565 |   void operator()(int i) | 
|---|
| 566 |   { | 
|---|
| 567 |     assert(i >= 0); | 
|---|
| 568 |     assert(static_cast<unsigned int>(i) < container.size()); | 
|---|
| 569 |     ++container[i]; | 
|---|
| 570 |   } | 
|---|
| 571 |   typename RandomAccessContainer::const_iterator begin() const  | 
|---|
| 572 |   { return container.begin(); } | 
|---|
| 573 |   typename RandomAccessContainer::const_iterator end() const  | 
|---|
| 574 |   { return container.end(); } | 
|---|
| 575 |  | 
|---|
| 576 | private: | 
|---|
| 577 |   RandomAccessContainer container; | 
|---|
| 578 | }; | 
|---|
| 579 |  | 
|---|
| 580 | // chi_square test | 
|---|
| 581 | template<class Experiment, class Generator> | 
|---|
| 582 | double run_experiment(const Experiment & experiment, Generator gen, int n) | 
|---|
| 583 | { | 
|---|
| 584 |   generic_counter<std::vector<int> > v(experiment.classes()); | 
|---|
| 585 |   experiment.run(gen, v, n); | 
|---|
| 586 |   return chi_square_value(v.begin(), v.end(), | 
|---|
| 587 |                           std::bind1st(std::mem_fun_ref(&Experiment::probability),  | 
|---|
| 588 |                                        experiment)); | 
|---|
| 589 | } | 
|---|
| 590 |  | 
|---|
| 591 | // number generator with experiment results (for nesting) | 
|---|
| 592 | template<class Experiment, class Generator> | 
|---|
| 593 | class experiment_generator_t | 
|---|
| 594 | { | 
|---|
| 595 | public: | 
|---|
| 596 |   experiment_generator_t(const Experiment & exper, Generator & gen, int n) | 
|---|
| 597 |     : experiment(exper), generator(gen), n(n) { } | 
|---|
| 598 |   double operator()() { return run_experiment(experiment, generator, n); } | 
|---|
| 599 | private: | 
|---|
| 600 |   const Experiment & experiment; | 
|---|
| 601 |   Generator & generator; | 
|---|
| 602 |   int n; | 
|---|
| 603 | }; | 
|---|
| 604 |  | 
|---|
| 605 | template<class Experiment, class Generator> | 
|---|
| 606 | experiment_generator_t<Experiment, Generator> | 
|---|
| 607 | experiment_generator(const Experiment & e, Generator & gen, int n) | 
|---|
| 608 | { | 
|---|
| 609 |   return experiment_generator_t<Experiment, Generator>(e, gen, n); | 
|---|
| 610 | } | 
|---|
| 611 |  | 
|---|
| 612 |  | 
|---|
| 613 | template<class Experiment, class Generator, class Distribution> | 
|---|
| 614 | class ks_experiment_generator_t | 
|---|
| 615 | { | 
|---|
| 616 | public: | 
|---|
| 617 |   ks_experiment_generator_t(const Experiment & exper, Generator & gen, | 
|---|
| 618 |                             const Distribution & distrib) | 
|---|
| 619 |     : experiment(exper), generator(gen), distribution(distrib) { } | 
|---|
| 620 |   double operator()() { return experiment.run(generator, distribution); } | 
|---|
| 621 | private: | 
|---|
| 622 |   const Experiment & experiment; | 
|---|
| 623 |   Generator & generator; | 
|---|
| 624 |   Distribution distribution; | 
|---|
| 625 | }; | 
|---|
| 626 |  | 
|---|
| 627 | template<class Experiment, class Generator, class Distribution> | 
|---|
| 628 | ks_experiment_generator_t<Experiment, Generator, Distribution> | 
|---|
| 629 | ks_experiment_generator(const Experiment & e, Generator & gen, | 
|---|
| 630 |                         const Distribution & distrib) | 
|---|
| 631 | { | 
|---|
| 632 |   return ks_experiment_generator_t<Experiment, Generator, Distribution> | 
|---|
| 633 |     (e, gen, distrib); | 
|---|
| 634 | } | 
|---|
| 635 |  | 
|---|
| 636 |  | 
|---|
| 637 | #endif /* STATISTIC_TESTS_HPP */ | 
|---|
| 638 |  | 
|---|