Skip to main content
Mod Moved Comments To Chat
bugfix (randint's upper bound is inclusive, so that had the possibility of causing an IndexError)
Source Link
import random
import statistics


def simulate(days_in_year):
    days = [0] * days_in_year
    emperors = 0
    while not all(days):
        days[random.randintrandrange(0, len(days))] = 1
        emperors += 1
        for index, value in enumerate(days):
            if value:
                continue
            if days[index - 1] and days[(index + 1) % len(days)]:
                days[index] = 1
    return emperors


def monte_carlo(amount, days):
    for _ in range(amount):
        yield simulate(days)


print(statistics.mean(monte_carlo(amount, days)))
import random
import statistics


def simulate(days_in_year):
    days = [0] * days_in_year
    emperors = 0
    while not all(days):
        days[random.randint(0, len(days))] = 1
        emperors += 1
        for index, value in enumerate(days):
            if value:
                continue
            if days[index - 1] and days[(index + 1) % len(days)]:
                days[index] = 1
    return emperors


def monte_carlo(amount, days):
    for _ in range(amount):
        yield simulate(days)


print(statistics.mean(monte_carlo(amount, days)))
import random
import statistics


def simulate(days_in_year):
    days = [0] * days_in_year
    emperors = 0
    while not all(days):
        days[random.randrange(len(days))] = 1
        emperors += 1
        for index, value in enumerate(days):
            if value:
                continue
            if days[index - 1] and days[(index + 1) % len(days)]:
                days[index] = 1
    return emperors


def monte_carlo(amount, days):
    for _ in range(amount):
        yield simulate(days)


print(statistics.mean(monte_carlo(amount, days)))
Remove bit that confuses some users
Source Link
Peilonrayz
  • 44.6k
  • 7
  • 80
  • 158
  1. The following any runs in \$O(n)\$ time, where \$n\$ is the length of days. This means worst case it will run however long days is each time you call it.

    while not all(days):
    

    We can do better than that by adding a variable in that increments each time we change a 0 to a 1. We can then compare that to days_in_year to see if the list is full. This will run in \$O(1)\$ time causing a significant saving.

  2. If a new emperor is born on an already existing holiday then no extra holidays will be made.

  3. When a new emperor is born you don't need to check whether each zero can be changed, you instead only need to check two. This will cut another \$O(n)\$ operation to \$O(1)\$.
    Say we have the following as days:

    0123456
    1000010
    

    If the new birthday is:

    • 6 - Because both 5 and 0 are already 1s no additional holidays can be made.

    • 3 - Because 4 is a 0 and 5 is a 1, 4 can become a 1. Because 2 is a 0 but 1 is a 0 then 3 cannot become a 1.

      This cannot propagate outwards.

  1. The following runs in \$O(n)\$ time, where \$n\$ is the length of days. This means worst case it will run however long days is each time you call it.

    while not all(days):
    

    We can do better than that by adding a variable in that increments each time we change a 0 to a 1. We can then compare that to days_in_year to see if the list is full. This will run in \$O(1)\$ time causing a significant saving.

  2. If a new emperor is born on an already existing holiday then no extra holidays will be made.

  3. When a new emperor is born you don't need to check whether each zero can be changed, you instead only need to check two. This will cut another \$O(n)\$ operation to \$O(1)\$.
    Say we have the following as days:

    0123456
    1000010
    

    If the new birthday is:

    • 6 - Because both 5 and 0 are already 1s no additional holidays can be made.

    • 3 - Because 4 is a 0 and 5 is a 1, 4 can become a 1. Because 2 is a 0 but 1 is a 0 then 3 cannot become a 1.

      This cannot propagate outwards.

  1. The following any runs in \$O(n)\$ time, where \$n\$ is the length of days. This means worst case it will run however long days is each time you call it.

    not all(days)
    

    We can do better than that by adding a variable in that increments each time we change a 0 to a 1. We can then compare that to days_in_year to see if the list is full. This will run in \$O(1)\$ time causing a significant saving.

  2. If a new emperor is born on an already existing holiday then no extra holidays will be made.

  3. When a new emperor is born you don't need to check whether each zero can be changed, you instead only need to check two. This will cut another \$O(n)\$ operation to \$O(1)\$.
    Say we have the following as days:

    0123456
    1000010
    

    If the new birthday is:

    • 6 - Because both 5 and 0 are already 1s no additional holidays can be made.

    • 3 - Because 4 is a 0 and 5 is a 1, 4 can become a 1. Because 2 is a 0 but 1 is a 0 then 3 cannot become a 1.

      This cannot propagate outwards.

Source Link
Peilonrayz
  • 44.6k
  • 7
  • 80
  • 158

Firstly lets get your code to be a little cleaner:

  • You can use statistics.mean rather than make my_mean.

  • You should use a for loop rather than a while loop in monte_carlo.

  • You don't need to do assign n_emperer at all in the function.

  • Exp and D should be lower_snake_case. This is as they are functions and variables.

  • You should put spaces around all operators.

  • There should be a space after commas.

  • You should have some better names day_list could just be days, D could also be something like days, summ can be total, iters could be amounts.

  • You can just use all(day_list) rather than all((d == 1 for d in day_list)).

  • Do not use == to compare to singletons like False. It would be better if you instead use not.

  • This doesn't check if both of the values are 1 it checks if the first is truthy and the second is one. This means if you set day_list[index - 1] to two it'd still be true.

    day_list[ind - 1] and day_list[ind + 1] == 1
    

    To check they are both equal to one you shoud use:

    day_list[ind - 1] == 1 and day_list[ind + 1] == 1
    

    Here I would instead just check if they are truthy.

  • You don't need if ind == 0: as if ind is 0 then ind - 1 will be -1.

  • You can just use (ind + 1) % len(days) to remove the need for elif index == len(days)-1:.

import random
import statistics


def simulate(days_in_year):
    days = [0] * days_in_year
    emperors = 0
    while not all(days):
        days[random.randint(0, len(days))] = 1
        emperors += 1
        for index, value in enumerate(days):
            if value:
                continue
            if days[index - 1] and days[(index + 1) % len(days)]:
                days[index] = 1
    return emperors


def monte_carlo(amount, days):
    for _ in range(amount):
        yield simulate(days)


print(statistics.mean(monte_carlo(amount, days)))

Now that the code is nice and small we can focus on what is causing performance issues.

  1. The following runs in \$O(n)\$ time, where \$n\$ is the length of days. This means worst case it will run however long days is each time you call it.

    while not all(days):
    

    We can do better than that by adding a variable in that increments each time we change a 0 to a 1. We can then compare that to days_in_year to see if the list is full. This will run in \$O(1)\$ time causing a significant saving.

  2. If a new emperor is born on an already existing holiday then no extra holidays will be made.

  3. When a new emperor is born you don't need to check whether each zero can be changed, you instead only need to check two. This will cut another \$O(n)\$ operation to \$O(1)\$.
    Say we have the following as days:

    0123456
    1000010
    

    If the new birthday is:

    • 6 - Because both 5 and 0 are already 1s no additional holidays can be made.

    • 3 - Because 4 is a 0 and 5 is a 1, 4 can become a 1. Because 2 is a 0 but 1 is a 0 then 3 cannot become a 1.

      This cannot propagate outwards.