Arc Forumnew | comments | leaders | submitlogin
Newbie arc app just for fun... (blackstag.com)
6 points by thaddeus 5399 days ago | 13 comments


2 points by palsecam 5398 days ago | link

In 'genWIN->Nums,

   (until (is (len winset) set-count)
      (let n (rand:1up top)
        (if (>= n bottom) (insortnew < n winset))))
is not optimal. Because, say if `bottom' is 30 and `top' is 40, in nearly the 3/4 of times, you'll call 'rand but not use the result, and you'll make a wasted loop turn.

A solution:

   (def genWIN->Nums2 (set-count bottom top)
     "Generates a list of `set-count' random numbers, each
     in the range between `bottom' and `top' (inclusives).
     The generation doesn't waste any CPU cycle :-)."
      (let winset nil 
         (until (is (len winset) set-count)
            (let n (rand (- (1up top) bottom))
	       (insortnew < (+ n bottom) winset)))
         (prall:pretty-nums winset)))
The idea is, for `bottom'=30 and `top'=40, to get a random number between 0 and 11, and then add `bottom' to it.

The result:

   (time (repeat 1000
      (genWIN->Nums 1 30 40)))
   => 1908 msec.
   (time (repeat 1000
      (genWIN->Nums2 1 30 40)))
   => 706 msec.

-----

3 points by thaddeus 5397 days ago | link

FYI - I decided to redefine the rand function.

   (def random (n1 (o n2 0))
        (if (is n2 0)
            (swap n1 n2))
          (+ (rand (- (1up n2) n1)) n1))
this will make my code more readable + less 1ups for future code.

   (until (is (len winset) set-count)
          (insortnew < (random bottom top) winset)) 
T.

-----

3 points by thaddeus 5395 days ago | link

eh-hem. oops. right when I said I need to think like you more palsecam - I didn't!

   (def random (n1 (o n2 0))
        (if (is n2 0)
            (swap n1 n2))
          (+ (rand (- (1up n2) n1)) n1))
I had been trying to allow (random n) to return 0 as a valid value, but given the poor performance - if I want that I can always use rand...

   arc> (time (repeat 1000 (random 50))) 
   time: 12 msec.

   arc> (time (repeat 1000 (rand 50)))
   time: 2 msec.

   .... so .... re-take :)

   (def random (n1 (o n2 nil))
        (if (no n2)
            (1up:rand n1)
            (+ (rand (- (1up n2) n1)) n1)))

   arc> (time (repeat 1000 (random 50)))
   time: 3 msec.
Just goes to show: don't get too fancy. :) T.

-----

2 points by palsecam 5395 days ago | link

Thanks for sharing your investigation & ideas on the question :-)

-----

2 points by thaddeus 5398 days ago | link

That's awesome. I hadn't gone that far... and have yet to create any sort of habitual thinking around optimizations... Need to think like you more. Thanks. T.

-----

1 point by palsecam 5398 days ago | link

From what I've seen, the numbers should be fairly random. It is often humans that misunderstand randomness, and not computers, as said in the linked article in CatDancer's post.

However, randomness is a complex issue in computing, and it's good to keep it in mind. See for example:

* "How I Hacked Hacker News" (http://news.ycombinator.com/item?id=639976) that exploited a vulnerability in 'rand-string.

* A famous "bug" with rand(0,1) in PHP on Windows: http://cod.ifies.com/2008/05/php-rand01-on-windows-openssl-r....

* The Unix `rand' command:

   $ man 3 rand  # here on a linux system
   [...]
   NOTES
   The versions of rand() and srand() in the Linux C Library use 
   the same random number generator as random(3) and srandom(3),
   so the lower-order bits should be as random as the higher-order 
   bits.
   However, on older rand() implementations, and on current 
   implementations on different systems, the lower-order bits are
   much less random than the higher-order bits.

It means, on old systems, the situation is something like:

- rand() outputs a 4 bits integer, so the value goes from 0 to 15,

- if you do 'rand() % 10', there is a 2/16 probability you get a number in [0, 5], and only 1/16 you get a number in [6, 9].

-----

1 point by shader 5398 days ago | link

Mind if I ask where the other 13/16 went? Since rand() % 10 can only output 0-10 there are only 10 possibilities, all of which are covered in your two ranges.

Either people frequently have problems with statistics as well, or random numbers are really complicated ;)

-----

1 point by palsecam 5398 days ago | link

Well, I'm totally not an expert in "randomness assisted by computer", I just know it's something, like cryptography, where you should be careful before implementing your own solution. And I am bad in maths, and statistics/probas are so tricky (at least for me), you are right.

However, a code demonstration (in Perl, sorry, for perf/convenience):

   # We fake a bad `rand()' function by only taking 
   # values between 0 and 15.
   $cnt{rand(15) % 10} += 1 for (0..1_000_000);
   print $_, "\t", $cnt{$_}, "\n" for (sort keys %cnt);
outputs:

   0       133556
   1       133050
   2       133423
   3       132772
   4       133342
   5       66815
   6       67065
   7       66582
   8       66858
   9       66538
Are you OK with this demonstration or shall I try to find a more formal explanation/example?

-----

3 points by absz 5397 days ago | link

What you said is correct; the only problem is that with the numbers you quoted, the total probability of rolling a number in the range [0,10) is 2/16 + 1/16 = 3/16 < 1 :) The numbers you meant to quote, as borne out by your data above, are a 10/16 = 5/8 chance to get a number in the range [0,5), and a 6/16 = 3/8 chance to get a number in the range [6,10). (Equivalently, a 12/16 = 3/4 chance to get a number in the range [0,5] and a 4/16 = 1/4 chance to get a number in the range [6,9], but since that doesn't partition the [0,10) range evenly, it's less instructive.)

-----

1 point by palsecam 5397 days ago | link

Thanks for the rectification :-)

-----

2 points by palsecam 5398 days ago | link

The same thing in Arc:

   (let num->occ (table [for i 0 9 (= _.i 0)])
      (repeat 1000000  ; 1 million iterations
         (++ (num->occ (mod (rand 15) 10))))
      (each k (sort < (keys num->occ))
         (prn k "\t" (num->occ k))))
(~ same output than the Perl version)

Just for information:

   $ time perl < test-rand15.pl
   0m1.545s
   $ time mzscheme -m -f as.scm < test-rand15.arc
   0m17.374s
   $ time java -jar rainbow.jar < test-rand15.arc2 
   0m32.104s

-----

1 point by thaddeus 5398 days ago | link

I have yet to win the lottery using this app, but hey its 15 million this coming Friday and I'm sure it's going to happen someday :).

That being said - my girlfriend insists the numbers don't look random.

-----

5 points by CatDancer 5398 days ago | link

the numbers don't look random: http://www.random.org/faq/#Q2.7

-----