@@ -450,6 +450,113 @@ func TestReadSeedReset(t *testing.T) {
450
450
}
451
451
}
452
452
453
+ func TestShuffleSmall (t * testing.T ) {
454
+ // Check that Shuffle allows n=0 and n=1, but that swap is never called for them.
455
+ r := New (NewSource (1 ))
456
+ for n := 0 ; n <= 1 ; n ++ {
457
+ r .Shuffle (n , func (i , j int ) { t .Fatalf ("swap called, n=%d i=%d j=%d" , n , i , j ) })
458
+ }
459
+ }
460
+
461
+ // encodePerm converts from a permuted slice of length n, such as Perm generates, to an int in [0, n!).
462
+ // See https://en.wikipedia.org/wiki/Lehmer_code.
463
+ // encodePerm modifies the input slice.
464
+ func encodePerm (s []int ) int {
465
+ // Convert to Lehmer code.
466
+ for i , x := range s {
467
+ r := s [i + 1 :]
468
+ for j , y := range r {
469
+ if y > x {
470
+ r [j ]--
471
+ }
472
+ }
473
+ }
474
+ // Convert to int in [0, n!).
475
+ m := 0
476
+ fact := 1
477
+ for i := len (s ) - 1 ; i >= 0 ; i -- {
478
+ m += s [i ] * fact
479
+ fact *= len (s ) - i
480
+ }
481
+ return m
482
+ }
483
+
484
+ // TestUniformFactorial tests several ways of generating a uniform value in [0, n!).
485
+ func TestUniformFactorial (t * testing.T ) {
486
+ r := New (NewSource (testSeeds [0 ]))
487
+ top := 6
488
+ if testing .Short () {
489
+ top = 4
490
+ }
491
+ for n := 3 ; n <= top ; n ++ {
492
+ t .Run (fmt .Sprintf ("n=%d" , n ), func (t * testing.T ) {
493
+ // Calculate n!.
494
+ nfact := 1
495
+ for i := 2 ; i <= n ; i ++ {
496
+ nfact *= i
497
+ }
498
+
499
+ // Test a few different ways to generate a uniform distribution.
500
+ p := make ([]int , n ) // re-usable slice for Shuffle generator
501
+ tests := [... ]struct {
502
+ name string
503
+ fn func () int
504
+ }{
505
+ {name : "Int31n" , fn : func () int { return int (r .Int31n (int32 (nfact ))) }},
506
+ {name : "int31n" , fn : func () int { return int (r .int31n (int32 (nfact ))) }},
507
+ {name : "Perm" , fn : func () int { return encodePerm (r .Perm (n )) }},
508
+ {name : "Shuffle" , fn : func () int {
509
+ // Generate permutation using Shuffle.
510
+ for i := range p {
511
+ p [i ] = i
512
+ }
513
+ r .Shuffle (n , func (i , j int ) { p [i ], p [j ] = p [j ], p [i ] })
514
+ return encodePerm (p )
515
+ }},
516
+ }
517
+
518
+ for _ , test := range tests {
519
+ t .Run (test .name , func (t * testing.T ) {
520
+ // Gather chi-squared values and check that they follow
521
+ // the expected normal distribution given n!-1 degrees of freedom.
522
+ // See https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test and
523
+ // https://www.johndcook.com/Beautiful_Testing_ch10.pdf.
524
+ nsamples := 10 * nfact
525
+ if nsamples < 200 {
526
+ nsamples = 200
527
+ }
528
+ samples := make ([]float64 , nsamples )
529
+ for i := range samples {
530
+ // Generate some uniformly distributed values and count their occurrences.
531
+ const iters = 1000
532
+ counts := make ([]int , nfact )
533
+ for i := 0 ; i < iters ; i ++ {
534
+ counts [test .fn ()]++
535
+ }
536
+ // Calculate chi-squared and add to samples.
537
+ want := iters / float64 (nfact )
538
+ var χ2 float64
539
+ for _ , have := range counts {
540
+ err := float64 (have ) - want
541
+ χ2 += err * err
542
+ }
543
+ χ2 /= want
544
+ samples [i ] = χ2
545
+ }
546
+
547
+ // Check that our samples approximate the appropriate normal distribution.
548
+ dof := float64 (nfact - 1 )
549
+ expected := & statsResults {mean : dof , stddev : math .Sqrt (2 * dof )}
550
+ errorScale := max (1.0 , expected .stddev )
551
+ expected .closeEnough = 0.10 * errorScale
552
+ expected .maxError = 0.08 // TODO: What is the right value here? See issue 21211.
553
+ checkSampleDistribution (t , samples , expected )
554
+ })
555
+ }
556
+ })
557
+ }
558
+ }
559
+
453
560
// Benchmarks
454
561
455
562
func BenchmarkInt63Threadsafe (b * testing.B ) {
@@ -514,6 +621,30 @@ func BenchmarkPerm30(b *testing.B) {
514
621
}
515
622
}
516
623
624
+ func BenchmarkPerm30ViaShuffle (b * testing.B ) {
625
+ r := New (NewSource (1 ))
626
+ for n := b .N ; n > 0 ; n -- {
627
+ p := make ([]int , 30 )
628
+ for i := range p {
629
+ p [i ] = i
630
+ }
631
+ r .Shuffle (30 , func (i , j int ) { p [i ], p [j ] = p [j ], p [i ] })
632
+ }
633
+ }
634
+
635
+ // BenchmarkShuffleOverhead uses a minimal swap function
636
+ // to measure just the shuffling overhead.
637
+ func BenchmarkShuffleOverhead (b * testing.B ) {
638
+ r := New (NewSource (1 ))
639
+ for n := b .N ; n > 0 ; n -- {
640
+ r .Shuffle (52 , func (i , j int ) {
641
+ if i < 0 || i >= 52 || j < 0 || j >= 52 {
642
+ b .Fatalf ("bad swap(%d, %d)" , i , j )
643
+ }
644
+ })
645
+ }
646
+ }
647
+
517
648
func BenchmarkRead3 (b * testing.B ) {
518
649
r := New (NewSource (1 ))
519
650
buf := make ([]byte , 3 )
0 commit comments