Readable

Circular random() for rarest traits

Imagine you want to make a generative project. You want the work to have one of 8 traits and you achieve it like this:

let options=['๐Ÿ’','๐Ÿ“','๐Ÿ‡','๐ŸŽ','๐Ÿ‹','๐Ÿ','๐Ÿ','๐Ÿฅฅ',]
let optionsIndex = Math.floor(Math.random() * options.length)
let trait = options[optionsIndex]

Then you run your script many times and print statistics.

let options = ["๐Ÿ’", "๐Ÿ“", "๐Ÿ‡", "๐ŸŽ", "๐Ÿ‹", "๐Ÿ", "๐Ÿ", "๐Ÿฅฅ"];
let stats = Array(8).fill(0)
for (let i = 0; i < 100; i++) {
  let optionsIndex = Math.floor(Math.random() * options.length);
  let trait = options[optionsIndex];
  stats[optionsIndex] ++
}
for(let i=0;i<8;i++){
  console.log(options[i].repeat(stats[i]))
}

Yes, you are right, line #5 is useless

That's what you get:

๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’ 
๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“ 
๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡ 
๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ 
๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹ 
๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ 
๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ 
๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ 

Apples got lucky this time

Then you decide you need coconuts to be more rare than cherries. No, you want coconuts to be the most rare trait in your project! One in hundreds! Therefore you change your random function to squared:

let optionsIndex = Math.floor(Math.random()**2 * options.length);

And you get a distribution similar to this:

๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’ 
๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“ 
๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡ 
๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ 
๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹ 
๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ 
๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ๐Ÿ 
๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ๐Ÿฅฅ

No way, apples

Cherries are the most common now! Isn't it great? Wait, coconut was meant to be the rarest, but they are as rare as the bottom half of the list!

Let's see why it happens:

You see, the cherry area on the horizontal axis is huge, but coconut area is as wide as its neighbours.

Blame on parabola! We need a circle here:

let optionsIndex = Math.floor((1-Math.sqrt(1-Math.random()**2)) * options.length);

Here's how it is different:

Pay attention, how tiny the coconut stripe is! Let's run the test again:

๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’๐Ÿ’ 
๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“๐Ÿ“ 
๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡๐Ÿ‡ 
๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ 
๐Ÿ‹๐Ÿ‹๐Ÿ‹๐Ÿ‹ 
๐Ÿ๐Ÿ๐Ÿ๐Ÿ 
๐Ÿ๐Ÿ 
๐Ÿฅฅ

Circular random test results

We were lucky to get even one, there are no coconuts most of the time.

We did it, hooray. That's a wrap.