Let's start with a function that determines if a number is prime; we will use the Miller-Rabin algorithm, which is faster than trial division, for the size of the numbers with which we will deal:
from random import randint def isPrime(n, k=5): # miller-rabin if n < 2: return False for p in [2,3,5,7,11,13,17,19,23,29]: if n % p == 0: return n == p s, d = 0, n-1 while d % 2 == 0: s, d = s+1, d/2 for i in range(k): x = pow(randint(2, n-1), d, n) if x == 1 or x == n-1: continue for r in range(1, s): x = (x * x) % n if x == 1: return False if x == n-1: break else: return False return True
Since we want to find the first number that satisfies the criteria, our strategy will be to start with 2 and work on us, preserving the results as we progress. We fill the cache (which is a bad pun, sorry) with the first numbers in Collatz sequences from 0 to 2:
primeCount = [0,0,1]
The pCount(n) function counts the primes in the Collatz sequence for n. As soon as the current value of the sequence k falls below n, we look at the result in the cache. Until then, we check every odd number in the Collatz sequence for primitiveness and, if necessary, increase the number p. When we have a primary number for n, we add it to the cache and return it.
def pCount(n): k, p = n, 0 while k > 0: if k < n: t = p + primeCount[k] primeCount.append(t) return t elif k % 2 == 0: k = k / 2 elif isPrime(k): p = p + 1 k = 3*k + 1 else: k = 3*k + 1
Now it's just a matter of calculating simple counts for each n, starting at 3, stopping when the primary count is over 65:
n = 3 t = pCount(n) while t < 65: n = n + 1 t = pCount(n)
It does not take much time, less than a minute on my computer. Here is the result:
print n
The result is 67 primes. If you want to see them, here is a simple function that prints a Collatz sequence for a given n:
def collatz(n): cs = [] while n != 1: cs.append(n) n = 3*n+1 if n&1 else n//2 cs.append(1) return cs
And here is a list of primes:
filter(isPrime,collatz(n))
What a fun problem recreational math!
EDIT:. Since people asked about the Miller-Rabin primitive test, let me show you this simple strength tester based on a 2,3,5-wheel; it performs trial divisions by 2, 3, and 5 and numbers that are not multiples of 2, 3, or 5, including some composites, so it’s a little less efficient than trial divisions by primes, but there’s no need to pre-calculate and store primes, therefore it is much easier to use.
def isPrime(n): # 2,3,5-wheel if n < 2: return False wheel = [1,2,2,4,2,4,2,4,6,2,6] w, next = 2, 0 while w * w <= n: if n % w == 0: return False w = w + wheel[next] next = next + 1 if next > 10: next = 3 return True
The statement filter(isPrime,range(1000000)) identifies 78,498 primes less than a million in a few seconds. You might want to compare the timings for this strength tester versus the Miller-Rabin tester and determine where the intersection point is in terms of execution efficiency; I did not do any formal timings, but it seems to have solved the 65-collatz-prime problem at about the same time as the Miller-Rabin tester. Or you can combine a trial unit with a 2,3,5-wheel to a certain limit, say 1,000 or 10,000 or 25,000, and then run Miller-Rabin on the survivors; which quickly eliminates most composites, so it can work very quickly.