Implementing memoization within a Collatz algorithm (Python) - python

I am trying to perform a Collatz algorithm on the following code. It works fine when I use a range of 1-10 etc... However, if the range is for example 1-500,000 it's too slow and won't ever show me the output of the longest sequence.
numberArray = []
s=int(1)
f=int(10)
def collatz(n):
global count
if n == 1:
count += 1
numberArray.append(count)
return True
elif (n % 2) == 0:
count += 1
collatz(n/2)
else:
count += 1
collatz(3*n+1)
for i in range (s, f+1):
count = 0
ourNumber = i
collatz(i)
print(max(numberArray))

Stef means something like this, which uses a dictionary to memorise the values that have already been counted:
s = 1
f = 10000000
def collatz(n):
if n in collatz.memory:
return collatz.memory[n]
if (n % 2) == 0:
count = collatz(n//2)+1
else:
count = collatz((3*n+1)//2)+2
collatz.memory[n] = count
return count
collatz.memory = {1:0}
highest = max(collatz(i) for i in range(s, f+1))
highest_n = max(collatz.memory, key=collatz.memory.get)
print(f"collatz({highest_n}) is {highest}")
Output:
collatz(8400511) is 685

Use lru_cache decorator. Its function to memorize (cache) the returned value of function called with specific argument.
Also read how to write clean code in python
The next code solves your problem
from functools import lru_cache
number_array = []
s = 1
f = 500000
#lru_cache
def collatz(n: int):
if n == 1:
return 1
elif n % 2 == 0:
return 1 + collatz(n // 2)
else:
return 1 + collatz(3 * n + 1)
for i in range(s, f + 1):
number_array.append(collatz(i))
print(number_array)

Related

Can't get out of While loop(Python 3.9)

I'm a new at programming, I like solving this euler questions and I know there are solutions for this problem but it's not about the problem at all actually.
So, i managed to create a working function for finding example: 33. triangular number. It works but i couldn't manage to properly desing my while loop. I wanted to make it like, it starts from first triangular checks it's divisors make list of it's divisors, checks the length of the divisors, because problem wants "What is the value of the first triangle number to have over five hundred divisors?" . But i never managed to work the while loop. Thank you for reading.
nums = [1]
triangles = [1]
divisors = []
def triangularcreator(x):
if x == 1:
return 1
n = 1
sum = 0
while n!=0:
n += 1
nums.append(n)
for i in range(len(nums)):
sum += nums[i]
triangles.append(sum)
sum = 0
if x == len(triangles):
n = 0
return triangles[-1]
counter = 1
while True:
for i in range(1, triangularcreator(counter) + 1):
if triangularcreator(counter) % i == 0:
divisors.append(i)
if len(divisors) == 500:
print(triangularcreator(counter))
break
counter +=1
divisors.clear()
You should try to change a few things, starting with calculating just once the value of triangularcreator(counter) and assigning this value to a variable that you can use in different points of your code.
Second, you create a loop which will be calculate always triangularcreator(1). At the end of each iteration you increase the value of counter+=1, but then at the beginign of the new iteration you assignt it again value 1, so it will not progress as you expect. Try this few things:
counter = 1
while True:
triangle = triangularcreator(counter)
for i in range(1, triangle + 1):
if triangle % i == 0:
divisors.append(i)
if len(divisors) == 500:
print(triangle )
break
counter +=1
Also these two arrays nums = [1], triangles = [1] should be declared and initialized inside the def triangularcreator. Otherwise you would be appending elements in each iteration
Edit: I believe it is better to give you my own answer to the problem, since you are doing some expensive operations which will make code to run for a long time. Try this solution:
import numpy as np
factor_num = 0
n = 0
def factors(n):
cnt = 0
# You dont need to iterate through all the numbers from 1 to n
# Just to the sqrt, and multiply by two.
for i in range(1,int(np.sqrt(n)+1)):
if n % i == 0:
cnt += 1
# If n is perfect square, it will exist a middle number
if (np.sqrt(n)).is_integer():
return (cnt*2)-1
else:
return (cnt*2)-1
while factor_num < 500:
# Here you generate the triangle, by summing all elements from 1 to n
triangle = sum(list(range(n)))
# Here you calculate the number of factors of the triangle
factor_num = factors(triangle)
n += 1
print(triangle)
Turns out that both of your while loop are infinite either in triangularcreatorin the other while loop:
nums = [1]
triangles = [1]
divisors = []
def triangularcreator(x):
if x == 1:
return 1
n = 1
sum = 0
while n:
n += 1
nums.append(n)
for i in range(len(nums)):
sum += nums[i]
triangles.append(sum)
sum = 0
if len(triangles) >= x:
return triangles[-1]
return triangles[-1]
counter = 1
while True:
check = triangularcreator(counter)
for i in range(1, check + 1):
if check % i == 0:
divisors.append(i)
if len(divisors) >= 500:
tr = triangularcreator(counter)
print(tr)
break
counter +=1
Solution
Disclaimer: This is not my solution but is #TamoghnaChowdhury, as it seems the most clean one in the web. I wanted to solve it my self but really run out of time today!
import math
def count_factors(num):
# One and itself are included now
count = 2
for i in range(2, int(math.sqrt(num)) + 1):
if num % i == 0:
count += 2
return count
def triangle_number(num):
return (num * (num + 1) // 2)
def divisors_of_triangle_number(num):
if num % 2 == 0:
return count_factors(num // 2) * count_factors(num + 1)
else:
return count_factors((num + 1) // 2) * count_factors(num)
def factors_greater_than_triangular_number(n):
x = n
while divisors_of_triangle_number(x) <= n:
x += 1
return triangle_number(x)
print('The answer is', factors_greater_than_triangular_number(500))

Create a mathematical sequence

i need to create a mathematical sequence of type:
P(n)= P(n-1) + P(n-2)
We know that P(0)=0 and P(1)=1
I really struggle creating a sequence, i tried to create it, here is some of what I’ve written...
def fibonacci(n):
number = 0
if n == 0:
number = 0
return (number)
if n == 1:
number = 1
return (number)
if n > 1:
def compute(limit):
for x in range(2, limit):
fibonacci(x) = fibonacci(x-1) + fibonacci(x-2)
for i in range(0, n):
number += compute(i)
First: you don't need to use brackets when you use 'return' statement
Solution:
def fibonacci(n):
number = 0
if n == 0:
number = 0
return number
if n == 1:
number = 1
return number
if n > 1:
return fibonacci(n-1)+fibonacci(n-2)
This code works fine, but u can calculate fibonacci more effective if u will use dynamic programming
Example:
fibonacci = [0,1]
for i in range(2,n+1):
fibonacci.append(fibonacci[i-1]+fibonacci[i-2])
print(fibonacci[-1])

How to reduce a lot of "if" statements

def tribonacci(signature, n):
f = 0
if n == 0:
return []
if n == 1:
return [signature[0]]
if n == 2:
return [signature[0], signature[1]]
while len(signature) != n:
i = signature[0 + f] + signature[1 + f] + signature[2 + f]
signature.append(i)
f += 1
return signature
That's a Tribonacci(same with Fibonacci but with 3 numbers) code from codewars.com , I know that it could be more beautiful and elegant but i want to know how to reduce this particular part:
if n == 0:
return []
if n == 1:
return [signature[0]]
if n == 2:
return [signature[0], signature[1]]
Thanks!
You can see in your if statements, when n==0 return is empty list, for n==1, return one item in a list and also for n==2 return two items in a list.
So for that you can do in a one if statement as below:
if 0 <= n < 3:
return list(signature[:n])
And here is your full code for your problem.
def tribonacci(signature, n):
#your code here
f = 0
if 0 <= n < 3:
return list(signature[:n])
while len(signature) != n:
i = signature[0 + f] + signature[1 + f] + signature[2 + f]
signature.append(i)
f += 1
return signature
The 3 cases 0, 1, 2 can be reduced to one, because all return the signature list until the given n
if n < 3: # if 0 <= n < 3: can be used for satefy
return signature[:n]

Iteration vs recursion when dynamic lookup table is required

I was wondering if iteration can be used when using a dynamic lookup table. Using recursion you can get all the necessary lookup table data from bottom to top. I can't seem to grasp how we can achieve the same using iteration, without making a stack and ending up reimplementing the recursion.
For example, take my solution to Project Euler problem #14:
table = {1:1}
def collatz(n):
if n in table:
return table[n]
elif n % 2:
x = 1 + collatz(3*n + 1)
else:
x = 1 + collatz(n/2)
table[n] = x
return x
MAXc = 0
for i in xrange(1,1000000):
temp = collatz(i)
if temp > MAXc:
MAXc = temp
result = i
print result
How could we implement the same using iteration?
An iterative algorithm which just counts the number of steps until reaching 1 is trivial. The problem is to also update the cache for all the intermediate values.
In this particular case there is an iterative algorithm which doesn't require an explicit stack. It uses two passes: the first counts the total number of steps, and the second updates the cache.
def next(n):
if n % 2 != 0:
return 3*n + 1
else:
return n/2
def collatz(n):
count = 0
i = n
while i not in table:
count += 1
i = next(i)
count += table[i]
i = n
while i not in table:
table[i] = count
count -= 1
i = next(i)
return table[n]
How about something like (built code on fly, so sorry for any typos/bugs):
def collatz_generator(n):
while n != 1:
n = n & 1 and (3 * n + 1) or n / 2
yield n
def add_sequence_to_table(n, table):
table = table or {1:1}
sequence = list(collatz_generator(n))
reversed = list(enumerate(sequence))[::1]
for len, num in reversed:
if num in table:
break
table[n] = len + 1
return table
def build_table(n):
table = add_sequence_to_table(2)
for n in xrange(3, n):
table = add_sequence_to_table(n, table)
return table
Without building table (typed on fly as wife wants me to get read):
def without_table(n):
max_l, examined_numbers = 0, set()
for x in xrange(2, n):
reversed = list(enumerated(collatz_generator(x)))[::-1]
for num, length in reversed:
if num in examined_numbers:
break
examined_numbers.add(num)
if num <= n: # I think this was a problem requirement.
max_l = max(max_l, length)
Doesn't this work?

Looking for values in Python Dictionaries

I'm trying to memoize divisor sums of numbers.
divisorSums = {}
def sumDivisors(num):
global divisorSums
total = 0
if num == 1:
return 0
for i in xrange(num/2, 0, -1):
if i in divisorSums:
return divisorSums[i]
else:
if not num % i:
total += i
divisorSums[num] = total
return total
However, this returns 1 for all numbers, when I loop through the numbers. It is correct when it used singularly, so the problem is my lookup system. I'm pretty sure I don't understand how to look for a value in a dictionary. Can someone help me out?
The usual shortcut trick for memoize is to use a mutuable default argument
def sumDivisors(num, divisorSums={}):
if num in divisorSums: # Check if you have
return divisorSums[num] # memoized the answer here
total = 0
if num == 1:
return 0
for i in xrange(num/2, 0, -1):
if not num % i:
total += i
divisorSums[num] = total
return total
Other than that your code seems to work ok. How were you runnning it to get just 1?
Here:
if i in divisorSums:
return divisorSums[i]
returning divisorSums[i] is not the right thing to do as there may be some prime factors less than i. Here is one way using a helper function to remember/calculate factors of numbers:
from itertools import groupby
divisorC={}
def divisors(num):
if num in divisorC:
return divisorC[num]
l = {1:1}
for i in xrange(num/2, 1, -1):
if num % i == 0:
l[i] = 1
l.update(divisors(i))
l.update(divisors(num/i))
break
divisorC[num] = l
return divisorC[num]
def sumDivisors(num):
if num == 1: return 0
l = {}
for i in xrange(num/2, 0, -1):
if num % i == 0:
l.update(divisors(i))
l.update(divisors(num/i))
l[i] = 1
l[num/i] = 1
break
return sum(v for v in l)

Categories

Resources