Ayman
Ayman

Reputation: 31

How to guarantee a certain number of random occurrences in a loop?

I have a program that simulates the rotation of a clock's hand and it randomly jumps 2x the normal rotation amount. I need to guarantee that in a full rotation, I get at least n jumps. How should I go about randomizing it but ensuring at least n jumps?

I thought about having a guaranteed jump every x iterations but then it's not random.

Here is the code of my rotation loop:

for (let i = 0; i < ticks; i++) {           
    // Move the clockhand by a position, sometimes by a greater amount 
    setTimeout(() => {
        let d = Math.random();
        jump = false;
        // Randomize and keep track of jumps
        if (d < (probabilty/100)) {
            jump = true;
        }
     }
}

Upvotes: 3

Views: 136

Answers (1)

aerial
aerial

Reputation: 1198

One way is to set a MAX_JUMPS and MAX_TICKS amount per full rotation then track the amount of jumps and ticks performed in each iteration. If both jumps and ticks still didn't reach their max amount then randomly choose between them. If one of them maxed out then just choose the other one untill the rotation completes.

Here is a working example in which exactly 5 jumps are guaranteed. Note that I set DISTANCE to only 30 because the snippet console doesn't show lots of lines. Also, the time out delay is set to 0 just to quickly show the full result.

const DISTANCE = 30 // change to 60
const MAX_JUMPS = 5
const MAX_TICKS = DISTANCE - 2 * MAX_JUMPS
const PROBABILITY = 0.25

let currentDistance = 0
let normalTicks = 0
let jumps = 0

function oneTick() {
  if (currentDistance === DISTANCE) {
    // Completed full rotation
    console.log('normalTicks = ', normalTicks, ', jumps = ', jumps)
    return
  }

  setTimeout(() => {
    if (normalTicks === MAX_TICKS) {
      // If we are out of normal ticks then just jump
      // to satisfy the MAX_JUMPS amount
      jump()

    } else if (jumps === MAX_JUMPS) {
      // If we run out of jumps, do normal tick
      normal()

    } else {
      // If both ticks and jumps are available then randomly choose one
      if (Math.random() > PROBABILITY) {
        normal()
      } else {
        jump()
      }
      
    }

    console.log(currentDistance)
    oneTick()
  }, 0 /* change to 1000 */)
}

function normal() {
  normalTicks += 1
  currentDistance += 1
}

function jump() {
  jumps += 1
  currentDistance += 2
  console.log('JUMPED! Jumps left: ', MAX_JUMPS - jumps)
}

oneTick()
.as-console-wrapper {
  min-height: 200px;
  top: 0;
}

EDIT: To make it do at least MAX_JUMPS and randomly do more jumps after that just remove the if (jumps === MAX_JUMPS) block:

const DISTANCE = 30 // change to 60
const MAX_JUMPS = 5
const MAX_TICKS = DISTANCE - 2 * MAX_JUMPS
const PROBABILITY = 0.25

let currentDistance = 0
let normalTicks = 0
let jumps = 0

function oneTick() {
  if (currentDistance >= DISTANCE) {
    console.log('normalTicks = ', normalTicks, ', jumps = ', jumps)
    return
  }

  setTimeout(() => {
    if (normalTicks === MAX_TICKS) {
      jump()
    } else {
      if (Math.random() > PROBABILITY) {
        normal()
      } else {
        jump()
      }
    }

    console.log(currentDistance)
    oneTick()
  }, 0 /* change to 1000 */ )
}

function normal() {
  normalTicks += 1
  currentDistance += 1
}

function jump() {
  jumps += 1
  currentDistance += 2
  console.log('JUMPED! Jumps performed: ', jumps)
}

oneTick()
.as-console-wrapper {
  min-height: 200px;
  top: 0;
}

Note that this may cause it to jump at the very last tick of the rotation so you will have to decide if that should count as a single tick or if the extra tick should be added to the next rotation.

Upvotes: 2

Related Questions