Question

Is there any suggestion on solving next higher prime and palindrome number from a given int.

Here is the snippet I am trying but its a kind of slow, please suggest if you ve any good algorithm that i can test.

#!/usr/bin/python                                                                                                                                            

def next_higher(n):
    while True:
        s = str(n)
        if not any([n % i == 0 \
            for i in range(2, int(n**0.5))]) and s == s[::-1]:
                return n
        n = n + 1

print next_higher(2004)
print next_higher(20)

Output:

10201
101 

Updated code testing for palindrome before prime. much faster than my previous code. I am implementing the suggestion from user2357112.

  #!/usr/bin/python                                                                                                                                          

  def next_higher(n):
      while True:
          s = str(n)
          if s == s[::-1]:
              if not any([n % i == 0 \
                  for i in range(2, int(n**0.5))]):
                      return n
          n = n + 1

  print next_higher(2004111)
  print next_higher(2004)
  print next_higher(2004)
  print next_higher(20)
Était-ce utile?

La solution

There are quite a few optimizations you could do:

  • Like user2357.. suggested in the comments, test palindromeness first, and then check if the number is prime, since prime check is more expensive.
  • You don't need to check even number divisibility once you check is the number is divisible by 2. So you can change it to [2] + range(3, int(n**0.5) + 1, 2) to check only odd numbers after 2. (Also you need to do sqrt + 1 like I mentioned in the comments)
  • You should use () instead of []. [] generates the entire list of factors first and only then checks for any. If you use (), it creates a generator, so it stops as soon as a True value is found without calculating the entire list.
  • You should also use xrange instead of range for the same reason (xrange gives a generator, range gives a list)
  • You can use the Sieve of Eratosthenes algorithm to significantly reduce the time taken for prime number check.
  • You can also see if the palindrome check can be made faster. You can actually skip a whole lot of numbers instead of doing just + 1 each time.

Here is a version with most of these optimizations except the last two:

def next_higher(n):
    if n % 2 == 0:
        n = n - 1
    while True:
        n = n + 2
        s = str(n)
        if s == s[::-1]:
            if not any((n % i == 0 for i in xrange(3, int(n**0.5) + 1, 2))):
                return n

This should be pretty fast for your needs I believe. But you can do the last 2 optimizations to make it much more faster if you want.

Autres conseils

Other than what has already been suggested,

What I suggest is that you first get the first palindrome number that is just higher than the given integer.

You can do this by trying to match the centre digits outwards.

Also, you should only check for numbers with odd number of digits, since if a number has even number of digits and it is a palindrome, then it will always be divisible by 11 and cannot be prime.

Once you get the first palindrome number that has odd number of digits and that is just higher than the current number, test it for primality and find the next palindrome number higher than this one.

You can do this by incrementing the centre digit.

Keep doing this till it rolls over to zero. In that case start incrementing the two neighbouring digits.

Continue, till you reach a prime number.

I tried optimizing the palindrome check i.e to find odd palindrome's. Since the first digit should be odd number, i focused on that part. Here's the code below with the assumptions its greater than 1 digit.

def next_odd_palindrome(n):
  """to check the next odd palindrome number"""
  if n%2==0:
    n=n-1
  while True:
    n=n+2
    s = str(n)
    if int(s[0])%2==0:
      n = int(str(int(s[0])+1)+ s[1:])
      s = str(n)
    if s==s[::-1]:
      return n

let me know if anything wrong.

Just for the fun of it, I implemented all optimizations by Hari Shankar and Abhishek Bansal.

It first finds the higher odd length palindrome, then increment the palindrome in a way that keeps its palindromity. Then checks each number using prime numbers calculated by Sieve method in the beginning.

This can process up to n=10^14 (can be higher if you increase the CACHE size) under 1 second in my computer =D

primes = []
CACHE = int(10**7) # Cache size for Sieve

# Custom class for immediate printing of output
import sys
class Unbuf:
    def __init__(self,stream):
        self.stream = stream

    def write(self,data):
        self.stream.write(data)
        self.stream.flush()
sys.stdout = Unbuf(sys.stdout)

def sieve():
    global primes
    is_prime = [False,False]+([True]*(CACHE-1))
    for i in xrange(2,int(CACHE**0.5)):
        if is_prime[i]:
            is_prime[i*i::i] = [False]*((CACHE-i*i+i)/i)
    primes = [num for num, bool_prime in enumerate(is_prime) if bool_prime]

def is_prime(n):
    """Checks whether n is prime"""
    global primes
    if n<2:
        return False
    if n==2:
        return True
    for prime in primes:
        if prime>n**0.5+1:
            return True
        if n%prime==0:
            return False
    # For the case that the number is bigger than the square of our largest prime
    for num in xrange(primes[-1]+2,n**0.5+1,2):
        if n%num==0:
            return False
    return True

def next_higher_odd_length_palindrome(n):
    n = str(n)
    if len(n)%2==0: # Even length, take the smallest odd length (10(00)*1)
        n = '1'+('0'*(len(n)-1))+'1'
    else:
        middle_idx = len(n)/2
        left = int(n[:middle_idx+1])
        left_cmp = n[middle_idx::-1]
        right_cmp = n[middle_idx:]
        # If mirroring left part to right part
        # makes the number smaller or equal, then
        if right_cmp>=left_cmp:
            # Increase the left half number
            left = left+1
        # Mirror left part to the right part
        n = str(left)+str(left)[-2::-1]
    return n

def next_higher(n):
    if n<=1:
        return 2
    # Ensure the number is a palindrome of odd length
    n = next_higher_odd_length_palindrome(n)
    while True:
        if is_prime(int(n)):
            return int(n)
        n = next_higher_odd_length_palindrome(n)
        if int(n[0])%2==0:
            new_lead = str(int(n[0])+1)
            n = new_lead+n[1:-1]+new_lead

import time
print 'Sieving...',
start_time = time.time()
sieve()
print 'Done in %.3fs' % (time.time() - start_time)
print next_higher(2004111)
print next_higher(2004)
print next_higher(20)
while True:
    n = int(raw_input('Enter n: '))
    start_time = time.time()
    result = next_higher(n)
    print 'Next higher prime palindrome: %d (calculated in %.3fs)' % (result, time.time() - start_time)

Which in my computer gives this output:

Sieving... Done in 1.444s
3007003
10301
101
Enter n: 1999999999
Next higher prime palindrome: 10000500001 (calculated in 0.004s)
Enter n: 1999999999999
Next higher prime palindrome: 3000002000003 (calculated in 0.051s)
Enter n: 1000000000000
Next higher prime palindrome: 1000008000001 (calculated in 0.030s)
Enter n:
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top