Project Euler #37 issue

Project Euler #37 issue



I tried to solve Project Euler #37:



The number 3797 has an interesting property. Being prime itself, it is possible to continuously remove digits from left to right, and remain prime at each stage: 3797, 797, 97, and 7. Similarly we can work from right to left: 3797, 379, 37, and 3.
Find the sum of the only eleven primes that are both truncatable from left to right and right to left.
NOTE: 2, 3, 5, and 7 are not considered to be truncatable primes.



I wrote my code in Python but I am facing weird issues.
Here's my code:


def isPrime(n):
if n == 2 or n == 3 or n == 5: return True
if n < 2 or n%2 == 0: return False
if n < 9: return True
if n%3 == 0: return False
if n%5 == 0: return False
r = int(n**0.5)
f = 5
while f <= r:
if n%f == 0: return False
if n%(f+2) == 0: return False
f +=6
return True

def gen(nb):
results =
nb_str = str(nb)
for k in range(0, len(nb_str) - 1):
results.append(nb_str[k:])
results.append(nb_str[-k:])
return results

def check(nb):
for t in gen(nb):
if not isPrime(int(t)):
return False
return True

c = 0
s = 0
i = 2
while c != 11:
if check(i):
c += 1
s += i
i += 1
print(s)



Where does the error come from? (The expected result is 748317)



I suspect the errors coming from the results list





Please fix your indentation
– teclnol
Aug 25 at 20:27





Sorry. I fixed it
– Samy
Aug 25 at 20:32





You've broken down your code into small, independently-usable functions, which is great—that means you can test those functions independently. Call gen(nb) on some numbers and see if you get the right results. If not, you've only got 6 lines of code to debug (and ask about on SO, if you get stuck) instead of the whole program.
– abarnert
Aug 25 at 20:48


gen(nb)




2 Answers
2



Joe Iddon has explained the error in your code, but you can speed it up a little by turning gen into an actual generator. That way, you can stop checking the results for a given nb as soon as you detect a composite number (and gen will stop generating them). I've also made a few minor tweaks to your primality tester. Remember, the or operator short-circuits, so if a is True-ish in a or b then it doesn't bother evaluating b.


gen


nb


gen


or


a


a or b


b


def isPrime(n):
if n in 2, 3, 5, 7:
return True
if n < 2 or n%2 == 0:
return False
if n%3 == 0 or n%5 == 0:
return False
r = int(n**0.5)
f = 5
while f <= r:
if n%f == 0 or n%(f+2) == 0:
return False
f += 6
return True

def gen(nb):
yield nb
nb_str = str(nb)
for k in range(1, len(nb_str)):
yield int(nb_str[k:])
yield int(nb_str[:-k])

def check(nb):
for t in gen(nb):
if not isPrime(t):
return False
return True

c = s = 0
# Don't check single digit primes
i = 11
while c < 11:
if check(i):
c += 1
s += i
print(i)
i += 2

print('sum', s)



output


23
37
53
73
313
317
373
797
3137
3797
739397
sum 748317



In fact, you can get rid of the check function, and replace it with all, which also short-circuits, like or does. So you can replace the


check


all


or


if check(i):



with


if all(map(isPrime, gen(i))):





If your talking about optimising the solution, then the most drastic improvement would be to use a prime sieve and memoize.
– Joe Iddon
Aug 26 at 11:46






@JoeIddon Using my best prime sieve, sieving the odd numbers under 1000000 in a bytearray, is about 10 times faster than this solution. But I didn't post that version, since it deviates so much from the OP's. And using a fixed sieve is cheating a little, since you need to specify the sieve size. I guess I could use a progressive sieve in a dict, but that would chew up a lot more RAM.
– PM 2Ring
Aug 26 at 16:23





Why would you have to use the progressive sieve in a dict? Just increase the size by like 10000 if you haven't found 11 yet.
– Joe Iddon
Aug 26 at 18:12



10000


11





@JoeIddon You could do that, but I don't like it because you have to copy the old array to the new one. Or maintain multiple arrays. I mentioned using dict because there's an interesting prime generator that does that in this answer. (However, it's about twice as slow as my segmented sieve that uses bytearrays). But I guess a generator isn't so useful when you need random access to an indefinite sized set of primes. ;)
– PM 2Ring
Aug 26 at 18:28






@JoeIddon Sorry if there's anything vague or confusing in my previous comment. It's 4:35AM here, and I may not be thinking straight. ;)
– PM 2Ring
Aug 26 at 18:35



Yes, the gen() function is not working correctly as your slicing is off, also, you count 2, 3, 5 and 7 as truncatable primes which the question denies.


gen()


2


3


5


7



The second slice should be the other way around:


>>> s = 'abcd'
>>> for i in range(1,len(s)-1):
... print(s[i:])
... print(s[:-i])
...
bcd
abc
cd
ab



which we can see produces the right strings.



Altogether then, the function should be:


def gen(nb):
results = [nb]
nb_str = str(nb)
for k in range(1, len(nb_str)):
results.append(int(nb_str[k:]))
results.append(int(nb_str[:-k]))
return results



note I also added a string to int conversion - not sure how Python didn't make that obvious for you :/



And before get the full solution, Project Euler nearly always gives you an example which you can use to check your code:


>>> check(3797)
True



You must also add a condition in the check function to return False if the number is 2, 3, 5 or 7 as this is stated clearly in the question.


check


False


2


3


5


7



And the result is the expected: 748317.


748317






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)