Check if num is anti-prime effectively - python

I am trying to write a predicate which checks if num is anti-prime.
A highly composite number (sometimes referred to as 'anti-prime') is a positive integer with more divisors than any smaller positive integer has.
I came up with this.
def count_divisors(num):
divisor_count = 0
for i in range(1, int(sqrt(num)) + 1):
if num % i == 0:
if num / i == i:
divisor_count += 1
continue
divisor_count += 2
return divisor_count
def is_highly_composite(x):
original_divisors = count_divisors(x)
for i in range(x-1, 0, -1):
if count_divisors(i) >= original_divisors:
return False
return True
Sadly it seems really inefficient and slow. Especially for big numbers.
I am learning coding so efficiency is kinda hard to wrap my head around.
Thanks for any help.

I could think of a bit neater approach to write your function count_divisors(). See the new version named count_divisors_np() below. (gmpy.is_square() allows for fast and accurate calculation for very large numbers, but you may just use divs[-1]**2 == num instead.)
import numpy as np
import gmpy2 as gp
def count_divisors_np(num):
divs = np.arange(1, round(np.sqrt(num)) + 1)
return 2*np.sum(num % divs == 0) - (1 if gp.is_square(num) else 0)
I compared the speeds, and on my PC, the new functions works consistently faster than the old one for numbers above 100,000, while it is almost twice as fast for numbers larger than 250,000. (See the code below that I used to compare the runtimes.)
import time
''' Other imports and definitions of count_divisors() and count_divisors_np()'''
def is_highly_composite(counter_func, x):
original_divisors = counter_func(x)
for i in range(x-1, 0, -1):
if counter_func(i) >= original_divisors:
return False
return True
for i in range(1, 10**3):
tic0 = time.time()
for x in range(i*1000 + 1, (i+1)*1000+1):
is_highly_composite(count_divisors, x)
toc0 = time.time() - tic0
tic1 = time.time()
for x in range(i* 1000 + 1, (i+1) * 1000+1):
is_highly_composite(count_divisors_np, x)
toc1 = time.time() - tic1
if toc1 < toc0:
print('from', i*1000+1, 'to', (i+1)*1000, ':', toc1, '<', toc0)

You can get good performance decomposing the number into its prime factors, applying the rules for a highly composite number.
All primes must be used sequentially at least once
The last prime factor must have a power of 1
The powers of prime factors must be in non-increasing order
If all of the above, check for other combinations of prime factors that can produce a higher (or equal) number of divisors with a smaller product.
Special cases: 4 and 36 are anti-primes
Infinite primes generator (you can make your own as needed, or just use a hard coded list of the first 100 primes):
primes = [2,3]
skips = {9:6}
def getPrimes(count=-1):
yield from primes[:max(0,count) or None]
p = primes[-1]+2
while count:
if p in skips:
mult,step = p,skips.pop(p)
mult += step
while mult in skips: mult += step
skips[mult] = step
else:
skips[p*p] = 2*p
primes.append(p)
yield p
count -= 1
p += 2
Anti-prime checking function:
from math import log
def isAntiPrime(N):
if N in [1,4,36]: return True # special cases
primeGen = getPrimes() # infinite primes generator
factors = [] # prime factors
lastPower = N # to check non-ascending order
divCount = 1 # number of divisors
R = N # reduced N (by factors)
for prime in primeGen: # go through prime factors
factors.append(prime)
if R==1: break # until number exausted
if R%prime: return False # unused prime factor
power = 0 # Count power of prime factor
while R%prime == 0:
power += 1
R //= prime # reduce number as we go
if lastPower<power: return False # increasing order
lastPower = power
divCount *= power+1 # compute number of divisors
if lastPower != 1: return False # last prime's power must be 1
def canReduce(i=0,prod=1,count=1): # recursively look for smaller number
if count>=divCount and prod<N: return True # found one
while i>=len(factors): # load primes list
factors.append(next(primeGen)) # up to index
prime = factors[i] # factor at index
for n in range(1,int(log(N/prod,prime))+1): # try prime^n
prod *= prime
if canReduce(i+1,prod,count*(n+1)): # combined
return True
return not canReduce() # no better power combo -> anti-prime
Output:
i = 0
for n in range(1,1000000):
if isAntiPrime(n):
i += 1
print(f"{i:2}",n)
1 1
2 2
3 4
4 6
5 12
6 24
7 36
8 48
9 60
10 120
11 180
12 240
13 360
14 720
15 840
16 1260
17 1680
18 2520
19 5040
20 7560
21 10080
22 15120
23 20160
24 25200
25 27720
26 45360
27 50400
28 55440
29 83160
30 110880
31 166320
32 221760
33 277200
34 332640
35 498960
36 554400
37 665280
38 720720
Other tests:
for n in [166320,166322,498960,494851, 169733893,
180068938933390833890254309768093,
7, 244877512201, 51983595331405913494380649,
2473017995717899, 252222613252687371148099,
7426227904486559070625807, 24247699, 3463957,
3674876304763078242658251219757, 713928607, 70693, 16807,
24975008738755062001]:
print(isAntiPrime(n),n)
True 166320
False 166322
True 498960
False 494851
False 169733893
False 180068938933390833890254309768093
False 7
False 244877512201
False 51983595331405913494380649
False 2473017995717899
False 252222613252687371148099
False 7426227904486559070625807
False 24247699
False 3463957
False 3674876304763078242658251219757
False 713928607
False 70693
False 16807
False 24975008738755062001
Note I didn't include any timing benchmarks because this is all instantaneous and just with the first 100 primes you can process numbers with up to 190 digits in less than a second
Finding anti-primes
The isAntiPrime function can be adapted to return the largest anti-prime <=N by leveraging its canReduce() function, turning it into an goal seeker from which we can get the largest divisor count with the smallest corresponding number:
def antiPrime(N):
if N in [1,4,36]: return N # special cases
primeGen = getPrimes() # infinite primes generator
factors = [] # prime factors
divCount = 1 # number of divisors
R = N # reduced N (by factors)
for prime in primeGen: # go through prime factors
factors.append(prime)
if R==1: break # until number exausted
if prime*prime>R: # when past square root,
prime = R # R is last prime
power = 0 # Count power of prime factor
while R%prime == 0:
power += 1
R //= prime # reduce number as we go
divCount *= power+1 # compute number of divisors
betterN = 0
def reduce(i=0,prod=1,count=1,lastPower=N): # look for smaller number
nonlocal divCount,betterN
if prod>=N: return
if count>divCount or count==divCount and prod<betterN:
divCount,betterN = count,prod # found better
while i>=len(factors): # load primes list
factors.append(next(primeGen)) # up to index
n,prime = 1,factors[i] # factor at index
while n<=lastPower and prod*prime<N: # try prime^n
prod *= prime
reduce(i+1,prod,count*(n+1),n) # combined
n += 1
reduce()
return betterN or N
Output:
for n in (1000000,713928607,2473017995717899,24975008738755062001,
180068938933390833890254309768093):
print(n,"anti-prime:",antiPrime(n))
1000000 anti-prime: 720720
713928607 anti-prime: 698377680
2473017995717899 anti-prime: 2021649740510400
24975008738755062001 anti-prime: 18401055938125660800
180068938933390833890254309768093 anti-prime: 156839524845080402008057229856000
This one is a bit slower on very large numbers because of the combinatory nature of the process.

Related

How can I determine The numerator and the power from a certain number?

8=2^3, 81=3^4.
How can I discover or find out which/what number can stand as a numerator and as a power for a certain number for example:
8 is the initial/certain number, but was split to 2 raised to the power of 3 where;
2 is the numerator and 3 is the power
Is there a software/algorithm of any sort that can provide an answer to this question for example 64?
You can do it by iterating over powers and finding the highest nth root that produces an integer.
from math import log
def findPower(N):
if N == 0: return (0,1) # support zero
if N < 0: # support negatives
r,p = findPower(-N)
return (-r,p) if p%2 else (N,1)
for power in range(int(log(N,2)),1,-1): # int(log(N,2))=N.bit_length()-1
root = int(N**(1/power))
for r in range(root-1,root+2):
if r**power == N: return (r,power)
return (N,1)
print(findPower(81)) # (3,4)
print(findPower(8)) # (2,3)
print(findPower(371293)) # (13,5)
print(findPower(29**7)) # (29,7)
print(findPower(-232630513987207)) # (-7, 17)
Note that this returns (n,1) for n = -1, 0 or 1 even though they all have an infinite number of solutions. It also returns only the positive base for even powers.
[EDIT] the above function is limited by the capabilities of floating point number representation. It will choke on very large integers.
Here is an alternative approach that supports Python's "infinite size" integers, using binary search for nth roots calculations and optimized using prime factors of the resulting exponent.
integer root calculation
# integer nth root of number X, Returns None if no exact root
rootMod7 = { p:{d**p%7 for d in range(1,8)} for p in range(1,7) }
def intRoot(X,n):
if n==1 or X==1: return X
odd,bits = X&1, X.bit_length() # odd/even and bit magnitude
if X%7 not in rootMod7[(n-1)%6+1]: return # mod 7 match possible roots
lo,hi = 1<<(bits//n),1<<(bits//n+1) # starting range on log(X,n)
while lo<=hi:
root = (lo+hi)//2 # binary search
if root&1 != odd: root += root < hi # odd/even must match X's
delta = X - root**n
if delta == 0: return root
if delta<0 : hi = root - 2 # adjust range
else: lo = root + 2 # on odd/even boundaries
return None
Prime number generator
def quickPrimes(N):
isPrime = [False,True]*(N//2+1)
primes = [2]
for p in range(3,N+1,2):
if not isPrime[p]: continue
primes.append(p)
isPrime[p*p:N+1:p] = (False for _ in range(p*p,N+1,p))
return primes
Solution for huge numbers
# finds base and power where base**power == N
def basePower(N):
base,power = (N,1) if N>=0 else basePower(-N)
if N<1: return (-base,power) if power%2 else (N,1)
maxPower = N.bit_length()
for p in reversed(quickPrimes(maxPower)):
if p>maxPower: continue
root = intRoot(base,p)
while root: # prime factorisation of exponents
maxPower = maxPower//p + 1
base,power = root,power*p
root = intRoot(base,p)
return base,power
This version can process huge numbers in a reasonable amount of time.
for example:
basePower(1522756**5553) # (1234, 11106) in 46 seconds, 34,333 digits
basePower(12345**12345) # (12345,12345) in 159 seconds, 50,510 digits
[EDIT2] A much better solution using prime factorisation:
You can find prime factors and take the greatest common denominator (gcd) of prime counts.
For example 216000's prime factors are 2^6, 3^3, 5^3 so the power will be 3. For each of the primes keep count/3 as the power to compute the base: 2^2 * 3^1 * 5^1 = 60. So 216000 = 60^3
def primeFactors(N): # returns dictionary of {prime:count}
result = dict()
p = 2 # p is a candidate prime factor
while p*p<=N: # prime candidates up to √N (remaining N)
while N%p == 0: # count prime factors
result[p] = result.get(p,0)+1
N //= p # by removing factors, only primes will match
p += 1 + (p&1) # next potential prime
if N>1: result[N] = 1 # anything remaining after √N is a prime
return result
def gcd(a,b=0,*c): # gcd using Euclid's algorithm
if c: return gcd(gcd(a,b),*c) # for multiple values
return a if not b else gcd(b,a%b) # Euclidian division version
def findPower(N):
counts = primeFactors(N) # {prime factor: count}
power = gcd(*counts.values()) # power is gcd of prime counts
base = 1
for f,c in counts.items(): # compute base
base *= f**(c//power) # from remaining prime powers
return base,power
This is much faster that the previous large-integer solution:
findPower(12345**12345)) # (12345,12345) in 2.8 seconds
A prime factor decomposition will give you the expected result. Demo:
def erato(n):
"""Search primes up to n using an Eratosthene's sieve"""
is_prime = [1] * n # expect all to be primes
for i in range(2,n): # and remove any multiple starting with 2
if is_prime[i] != 0: # but ignoring non primes
for j in range(i*i, n, i):
is_prime[j] = 0 # a multiple is not prime
return [i for i in range(2, n) if is_prime[i] != 0]
def decomp(n):
s = int(math.sqrt(n) + 1.5) # extract primes list up to sqrt(n)
primes = erato(s)
factors = []
for i in primes:
if i * i > n: # stop at sqrt(n)
break
q, r = divmod(n, i)
if r == 0: # found a divisor
p = 1
n = q
while True: # compute the exponent
q, r = divmod(n, i)
if r == 0:
p += 1
n = q
else:
break
factors.append((i, p)) # store the pair divisor, exponent
return factors
>>> decomp(8)
[(2, 3)]
>>> decomp(81)
[(3, 4)]

Sum the factors of a number (excluding the number itself)

After I edited my code, I understand how to do it:
def sum_factors(n):
sum = 0
factor = 1
# Return the sum of all factors of n, not including n
while n!=0 and factor < n:
if n % factor == 0:
sum = sum + factor
factor = factor + 1
else:
factor = factor + 1
return sum
My initial code could not sum the factors but I don't understand why:
def sum_divisors(n):
sum = 0
factor = 1
# Return the sum of all divisors of n, not including n
while n % factor == 0 and factor < n:
sum = sum + factor
factor = factor + 1
return sum
Could you please explain why my initial code didn't work?
Your while loop writes
while n % factor == 0 and factor < n:
This would mean that once n % factor test condition is false, the code will break out of the while loop.
Let's use 10 as an example.
10 % 1 equals zero and 10 % 2 equals 0. But 10 % 3 does not equal 0. This means we break out of the while loop once factor gets to 3.
The answer we get would then be the sum of 1 and 2 which would be incorrect.
The initial code put a strong emphasize on the condition while n % factor == 0 which is not a necessary condition to end the while loop.
Sometimes, it helps to add some debugging output to a Python program:
def sum_divisors(n):
sum = 0
factor = 1
# Return the sum of all divisors of n, not including n
print(f"factor = {factor}")
while n % factor == 0 and factor < n:
sum = sum + factor
factor = factor + 1
print(f"factor = {factor}")
return sum
print(sum_divisors(30))
Output
factor = 1
factor = 2
factor = 3
factor = 4
6
Notes
As we can see, the while loop exits when it finds a number that is not a factor.
Refactoring
I noticed that you have duplicate code in your if-else block. Whenever this happens, you can move the common code outside:
if n % factor == 0:
sum = sum + factor
factor = factor + 1

What's the most efficient way to find Wilson Prime number using python?

I have written a code of Wilson prime numbers and my code is working for most of the numbers but it's giving OverflowError: int too large to convert to float for very large numbers. Is there any way to write Wilson prime number code for very large numbers.
The main problem is for checking Wilson prime Wilson primes it should satisfy the following condition. Where P represents a prime number.
Then ((P-1)! + 1) / (P * P) should give a whole number.
And as you can see factorials are involved in this procedure, so for very large numbers it's pretty difficult.
My Code :
def am_i_wilson(n):
import math
n1 = math.sqrt(n)
n1 = math.ceil(n1)
c = 0
def fact(n):
num = 1
for i in range(2,n+1):
num = num*i
return num
if n <= 1:
return False
for i in range(2, n1 + 1):
if n%i == 0:
c+ = 1
if c != 0:
return False
x = (fact(n-1)+1)/((n**2)*1.0)
return x.is_integer()
In my code, I am returning True if the number is Wilson Prime else False. Here n is the number to check if it's Wilson prime or not.
I think this is the most efficient way
import math
def am_i_wilson(num):
if num < 2 or not all(n % i for i in range(2, num)):
return False
return (math.factorial(num - 1) + 1) % (num ** 2) == 0
or you can try this too
import math
def am_i_wilson(n):
if n <= 2:
return False
fact=math.factorial(n-1)
#this conditional checks that the number is prime or not
#this condition is called wilson theorem in number theory
if (fact+1)%n==0:
x = (fact+1)%(n**2)
if x==0:
return True
else:
return False
else:
return False
if anyone has any better method then please answer it.
Your program primarily relies on testing for primes and computing factorials. You separate out the factorial logic but embed an inefficient prime test -- it keeps testing remainders after it knows the number isn't prime! I would separate both out so that they can be tested and optimized independently of the Wilson prime test itself:
def factorial(n):
number = 1
for i in range(2, n + 1):
number *= i
return number
def am_i_prime(n):
if n < 2:
return False
if n % 2 == 0:
return n == 2
for divisor in range(3, int(n ** 0.5) + 1, 2):
if n % divisor == 0:
return False
return True
def am_i_wilson(n):
return am_i_prime(n) and (factorial(n - 1) + 1) % n ** 2 == 0
A more efficient approach, given a fixed target to test up to, would be to implement a prime sieve and for each prime encountered, while you're computing the sieve, test if it's a Wilson prime.
I've been experimenting with prime sieves recently. I did a quick modification (i.e. hack) to one of them written by Robert William Hanks and came up with this. Output first:
$ ./wilson_primes.py 10000
[5, 13, 563]
...so I suspect the Wikipedia article about Wilson primes is correct ;-)
import sys
def fact(n):
num = 1
for i in range(2, n+1):
num *= i
return num
def is_wilson(n):
return (fact(n-1)+1) % n**2 == 0
def rwh_primes1(n):
""" Returns a list of primes < n """
sieve = [True] * (n/2)
for i in range(3,int(n**0.5)+1,2):
if sieve[i/2]:
sieve[i*i/2::i] = [False] * ((n-i*i-1)/(2*i)+1)
# return [2] + [2*i+1 for i in xrange(1,n/2) if sieve[i]]
for i in range(1,n/2):
if sieve[i]:
p = 2*i + 1 # convert index to normal prime
if is_wilson(p): #
yield p #
if len(sys.argv) > 1:
N = int(float(sys.argv[1]))
else:
N = 10000 # default: 1e4 10,000
print [p for p in rwh_primes1(N)]
First I tried just the fact() function and was pleasantly surprised to see it can produce huge results. But it is very slow compared to the original prime sieve. Perhaps it could be made to run faster by remembering the last factorial computed and re-use that to skip part of next factorial computation.
EDIT
I changed fact() to remember its last result, as follows:
last_fact = 1
last_n = 1
def fact2(n):
global last_fact, last_n
num = last_fact
for i in range(last_n+1, n+1):
num *= i
last_n = n
last_fact = num
return num
def is_wilson(n):
return (fact2(n-1)+1) % n**2 == 0
That did speed it up quite a bit. cProfile shows that is_wilson() is now the bottleneck. I can't think of an easy way to make it faster.

Fastest way of testing if a number is prime? [duplicate]

This question already has answers here:
Fast prime factorization module
(7 answers)
Closed 5 years ago.
I'm trying to get a fast way to determine if a number is prime using Python.
I have two functions to do this. Both return either True or False.
Function isPrime1 is very fast to return False is a number is not a prime. For example with a big number. But it is slow in testing True for big prime numbers.
Function isPrime2 is faster in returning True for prime numbers. But if a number is big and it is not prime, it takes too long to return a value. First function works better with that.
How can I come up with a solution that could quickly return False for a big number that is not prime and would work fast with a big number that is prime?
def isPrime1(number): #Works well with big numbers that are not prime
state = True
if number <= 0:
state = False
return state
else:
for i in range(2,number):
if number % i == 0:
state = False
break
return state
def isPrime2(number): #Works well with big numbers that are prime
d = 2
while d*d <= number:
while (number % d) == 0:
number //= d
d += 1
if number > 1:
return True
else:
return False`
Exhaustive division until the square root is about the simplest you can think of. Its worst case is for primes, as all divisions must be performed. Anyway, until a billion, there is virtually no measurable time (about 1.2 ms for 1000000007).
def FirstPrimeFactor(n):
if n & 1 == 0:
return 2
d= 3
while d * d <= n:
if n % d == 0:
return d
d= d + 2
return n
Note that this version returns the smallest divisor rather than a boolean.
Some micro-optimizations are possible (such as using a table of increments), but I don' think they can yield large gains.
There are much more sophisticated and faster methods available, but I am not sure they are worth the fuss for such small n.
Primality tests is a very tricky topic.
Before attempting to speed up your code, try to make sure it works as intended.
I suggest you start out with very simple algorithms, then build from there.
Of interest, isPrime2 is flawed. It returns True for 6, 10, 12, ...
lines 3 to 6 are very telling
while d*d <= number:
while (number % d) == 0:
number //= d
d += 1
When a factor of number d is found, number is updated to number = number // d and at the end of the while loop, if number > 1 you return True
Working through the code with number = 6:
isPrime2(6)
initialise> number := 6
initialise> d := 2
line3> check (2 * 2 < 6) :True
line4> check (6 % 2 == 0) :True
line5> update (number := 6//2) -> number = 3
line6> update (d : d + 1) -> d = 3
jump to line3
line3> check (3 * 3 < 3) :False -> GOTO line7
line7> check(number > 1) -> check(3 > 1) :True
line8> return True -> 6 is prime
Here is what I came up with
def is_prime(number):
# if number is equal to or less than 1, return False
if number <= 1:
return False
for x in range(2, number):
# if number is divisble by x, return False
if not number % x:
return False
return True

Get combination of factors product of a given number

I am not sure whether this question was posted before, after searching it, I cannot find it.
Question: Give one number, to print all factor product.
Example:
Given number: 20
Output: 1 * 20
2 * 10
2 * 2 * 5
4 * 5
Given number: 30
Output: 1 * 30
2 * 15
2 * 3 * 5
3 * 10
5 * 6
Here are my thoughts:
Solution 1.
step 1) First, get all prime factors of this number
def get_prime_factors(n):
factors = []
if n == 0:
return factors
# Get the number of 2s that divide n
while n%2 == 0:
factors.append(2)
n /= 2
# n must be odd
for i in range(3, int(ceil(sqrt(n))), 2):
while n%i == 0:
factors.append(i)
n /= i
# handle the case n is prime number greater than 2s
if n > 2:
factors.append(n)
return factors
step 2) Then get the combination of those factors
I plan to get all factor product through combination, however, I am stuck in how to handle those duplicate factors in this case? (question 1)
Solution 2:
Solve it through backtracking method.
def get_factors_recv(n, cur_ret, ret):
for i in range(2, int(ceil(sqrt(n)))):
if n%i == 0:
fact_arr = [i, n/i]
# add the current value to current result
cur_ret.extend(fact_arr)
if sorted(cur_ret) not in ret:
ret.append(sorted(cur_ret))
# backtracking
cur_ret = cur_ret[:-2]
get_factors_recv(n/i, cur_ret + [i], ret)
def get_all_factors_product(n):
if n == 0:
return '';
result = []
# push the simple factor multiplier
result.append([1, n])
get_factors_recv(n, [], result)
return result
I want to know is there any optimization for the above codes? (Question 2)
Is there any better solution to solve it? (Question 3)
A simple while loop can solve your first problem of dupicates. Given a number:
num_list = []
i = 2;
num = 72*5*5*19*10
while i &lt=num:
if(num%i == 0):
num_list.append(i)
num = num/i
else:
i = i + 1
print num_list
num_list will contain the factors. The idea is to not increase the index variable untill the number is no longer divisible by it. Also the number keeps reducing after every division so the loop will actually run a lot less iterations than the actual number. Instead of
while i&lt=num
you can also use
while i&lt=num/2
This is correct mathematically and results in further reduction of no of iterations.
This will give you all the factors.
Hope this helps.
number = 30
factors = []
for i in range(1, number+1):
if number%i == 0:
factors.append(i)
print factors

Categories