2

I have a simple JavaScript that builds a random string by choosing "randomly"* from several lists of other strings. When run the script repeatedly I notice a lot of repetition and I'm trying to understand why. I understand that there is no such thing as truly random in this context. But the repetition I'm seeing is not subtle. My lists have 20-30 items each and I frequently see the same string appear two and three times in a row.

Here is the relevant code for how strings are selected from individual lists.

function(list) {
    return list[Math.floor(Math.random()*list.length)];
}

The script calls this function several times in a row, passing different lists as parameters.

Is this a problem with my code? Or maybe something is getting cached?

(NOTE: This is not for security! Never do something like this as a substitute for proper encryption)

emersonthis
  • 32,822
  • 59
  • 210
  • 375
  • 1
    If you have particular statistical requirements about your distribution that you're trying to achieve beyond "random", then please state them explicitly. So far your only objection is that you have a list of 30 items and you "frequently" (not a concise term) see the same string appear two and three times in a row. _That's normal._ Are you looking for a way to prevent consecutively repeated numbers entirely? – Wyck Apr 12 '22 at 05:25
  • I don't have a fixed distribution requirement, but the amount of repetition seemed far beyond what would be statistically plausible for anything close to random. The script builds a sentence by picking "randomly" from several lists of words and combining them. Each list has ~30 items. So I think the odds of getting the same word 3 times in a row is: `(1/(30*30*30)) * 30`. Often it only take me a few tries before this happens, which seemed suspiciously frequent. But your question does make me wonder if maybe it's less surprising than I thought. – emersonthis Apr 12 '22 at 19:52
  • With true randomness, literally all outcomes are statistically "plausible". Now with [xorshift128+](https://github.com/v8/v8/blob/main/src/base/utils/random-number-generator.h), you're getting something that passes (almost) all of the [Diehard randomness tests](https://en.wikipedia.org/wiki/Diehard_tests). I'm certain this is less surprising than you think. – Wyck Apr 12 '22 at 23:36

4 Answers4

1

I gave your code a try as follows:

list = ['one','two','three','four','five','six','seven','eight','nine','ten']

for (i=0;i<10;i++) {
  console.log(list[Math.floor(Math.random()*list.length)])
}

// RUN ONE
// 'two'
// 'six'
// 'eight'
// 'one'
// 'eight'
// 'two'
// 'two'
// 'two'
// 'ten'
// 'six'

// RUN TWO
// 'four'
// 'seven'
// 'nine'
// 'one'
// 'six'
// 'six'
// 'eight'
// 'two'
// 'nine'
// 'three'

It's interesting how much duplication there is, however I think you should expect some. Is it possible that what you're really looking for is a shuffle? If so, you might find the answer to this question useful: How to randomize (shuffle) a JavaScript array?

SteveGreenley
  • 630
  • 1
  • 5
  • 7
  • All of the answers so far have been helpful, but I have to give someone the green check mark and I think your answer best demonstrates and encapsulates the overall take-away. Also the link to that other question was really helpful. – emersonthis Apr 15 '22 at 23:47
1

your code is correct. there's a major difference between what humans think randomness looks like and what randomness actually looks like. it sounds like you may be considering ANY successive duplicate word in ANY position in the sentence to be the same outcome. it's really not; it's a ton of different outcomes that you're grouping together to call it a singular "frequent" result. try picking a word first and seeing how long it takes for that word come up as the last three words in your sentence.

if you're still interested in stronger randoms, you can check out rando.js or random.org, but know that they'll exhibit the same behavior- and that's a good thing.

Aaron Plocharczyk
  • 2,776
  • 2
  • 7
  • 15
0

This is a lot of code to get a random element. I know, but it is just an idea that may help you achieve a better result.

So mainly I'm saving the three previews random numbers in an array and then just using conditions to see if a new random number must be generated or not.

In randomness two/three/four/... consecutive same numbers are always possible so one should never avoid having them. This code will also give consecutive same elements but much less.

const list = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

function getRand(){ 
  return Math.floor(Math.random() * list.length)
}

let previews = [null, null, null]
let n = null

list.forEach((el) => {
  n = getRand()
  
  if(previews[0] === n || previews[1] === n || previews[2] === n){
     n = getRand()
     previews = [n, ...previews]
   }
   else {
     previews = [n, ...previews]
   }
   console.log(list[n])
})
Gass
  • 7,536
  • 3
  • 37
  • 41
0

A couple of the answers and comments caused me to second-guess whether the results I'm seeing are actually surprising. With a list of 30 items, the odds of getting a specific one three times in a row is 1/(303030). But the odds of getting any item three times is much greater.

I think my code is actually pretty close to the reputable Fisher-Yates shuffle algorithm: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

So maybe my code is actually behaving as expected?

emersonthis
  • 32,822
  • 59
  • 210
  • 375