Javascript Challenge: Lotto Number Generator

Matthias Reuter's picture

The German lottery currently holds a jackpot of about 30 million Euro. A friend of mine took the bait and participated yesterday. Since he is a software developer, he wrote a small program to get him six random numbers in the range of 1 and 49.

Well, it's not difficult to write such a programm. The challenge is to do so in little bytes. So I challenge you:

Write a JavaScript function that generates random lotto numbers. This function has to return an array of six different numbers from 1 to 49 (including both) in ascending order. You may use features of ECMA-262 only, that means no Array.contains and stuff. You must not induce global variables.

The function has to look like this

var getRandomLottoNumbers = function () {
    // your implementation here
};

Minify your function using JSMin (level aggressive) and count the bytes between the outer curly braces.

To participate in this challenge, simply post your solution as a comment.

Spoiler warning On the next page I describe the steps I took for my solution. Don't read if you want to submit your own.

Trackback URL for this post:

http://united-coders.com/trackback/39

Comments

Anonymous's picture

for(var n=[],i=0;++i<50;n.push(i));for(;--i>6;n.splice(i*Math.random()|0,1));return n

Cheers, Stephan

Anonymous's picture

How about skipping push and assigning directly?

- n.push(i)
+ n[i-1]=i

1 char saved.

Anonymous's picture

for(n=[],i=0;i<49;n[i++]=i);for(;i>6;n.splice(i--*Math.random()|0,1));return n

78 characters (7 characters shorter):
-removed 'var'
-replaced push with n[i++] (and removed ++ from i++<50)
-50 becomes 49
-moved the -- (could use --i>5, but it's better for readability this way)

too bad you can't do 49>n[i++]=i as it would have shaved off another character

Aleksander

Anonymous's picture

for(n=[],i=0;i<49;n[i++]=i);for(;i>6;n.splice(i--*Math.random(),1));return n

76 chars
-call to floor is not needed.

Anonymous's picture

for(n=[],i=0;i<49;n[i++]=i);for(;i>6;n.splice(i--*Math.random(),1));return n

76 characters
-no floor needed for splice argument

Aleksander

Matthias Reuter's picture

removing var leads to global variables, which was prohibited by the rules.

So this is no valid solution.

Christian Harms's picture

A small pice in an other language: I have added the needed import to the effective codesize: 'import random\n\ta=range(1,49)\n\twhile len(a)>6:a.remove(random.choice(a))\n\treturn a' - then becomes 81 bytes.

  1. import random
  2. def lottoNumbers():
  3.     a=range(1,49)
  4.     while len(a)>6:a.remove(random.choice(a))
  5.     return a

Anonymous's picture

return [1,2,3,4,5,6];

Oh, is it supposed to be different every time?

return [1,2,3,4,5,5+Math.random()*10|0];

See where I'm going with this? Please define your terms. Did you mean a uniform probability distribution over {(a,b,c,d,e,f) | 1 <= a < b < c < d < e < f <= 49}?

Matthias Reuter's picture

When in doubt use your common sense :-)

To clarify: Yes, I mean a uniform probability distribution. Would you use a lotto number generator, if only one number was randomly generated?

Anonymous's picture

Here's 76 bytes(!) using an algorithm that decides randomly whether to include each number from 1 to 49, then checks to see if there were 6 of them. The outer loop seems to run about 7 times on average, so not too bad.

for(;n^6;)for(var a=[],i=0,n;++i<50;)n=Math.random()<.1?a.push(i):n;return a

The probability distribution is correct, and no global variables are created. I think this meets all the criteria.

Note that "push" returns the new length of the array, and "n^6" essentially means "n!=6".

-- DavidG

Anonymous's picture

@DavidG: what is the initial value of n supposed to be?

Matthias Reuter's picture

it's undefined. In a function, Javascript first generates all variables, no matter where they're declared. If no value is given, the value is undefined.

undefined^6 is 6, which converts to true in the outer for-loop

Anonymous's picture

I'll see your 76 bytes and raise you 75 bytes.

for(;n^6;)for(var a=[],i=0,n=0;++i<50;)Math.random()<.1?a[n++]=i:0;return a

Also, this will not cause an undefined error for 0.6% of the calls of the function, as your entry seems to do. Or am I wrong on this?

--Stephan

Matthias Reuter's picture

Wow.

My first thought was: n is global, but no, it's not. My second thought was: what the f*** is happening here? It took me a while to understand what the algorithm does. My third thought was: That's not uniformly distributed. But it is.

So this algorithm meets all the criteria.

Anonymous's picture

83 bytes: for(var n=[],i=0;++i<50;n[i-1]=i);for(;--i>6;n.splice(i*Math.random(),1));return n

(1) Splice will automatically convert the first argument to an integer, so floor is not needed. (2) n[i-1]=i can work instead of n.push(i). (3) last return needs no semi-colon.

Matt's picture

  1. r=function(){return Math.random()*50|1};return [r(),r(),r(),r(),r(),r()]

72 bytes

Christian Harms's picture

This simple solution doesn't check for doubles!

Matt's picture

Yeah, turns out I have trouble with this whole "reading comprehension" thing.

I also created a global var and the values aren't in ascending order.

Anonymous's picture

return (Math.random()+"").substr(2).match(/\d{2}/g).splice(0,6).sort().map(function(a){Math.ceil(a/2)})

Not the smallest but fun to write. Doesn't check for dups and not sure the range is correct.

@mpr312

Anonymous's picture

Should be even longer, forgot a return

return (Math.random()+"").substr(2).match(/\d{2}/g).splice(0,6).sort().map(function(a){return Math.ceil(a/2)})

@mpr312

Anonymous's picture

for(var n=[],i=6,r;i;r=Math.random()*49|1,n[-r]?0:n[--i]=n[-r]=r);return n

74 bytes. Fails on the sorting, but c'mon – it's a lottery ;-).

— hzr

Anonymous's picture

in firefox. 48bytes

[0,0,0,0,0,0].map(function() Math.random()*50|1)

Anonymous's picture

interesting how the lambda expression makes it more readable.

Anonymous's picture

42
[,,,,,,].map(function()Math.random()*50|1)

Anonymous's picture

This is not ECMA-262. Play by the rules.

Christian Harms's picture

Hmmmm, nice - but it's not sorted and map not part of the of ECMA-262 definition.

marcelduran's picture

They are not the smallest but they're in a single line (jQuery-like) using features of ECMA-262 only.

This one has 204 bytes (211 including return) and has no repetitions:

new Array(7).join(',x').replace(/x/g,function(){Array.a=Array.a||{};var x;while((x=Math.random()*50|1)in Array.a);Array.a[x]=1;return x}).slice(1).split(',').sort(function(a,b){delete Array.a;return a-b})

Note: I augmented Array by adding an object to it but removed it later, so no global vars added.

This one has 127 bytes (134 including return) and might have repetitions:

new Array(7).join(',x').replace(/x/g,function(){return Math.random()*50|1}).slice(1).split(',').sort(function(a,b){return a-b})

marcelduran's picture

even smaller:

no dupes: 192 or 199 w/ return

[0,0,0,0,0,0].join().replace(/0/g,function(){Array.a=Array.a||{};var x;while((x=Math.random()*50|1)in Array.a);Array.a[x]=1;return x}).split(',').sort(function(a,b){delete Array.a;return a-b})

w/ dupes: 115 or 122 w/ return

[0,0,0,0,0,0].join().replace(/0/g,function(){return Math.random()*50|1}).split(',').sort(function(a,b){return a-b})

Anonymous's picture

I got 72 bytes with an algorithm that memoizes the already chosen numbers.

var a=[],r=n=0;for(;++n<7;a[r]=r)while(a[r=Math.random()*49|1]);return a

The array is not consecutively indexed, but that shouldn't matter, because it is in order and still has only 6 elements.

Anonymous's picture

That defines a variable n in the global scope. Also, it has more than 6 elements. Exactly 6 elements are not undefined, but there are more than 6 nonetheless.

Anonymous's picture

New algorithm: 65 bytes

for(var v=[],m=6,n=49;m;--n)Math.random()*n>m?0:v[--m]=n;return v

Looping until all numbers have been "found", checking numbers 49 to 1 one by one, updating probabilities for each number found/not found.

The probability of the next unchecked number is a number chosen by the lottery is P(number) = m / n where n are the number of unchecked numbers and m are the number of selected numbers left.