On Sat, May 14, 2022 at 05:03:17PM -0700, Philip Guenther wrote:

> On Sun, 15 May 2022, Steffen Nurpmeso wrote:
> > Stuart Henderson wrote in
> ...
> >  |what's the perceived problem you're wanting to solve? and does that
> >  |problem actually exist in the first place?
> > 
> > The problem is that if have a low upper bound then modulo will "remove a 
> > lot of randomization".  For example if you have a program which 
> > generates Lotto numbers (1..49), then using _uniform() as it is will 
> > generate many duplicates.
> 
> Wut.  The *WHOLE POINT* of arc4random_uniform() is that it has uniform 
> distribution.  Says right so in the manpage.  If an implementation of that 
> API fails to do that, it's a broken implementation.
> 
> The OpenBSD implementation achieves that.  NetBSD's implementation has the 
> same core logic.  Here's my quick lotto pick demo program, doing 4900000 
> picks, so they should all be somewhere near 100000:
> 
> #include <stdlib.h>
> #include <stdio.h>
> #define LIMIT   49
> int
> main(void)
> {
>         unsigned counts[LIMIT] = { 0 };
>         int i;
>         for (i = 0; i < 100000 * LIMIT; i++)
>                 counts[arc4random_uniform(LIMIT)]++;
>         for (i = 0; i < LIMIT; i++)
>                 printf("%2d\t%7u\n", i+1, counts[i]);
>         return 0;
> }
> 
> And a sample run:
> 
> : bleys; ./a.out                                                              
>                                          
>  1       100100
>  2       100639
>  3        99965
>  4        99729
>  5        99641
>  6        99650
>  7       100299
>  8       100164
>  9        99791
> 10       100101
> 11       100657
> 12       100042
> 13        99661
> 14        99927
> 15        99426
> 16        99491
> 17        99646
> 18       100133
> 19       100013
> 20        99942
> 21        99873
> 22        99924
> 23        99567
> 24       100152
> 25       100688
> 26       100011
> 27       100481
> 28        99980
> 29       100406
> 30        99726
> 31        99808
> 32        99929
> 33       100050
> 34        99983
> 35       100048
> 36        99771
> 37        99906
> 38       100215
> 39       100261
> 40       100426
> 41        99847
> 42        99533
> 43       100368
> 44        99695
> 45       100041
> 46       100465
> 47        99875
> 48       100034
> 49        99920
> : bleys; 
> 
> Looks pretty good to me, with repeated runs showing the values bouncing 
> around.
> 
> 
> ...
> > I was a bit interested when i saw Luke's message, but i am no
> > mathematician and, like i said, i in fact never needed the
> > functionality.  And the question i would have is how small
> > upper_bound has to be for the modulo problem to really kick in.
> 
> If the implementation isn't broken, it's not a problem, period.
> 
> 
> > And even if, whether it matters.  For example, if you have
> > a bitset of 1024 "in-flight things", and the small upper bound
> > hits a used slot, you could simply search forward until you find
> > a free one.  I mean, with 1024 possible values the number of
> > possibilities is anyhow restricted.
> 
> Well hey, let's give Luke's a try and see how uniform it is:
> 
> 
> #include <stdlib.h>
> #include <stdio.h>
> #define LIMIT   49
> int
> main(void)
> {
>         unsigned counts[LIMIT] = { 0 };
>         unsigned counts2[LIMIT] = { 0 };
>         int i;
>         for (i = 0; i < 100000 * LIMIT; i++) {
>                 counts[arc4random_uniform(LIMIT)]++;
>                 counts2[arc4random_uniform_fast_simple(LIMIT)]++;
>         }
>         for (i = 0; i < LIMIT; i++)
>                 printf("%2d\t%7u\t%7u\n", i+1, counts[i], counts2[i]);
>         return 0;
> }
> 
> : bleys; ./a.out  
>  1       100188   76502
>  2        99983   76602
>  3       100521   76522
>  4       100416   76682
>  5       100171   76387
>  6       100163   76759
>  7       100024   76336
>  8       100009   76703
>  9        99769   76237
> 10        99892   76532
> 11       100197   76730
> 12       100483   76398
> 13        99769   76310
> 14       100075   76474
> 15        99781   76599
> 16        99846   76439
> 17        99814   76430
> 18       100313   76648
> 19       100259   76813
> 20        99885   77068
> 21       100302   76546
> 22        99998   76698
> 23        99491   76678
> 24       100340   76324
> 25        99763  115263
> 26        99872  153008
> 27       100022  152979
> 28        99481  153793
> 29       100018  210714
> 30        99617  229286
> 31       100167  297003
> 32       100270  449664
> 33       100468   76790
> 34        99115   76452
> 35        99921   76392
> 36        99862   76140
> 37       100485   76607
> 38       100029   75885
> 39        99577   76498
> 40        99479   76727
> 41       100139   76746
> 42       100883   76698
> 43       100102   76474
> 44        99801   76592
> 45       100117   76124
> 46        99678   76417
> 47        99770   76639
> 48        99524   77034
> 49       100151   76658
> : bleys; 
> 
> Wow, that last column is *bad*.  Repeated runs show 25-32 are consistently 
> high, 32 being *outrageously* off, and the others are low.
> 
> Luke's implementation does not correctly implement the API.  Doesn't 
> matter if it's a million times faster when it doesn't deliver the goal.
> 
> 
> Philip Guenther
> 

In my first reply I already said:

> I see a lot of code with no demonstration, explanation or proof why it
> would be correct (i.e. produces uniform results). You only talk about speed.

As I suspected it would be non-uniform. Thanks for demonstrating that.

        -Otto

        

Reply via email to