Неверный процент результатов, имитирующий парадокс дня рождения

Неверный процент результатов, имитирующий парадокс дня рождения

Я видел видео о проблеме с днем ​​рождения: если в комнате 23 человека, то с вероятностью 50% у двух человек день рождения будет одинаковым. Вместо того, чтобы просто разбираться в математике, я хотел провести быстрое моделирование, поэтому поторопился и написал этот код с нуля.

Просто для некоторого контекста: я занимаюсь программированием всего около года, пройдя курс в старшей школе и имея некоторый опыт работы за пределами школы. Очевидно, что когда я запускаю симуляцию, истинный процент оказывается равным 5%, что на порядок превышает ожидаемый. Что именно не так с кодом?

Я написал код для запуска только 1000 симуляций, и мне нужно было просто указать количество совпадений, чтобы получить приблизительную оценку, но я получал только около 50-70, а не 500. Итак, я переписал код, чтобы он работал бесконечно. и просто создайте хороший процент.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Здесь есть пара проблем. Первый — это цикл repeat until. Все, что это делает, — это просматривает список из 23 неотсортированных дней рождения и сравнивает каждый день рождения с тем, который находится непосредственно рядом с ним. Логика, которая вам действительно нужна, сравнивает каждый день рождения со всеми другими днями рождения, а не только с соседним.

Другая проблема заключается в том, что сравниваются не два дня рождения, а переменная счетчика i и день рождения. Вместо:

if i = birthday[i - 1]

вам нужно что-то вроде:

if birthday[i] = birthday[i - 1]

...кроме использования birthday[i] = birthday[j], где j — каждый второй день рождения в списке дней рождения.

Вот широко комментируемый код JavaScript, который вы можете попытаться адаптировать, чтобы исправить свой код Scratch. Вы можете относиться к нему как к псевдокоду, делая его немного более интересным, чем если бы я просто дал вам ответ в Scratch.

// Declare a variable to keep track of how
// many simulations returned a matching birthday.
let matchingBirthdays = 0;

// The total number of simulations, a constant.
const simulations = 1000;

// For each simulation, we will pick 23
// random birthdays for the year.
const numberOfBirthdaysToTest = 23;

// Now loop `simulations` times and run each simulation.
for (let i = 0; i < simulations; i++) {
  
  // Start the simulation by making an empty
  // array (list) of 23 birthdays.
  const birthdays = [];
  
  // For each of the 23 days...
  for (let j = 0; j < numberOfBirthdaysToTest; j++) {

    // ...pick a random day of the year
    // and add it to the `birthdays` list.
    birthdays.push(Math.floor(Math.random() * 365));
  } // end random birthday array creation block
  
  // Show the birthdays that were selected (if you want):
  // console.info(birthdays)

  // Now, let's find matching birthdays.
  // Let's assume we won't find any matches:
  let found = false;

  // Then loop over every birthday to try to find a match...
  for (let j = 0; !found && j < birthdays.length; j++) {
  
    // For birthdays[j], see if there's a birthdays[k]
    // later in the array that has the same value.
    for (let k = j + 1; k < birthdays.length; k++) {

      // Now we have two different birthdays
      // pointed to by indices j and k.
      // Determine if they are equal:
      if (birthdays[j] === birthdays[k]) {
      
        // We found a match! Add 1 to the
        // total number of birthdays:
        matchingBirthdays++;
        
        // Optionally log the hit:
        // console.info("Found match!");
        
        // Then set `found` to true and break out of
        // both loops to end the whole search.
        // We'll move on to the next simulation.
        found = true;
        break;
      } // end birthday match block
    } // end search block
  
    // If we got here and `found` is still false,
    // then no match was found, and
    // `matchingBirthdays` will not change.
  } // end loop over all birthdays
} // end simulation block

// Print the final ratio of simulations that have
// at least a pair of matching birthdays over the
// total number of simulations. This should be around
// 0.5 assuming we've done at least 1000 simulations.
console.info(matchingBirthdays / simulations);

Обратите внимание, что этот код намеренно избегает умных функций JavaScript, которые упрощают код, но плохо переводятся в Scratch.

Этот проект — отличная иллюстрация сложности (скорости роста) вложенных циклов. Причина, по которой это называется парадоксом дня рождения, заключается в том, что удивительно, что нужно всего 23 человека, чтобы с вероятностью 50% достичь хотя бы одной пары дней рождения. Вместо 23 сравнений доступно 253 сравнения, среди которых можно найти одно совпадение.

Говоря простым языком, на каждого человека приходится 22 человека, с которыми можно сравнивать. С точки зрения кода это будет вложенный цикл. Вложенные циклы выполняют гораздо больше работы, даже если вы увеличиваете количество элементов лишь на небольшую величину. Попробуйте увеличить переменную numberOfBirthdaysToTest, и вы увидите, что вероятность совпадения быстро приближается к 100%, даже не приближаясь к 365.

Это явление известно как O(n^2) или квадратичный алгоритм. Вот скорость роста квадратичного алгоритма:

const nestedLoop = n => {
  let iterations = 0;

  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      iterations++;
    }
  }

  return iterations;
};

for (let n = 0; n < 15; n++) {
  const iterations = nestedLoop(n);
  console.info(`n = ${n}, iterations = ${iterations}`);
}

В Википедии есть диаграмма , иллюстрирующая темпы роста по мере увеличения n (количества людей). Надеюсь, это поможет вам интуитивно определить, почему ваше решение неверно: не видя этого вложенного цикла в своем коде, вы не увидите того эффекта «взрывного роста», который характерен для проблемы дня рождения.

Не могу поверить, что забыл о вложенных циклах. Спасибо за напоминание. Прошло много времени с тех пор, как я что-либо кодировал, и я не проверял свой код должным образом. Теперь он работает правильно.

Abhinav Jonnala 22.06.2024 17:55

Другие вопросы по теме