I've written a small script to calculate Collatz sequences. For background, read here about the Collatz conjecture. Largest number I've tried so far in my implementation is 93571393692802302 (which wikipedia claims is the number requiring most steps below 10e17), which needs 2091 steps. Apparently I count different from Wikipedia, as my len(collatz(93571393692802302))
is 2092 long, but I include the number itself as well as the final 1 .
A Collatz sequence takes a number, and calculates the next number in sequence as following:
- If it is even, divide by two
- If it is odd, triple and add one.
The Collatz Conjecture claims that all sequences created this way converge to 1. It is unproven, but it seems most mathematicians suspect it's True.
Without further ado, the code:
from typing import List
collatz_cache = {}
def _calc(number: int) -> int:
"""
Caches this number in the collatz_cache, and returns the next in sequence.
:param number: Integer > 1.
:return: Integer > 0
"""
if number % 2 == 0:
next_number = number // 2
else:
next_number = number * 3 + 1
collatz_cache[number] = next_number
return next_number
def collatz(number: int) -> List[int]:
"""
Returns the collatz sequence of a number.
:param number: Integer > 0
:return: Collatz sequence starting with number
"""
if not isinstance(number, int):
raise TypeError(f"Collatz sequence doesn't make sense for non-integers like {type(number).__name__}")
if number < 1:
raise ValueError(f"Collatz sequence not defined for {type(number).__name__}({number})")
new_number = number
result = [number]
while new_number not in collatz_cache and new_number != 1:
new_number = _calc(new_number)
result.append(new_number)
while result[-1] != 1:
result.append(collatz_cache[result[-1]])
return result
I've tried to minimize calculation time in repeated attempts by creating a mapping for each number to the next number in the sequence. First I just mapped to the full list, but that would make the dictionary a bit bigger than I really wanted.
I feel that there should be some gains to be made in the list construction, but I'm at loss as to the how.
Purpose of this code is to basically be a general library function. I want to:
- Be fast
- Be memory efficient with my cache
- Handle multiple equal/partially overlapping sequences
- Handle completely different sequences
And all of that at the same time. Any code stylistic improvements are of course welcome, but any suggestions improving the above goals without disproportional disadvantage to the others are also welcome.
main
function demonstrating what you program actually does? It is for computing the Collatz sequence for a single number? Or does it compute many Collatz sequences? Or does it try to find the longest sequence (in some range)? \$\endgroup\$