Falling in Love with Math Again

April 15, 2025

Math controlled my life for the last week.

It’s all I thought about. Nearly every second of the day. This may sound unpleasant, had it happened to you. What you may find strange is that I am okay with this. I not only like math, I love it. There’s a reason I have a bachelor's degree for it.

However, my love for math is inconsistent. I didn’t think about it much over the last two years, which I now regret. I wish I had spent more time reading, studying, and practicing the discipline because it’s so beautiful. You may disagree with that last sentence, but give me a few minutes, and I’ll show you how beautiful math can be.

My recent obsession with math stems from a desire to improve my problem-solving skills. I already consider myself a great problem solver. This isn’t me being cocky. I say it because problem-solving is the core purpose of my data analyst job. So, if I do great work as a data analyst, that must mean I’m a great problem solver. But being a great problem solver doesn’t mean there’s no room for growth. There’s plenty of space for improvement, and I strive for that.

The problem is that I didn’t know how to improve my problem-solving skills. It’s one thing to say that problem-solving skills matter in life, and many creators and writers say so on the internet. But few people give practical advice on actually improving your skills. That’s why I did the only reasonable thing any sane person in 2025 would do…

I asked ChatGPT for help.

I fed it the following prompt: What are some good ways to improve problem-solving and strategy skills?

ChatGPT responded with a long list of options, but the second item snatched my attention. It said:

2. Learn Through Games and Puzzles

- Strategy games
: Chess, Go, Sudoku, or strategy-based board games (e.g., Catan, Risk).
- Logic puzzles
: Try sites like Brilliant.org, Project Euler, or brain teasers.
- Escape rooms
: Real or virtual ones challenge both logic and team collaboration.

My eyes lit up when I saw “Project Euler” mentioned in the second bullet point. It felt as though I saw an old frenemy for the first time in ages. I knew I would be in trouble.

Project Euler is a website I used briefly in college. The site's best explanation is as “Leet Code for Mathematicians.” The site contains an archive of mathematical and computational challenges for math geeks like me to solve through code. It’s the perfect blend of math and programming—two things that interest me.

The previous time I used Project Euler was during my sophomore year of college when I took a Number Theory course (MATH 407 at Binghamton University). The original syllabus did not mention Project Euler, but I took the course during the spring of 2020 when COVID happened. Once all classes went remote, professors were unprepared for the teaching style they had to adapt to. My Number Theory professor especially struggled. So, he did what all mathematicians do when they struggle to solve a problem. He got creative.

Rather than administer and proctor the originally scheduled second midterm, my professor cancelled it and told us to take the beginner’s Python course on Codecademy. After we did that, he assigned 5-10 Project Euler problems for us to solve. The number of problems we correctly solved corresponded to a grade for our second midterm. I had taken Computer Science 101 the previous semester, so I felt I had an advantage over other students regarding my Python skills. But I soon realized understanding math and number theory mattered more than programming skills.

The problems were challenging, and I struggled to apply number theory concepts to code. So, I decided that I hated Project Euler and swore never to do it again. (Yet, here I am now.) I would have preferred taking an actual midterm because at least then I would have known what types of problems would appear on the test (basically ones similar to the textbook’s problem set).

Things are different now.

Seeing Project Euler excites me instead of pissing me off. The difference between now and then is that I am no longer doing math to pass a class or get a piece of paper saying I know math. Instead, I do it because I enjoy solving complex problems that most people cannot solve. So, when ChatGPT recommended Project Euler, I knew I would have more fun this time.

I began with Problem 1, Multiples of 3 and 5. The goal is to find the sum of all multiples of 3 and 5 below 1000.

This is similar to the well-known FizzBuzz problem. If you don’t know what that is, the FizzBuzz problem is a classic coding interview problem that asks you to print all numbers from 1 to n, where n is some positive integer greater than one. The trick is that you have to follow the following rules:

  • If a number is divisible by 3, print “Fizz” instead of the number.
  • If a number is divisible by 5, print “Buzz” instead of the number.
  • If a number is divisible by both 3 and 5, print “FizzBuzz” instead of the number.

Once you understand basic for loops and if statements in your programming language of choice, you realize how simple the FizzBuzz problem is. A typical answer looks like this:

def FizzBuzz(n:int):

    for i in range(1,n+1):
        if i % 3 == 0 and i % 5 == 0:
            print("FizzBuzz")
        elif i % 3 == 0:
            print("Fizz")
        elif i % 5 == 0:
            print("Buzz")
        else:
            print(i)
			
if __name__ == '__main__':
    FizzBuzz(100)

I saw a connection between Project Euler Problem 1 and the FizzBuzz problem. To solve Problem 1, I only needed to loop through a range of numbers and add them to a list if it was divisible by 3 or 5. Then, I would return the sum of said list.

def SolveProblem1(TargetNum:int) -> int:
    ThreeAndFiveMultiples = []
    
    # Start at 3 because 1 and 2 will never be included
    for i in range(3,TargetNum): #Note, TargetNum is exclusive
        if i % 3 == 0 or i % 5 == 0:
            ThreeAndFiveMultiples.append(i)
    
    return (sum(ThreeAndFiveMultiples))
    
if __name__ == '__main__':
    print(SolveProblem1(1000))
    # This returns the correct answer - 233168

That was easy.

I submitted my answer to Project Euler and patted myself on the back after seeing a giant green check mark pop up on the screen.

Project Euler has a thread/forum for each problem where users can discuss their answers. However, you can only access the threads once you solve the problem. This prevents people (like me in college) from using the threads to steal hints and answers. Oh, how wonderful college would have been had I had access to the problems’ threads. But sadly, I didn’t have that luxury. Nor did I have AI models like ChatGPT readily available to help solve the advanced problems.

I checked out Problem 1’s thread and saw many answers use a similar for loop logic to mine. But some answers used no for loops or if statements, which confused me. They ran some strange multiplication that magically spit out the correct answer. This intrigued me, so I researched optimized solutions on Google. That’s when I found this article.

Reading the article made me fall in love with math again.

Here’s why…

My solution to Problem 1 was efficient in the scope of the problem, which only required me to iterate through every number between 1 and 1,000 (exclusive). All programming languages can execute 1,000 iterations quickly. That’s why my solution returned the correct answer in a fraction of a second. But it made me wonder how efficient my solution would be if we increased the target number from 1,000 to 1,000,000 or 10,000,000.

So, I ran a little experiment.

I executed my code three times for each target number, timed the executions, and then calculated the average time it took each execution to complete. The results are below.

Target Number Average Execution Time (s)
1,000 0.0001 s
1,000,000 0.1500 s
10,000,000 1.3520 s

Seeing the average execution time increase as the target numbers increased made me curious how long my code would take if the target number equaled 1,000,000,000. When I plugged one billion into my code, it took 259.2416 seconds to execute. I didn’t bother running the code twice more to find the average execution time because I knew it would take just as long as the following two executions.

So, yeah, my brute force logic became inefficient as the target number increased.

The linked article shares a formula so beautiful that it made me smile once I saw it. Legendary mathematician Carl Friedrich Gauss discovered an equation that produces the sum of all integers from 1 to n, where n is a positive integer greater than or equal to 1. What makes the discovery impressive is that he made it as an elementary school student. The guy was a genius and wizard with numbers. Young Mike was good at math, but certainly not as good as Gauss. Read the article if you want a thorough explanation of Gauss’s discovery and how it applies to Project Euler Problem 1.

It turns out all of the answers in Problem 1’s thread that did not use for loops or if statements leveraged Gauss’s formula (also known as Gauss Summation). I learned how the new logic works and enhanced my solution to use Gauss Summation instead of brute force iteration. My new solution looked like this:

def ProblemOneOptimized(Target:int) -> int:

    def SumMultiplesForTarget(n):
        p = (Target - 1) // n
        return x * (p * (p + 1)) // 2

    return SumMultiplesForTarget(3) + SumMultiplesForTarget(5) - SumMultiplesForTarget(15)

The solution is far from intuitive. Without a strong understanding of arithmetic and math, you will never come up with this on your own.

I reran my original test to find the average execution time for this optimized logic, and the results are beautiful.

Target Number Average Execution Time (s)
1,000 0.0000 s
1,000,000 0.0000 s
10,000,000 0.0000 s
1,000,000,000 0.0000 s

The solution computed all of the answers instantly, including one billion! Isn’t that awesome? That’s the beauty of math. By understanding how numbers and equations work, you can solve problems faster and more efficiently. It’s not easy to recognize the mathematical concepts or equations needed to solve problems. They are rarely, if ever, obvious. But once you begin to recognize patterns, you realize how helpful understanding and loving math can be.

Problem 1 taught me a valuable lesson regarding the purpose of Project Euler. The site is not only about solving the problems. Anyone with an elementary programming and math background can solve the problems through brute force. The real purpose of Project Euler is to leverage math to solve the problems efficiently.

The site says that every problem can be solved with code that executes in less than one minute. So while you or I may write a script that takes minutes or hours to execute through brute force, there’s a mathematical method we can use to solve the problem faster. We’re meant to strive for that efficient answer as opposed to the brute force one.

As of writing this, I have completed 13 Project Euler problems (1-8, 11, 12, 14, 16, 17). All are 5% difficulty but felt like they were over 50% difficulty. I will solve more problems over time, and as the difficulty rises, I will have to learn more math to complete the problems the intended way. That will be fun.

Completing so many Project Euler problems inspired me to study and read about math more. It’s a weird thing to hear, I’m sure. Few people can say that they read math books for fun, and it turns out I’m one of them. Last week, I went to the bookstore and bought Haim Shapira’s book Eight Lessons on Infinity: A Mathematical Adventure.

I started to read the book but put it down after six pages. I didn’t do this because the book bored me, and certainly not because the math didn’t interest me. No, no, no. The opposite happened, instead—the book inspired me.

The first math problem introduced in the book regards the Collatz Conjecture. This conjecture states that by repeating two arithmetic operations (multiply by 3 and add 1 if odd or divide by 2 if even) for any positive integer, the process will eventually lead to 1. A basic example is taking the number 16. Since it’s even, we divide by two and end up with 8. That’s also even, so we divide by 2 again, and so on. In total, the following steps occur…

16 → 8 (16 is even so 16/2)
8 → 4 (8 is even so 8/2)
4 → 2 (4 is even so 4/2)
2 → 1 (2 is even so 2/2)

As you see, the conjecture holds true for 16. But since 16 is a power of 2, the answer is obvious. So, let’s do the same with the number 7. We get the following…

7 → 22 (7 is odd so 3 x 7 + 1)
22 → 11 (22 is even so 22/2)
11 → 34 (11 is odd so 3 x 1 + 1)
34 → 17 (34 is even so 34/2)
17 → 52 (17 is odd so 3 x 17 + 1)
52 → 26 (52 is even so 52/2)
26 → 13 (26 is even so 26/2)
13 → 40 (13 is odd so 3 x 13 + 1)
40 → 20 (40 is even so 40/2)
20 → 10 (20 is even so 20/2)
10 → 5 (10 is even so 10/2)
5 → 16 (5 is odd so 3 x 5 + 1)

Oh, look at that. We end up at 16, which we already tested and confirmed, so we know it leads to one. This means the Collatz Conjecture holds true for 7 as well.

These are two examples, but there’s an infinite number of integers. The reason the conjecture is a conjecture, and not a theorem, is because no one has proven the problem to be true. And, at the same time, no one has discovered a number that disproves the problem. Fascinating, right?

I am far from a world-class mathematician, so I will not be the person who proves or disproves the Collatz Conjecture. That’s a truth I can live with. What I can do, however, is validate that the conjecture holds true for all numbers within a specific range. After reading six pages of the book, I felt inspired to do this. So, I put the book down and opened VS Code on my laptop.

Unfortunately, I did not source control my initial script and accidentally rewrote it. In my notes, I wrote that my code took 1.5262 seconds to execute for all numbers less than or equal to 1,000,000 and 11.4754 seconds for all numbers less than 10,000,000. But when I executed the code days later for all numbers less than or equal to 1,000,000, the script took over 60 seconds to execute. What a shame. I have no idea what I changed.

Luckily, I have an optimized function that I recently wrote to help me solve Project Euler Problem 14.

My original logic often processed steps that I had already processed during iterations for previous integers. Take the 16 and 7 example above. We proved that 16 passes the Collatz Conjecture, and while running through the steps for 7, we encountered 16. Since we already knew 16 passed, we concluded that 7 passed the conjecture without continuing the chain past 16. My original logic did not have this intelligence. It could not detect when it encountered numbers in chains that already held true for the conjecture.

Meaning, the original logic was a brute force method that took a while to execute.

To enhance it, I created a set to track all numbers that had already passed the conjecture or were included in chains that had passed it. Using the 7 example again, we see that 52 was one of the steps. And 52 eventually leads to 16, which we know passed the conjecture. So, without a set tracking previously passed numbers, when my original script reached 52, it would have executed all of the steps to verify if 52 passes. But since 52 was involved in the chain for 7, my optimized logic would avoid running the calculations for 52 and continue to the next number.

In the end, my optimized code looked like this:

def CollatzConjecture(TargetNum:int):
    Cache = set()
    for i in range(2,TargetNum+1):
        Curr = i
        if Curr in Cache: # indicates integer already passed or included in chain that passed
            continue 
        CurrChecked = set()
        while Curr != 1:
            if Curr in Cache: # Check if other integers in chain already in cache
                break

            CurrChecked.add(Curr)
            if Curr % 2 == 0:
                Curr //= 2
            else:
                Curr = 3 * Curr + 1

        Cache.update(CurrChecked)

    return f"All integers less than or equal to {TargetNum} passed the Collatz Conjecture"

if __name__ == '__main__':
    print(CollatzConjecture(10000000))

As I continued to read the book, a pattern developed…

I read a few pages, find some interesting concepts, then write a Python script to validate or test the concept. Aside from the Collatz Conjecture, I also did this for finding Amicable Numbers and testing Masahiko Fujiwara’s 1,729 findings. I won’t explain or share my code for these math concepts I translated into code, but perhaps I’ll write more about them in the future.

For now, I have a few things to say before ending this piece…

I fell in love with math again because I fell out of love with it two years ago. I don’t know exactly why that happened, but my hypothesis is that I didn’t see a direct correlation between studying math and furthering my career.

For the last few years, I felt as though everything I studied or learned needed to further my career. That led to me pursuing projects that didn’t interest me, and I would often give up on those before making real progress. But now I realize I do not have to base my decisions on what would be best for my career. Instead, I can (and should) base them on what interests me. Currently, I’m interested in math, programming, and solving difficult problems, hence why I’m excited to write this post.

But I recognize that my interests and obsessions change over time. While I love math now, I may fall out of love with it again. Hopefully that doesn’t happen, but it’s not impossible.

So, moving forward, I plan to continue to study math, specifically the intersection between math and programming. Those are two things that fascinate me most right now.

Again, it may not be what’s best for my career growth, but if it interests me, I’ll pursue it.