Luke Spademan
Luke Spademan

Reputation: 106

Python program outputting different results, even though no random is used

I am trying to solve Project Euler Problem 19.

I am not here to ask for the answer to the problem, but I noticed that every time my program runs, its output is different.

Please can someone explain why for me

"""
    Author: Luke Spademan
    Calculates answer to project euler problem id19
    https://projecteuler.net/problem=19
"""


def calculate():
    ans = 0
    months = {
        "jan": 31,
        "feb": 28,
        "mar": 31,
        "apr": 30,
        "may": 31,
        "jun": 30,
        "jul": 31,
        "aug": 31,
        "sep": 30,
        "oct": 31,
        "nov": 30,
        "dec": 31,
    }
    i = 1
    for year in range(1901, 2001):
        for month in months:
            months["feb"] = 28
            if year % 4 == 0 and not (year % 100 == 0 and year % 400 != 0):
                months["feb"] = 29
            if i % 7 == 0:
                ans += 1
            i += months[month]
    return ans

if __name__ == "__main__":
    answer = calculate()
    print(answer)

Upvotes: 6

Views: 1238

Answers (2)

Eric Duminil
Eric Duminil

Reputation: 54303

With datetime

Before you get lost in implementation details about dicts and OrderedDicts, you could use datetime to know what the answer should be:

>>> from datetime import date
>>> sum(1 for month in range(1,13) for year in range(1901, 2001) if date(year, month, 1).weekday() == 6)
171

With list of tuples

Note that even with Python 3.6 or Python2 + OrderedDict, your code returns 172.

Your sunday test is written as i % 7 == 0. It means that the 1st day in your loop (1st of january 1901), which is a tuesday, should be initialized with i = 2.

To avoid any problem with unsorted dicts, you could simply use a list of tuples :

def calculate():
    ans = 0
    months = [('jan', 31), ('feb', 28), ('mar', 31), ('apr', 30), ('may', 31), ('jun', 30), ('jul', 31), ('aug', 31), ('sep', 30), ('oct', 31), ('nov', 30), ('dec', 31)]
    i = 2
    for year in range(1901, 2001):
        for month_name, days in months:
            if month_name == "feb":
              if year % 4 == 0 and not (year % 100 == 0 and year % 400 != 0):
                  days = 29
              else:
                  days = 28
            if i % 7 == 0:
                ans += 1
            i += days
    return ans

if __name__ == "__main__":
    answer = calculate()
    print(answer)

This code returns 171 with any Python version.

Upvotes: 0

javidcf
javidcf

Reputation: 59731

The problem is that the result of the computation depends on the order in which months is iterated. As of Python 3.3, string hashing is randomized by default, meaning that this order will not be deterministic (until Python 3.6). You can read about how to make this program run deterministically here, although I think that your intention is to iterate months in a predefined order always, in which case it should probably be a list or an OrderedDict.

Upvotes: 6

Related Questions