I am looking for an algorithm that could factorize numbers based on numbers it already factorized. In other words, I am searching for a fast algorithm to factorise all numbers up to a given number, and store them in a (I guess this is the easiest data structure to use) list / tuple of tuples. I am looking for an "up to n" algorithm because I need all numbers up to "n", and I guess it's faster than just checking one by one.
I want this algorithm to work within a reasonable time (less than an hour) for 2*10^8, for a program I am running. I have tried one of the more naive approaches in python, finding all primes up to "n" first, and then for each number "k" finding it's prime factorization by checking each prime until one divides it (we will call it p), then it's factorization is the factorization of k/p + p.
from math import *
max=1000000 # We will check all numbers up to this number,
lst = [True] * (max - 2) # This is an algorithm I found online that will make the "PRIMES" list all the primes up to "max", very efficent
for i in range(2, int(sqrt(max) + 1)):
if lst[i - 2]:
for j in range(i ** 2, max, i):
lst[j - 2] = False
PRIMES = tuple([m + 2 for m in range(len(lst)) if lst[m]]) # (all primes up to "max")
FACTORS = [(0,),(1,)] #This will be a list of tuples where FACTORS[i] = the prime factors of i
for c in range(2,max): #check all numbers until max
if c in PRIMES:
FACTORS.append((c,)) #If it's a prime just add it in
else: #if it's not a prime...
i=0
while PRIMES[i]<= c: #Run through all primes until you find one that divides it,
if c%PRIMES[i] ==0:
FACTORS.append(FACTORS[c//PRIMES[i]] + (PRIMES[i],)) #If it does, add the prime with the factors of the division
break
i+=1
From testing, the vast majority of time is wasted on the else section AFTER checking if the candidate is prime or not. This takes more than an our for max = 200000000
P.S. - WHAT I'M USING THIS FOR - NOT IMPORTANT
The program I am running this for is to find the smallest "n" such that for a certain "a" such that (2n)!/((n+a)!^2) is a whole number. Basically, I defined a_n = smallest k such that (2k)!/((k+n)!^2) is an integer. turns out, a_1 =0, a_2 = 208, a_3 = 3475, a_4 = 8174, a_5 = 252965, a_6 = 3648835, a_7 = 72286092. By the way, I noticed that a_n + n is squarefree, although can't prove it mathematically. Using Legendre's formula: https://en.wikipedia.org/wiki/Legendre%27s_formula, I wrote this code:
from math import *
from bisect import bisect_right
max=100000000 # We will check all numbers up to this number,
lst = [True] * (max - 2) # This is an algorithm I found online that will make the "PRIMES" list all the primes up to "max", very efficent
for i in range(2, int(sqrt(max) + 1)):
if lst[i - 2]:
for j in range(i ** 2, max, i):
lst[j - 2] = False
PRIMES = tuple([m + 2 for m in range(len(lst)) if lst[m]]) # (all primes up to "max")
print("START")
def v(p,m):
return sum([ (floor(m/(p**i))) for i in range(1,1+ceil(log(m,p)))]) #This checks for the max power of prime p, so that p**(v(p,m)) divides factorial(m)
def check(a,n): #This function checks if a number n competes the criteria for a certain a
if PRIMES[bisect_right(PRIMES, n)]<= n + a: #First, it is obvious that if there is a prime between n+1 and n+a the criteria isn't met
return False
i=0
while PRIMES[i] <= n: #We will run through the primes smaller than n... THIS IS THE ROOM FOR IMPROVEMENT - instead of checking all the primes, check all primes that divide (n+1),(n+2),...,(n+a)
if v(PRIMES[i],2*n)<2*v(PRIMES[i],n+a): # If any prime divides the denominator more than the numerator, the fraction is obviously not a whole number
return False
i+=1
return True #If for all primes less than n, the numerator has a bigger max power of p than the denominator, the fraction is a whole number.
#Next, is a code that will just make sure that the program runs all numbers in order, and won't repeat anything.
start = 0 #start checking from this value
for a in range(1,20): #check for these values of a.
j=start
while not check(a,j):
if j%100000==0:
print("LOADING ", j) #just so i know how far the program has gotten.
j+=1
print("a-",a," ",j) #We found a number. great. print the result.
start=j #start from this value again, because the check obviously won't work for smaller values with a higher "a"