How to find the nearest prime number in an array, to another number in that array?
Solution 1:
First, you need a good prime number checker. Wikipedia has an implementation -- It could probably be optimized a bit further depending on python version, etc.
Now, make a list of the indices of all prime numbers:
indices = [i for i, val in enumerate(data) if is_prime(val)]
Next, pick an arbitrary number and find it's index (or not arbitrary ...).
n = random.choice(data)
idx = data.index(n)
we're almost there ... bisect your way to figure out where the index of n
fits in the indices list.
indices_idx = bisect.bisect_left(indices, idx)
Now, to figure out whether the closer number is on the left or the right we need to look at the values.
# Some additional error handling needs to happen here to make sure that the index
# actually exists, but this'll work for stuff in the center of the list...
prime_idx_left = indices[indices_idx - 1]
prime_idx_right = indices[indices_idx]
and finally, figure out which index is closer and pull out the value:
if (idx - prime_idx_left) <= (prime_idx_right - idx):
closest_prime = data[prime_idx_left]
else:
closest_prime = data[prime_idx_right]
Note I cooked this up under the assumption that you'll be using the same list over and over. If you're not, you'd do better to just:
- find the index of the number you're interested in.
- find the index of the first prime to the right (if it exists)
- find the index of the first prime to the left (if it exists)
- Check which one is closer
e.g.
def find_idx_of_prime(lst, start_idx, stop_idx, dir):
for ix in xrange(start_idx, stop_idx, dir):
if is_prime(lst[ix]):
return ix
return dir*float('inf')
idx = data.index(number)
left_idx = find_idx_of_prime(data, idx, 0, -1)
right_idx = find_idx_of_prime(data, idx, len(data), 1)
prime_idx = left_idx if idx - left_idx < right_idx - idx else right_idx
prime_value = data[prime_idx] # raises TypeError if no primes are in the list.
Solution 2:
Below is a fairly efficient implementation of the Sieve of Eratosthenes that could be used in conjunction with mgilson's code. But as J.F. Sebastian says, using a pre-computed table of primes may not be efficient if the numbers in your list are very large, &/or if the length of the list is small.
def primes(n):
''' Return a boolean list of all primes < n '''
s = [False]*2 + [True]*(n-2)
for i in xrange(2, int(n**0.5) + 1):
if s[i]:
s[i*i : n : i] = [False] * (1 + (n - 1)//i - i)
return s
You'd use it like this:
a = [1,2,4,6,8,12,9,5,0,15,7]
is_prime = primes(max(a) + 1)
And then change mgilson's find_idx_of_prime()
function to:
def find_idx_of_prime(lst, start_idx, stop_idx, dir):
for ix in xrange(start_idx, stop_idx, dir):
if is_prime[lst[ix]]:
return ix
return dir*float('inf')