Project Euler #37 issue - python

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

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.
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.
And the result is the expected: 748317.

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.
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
if check(i):
with
if all(map(isPrime, gen(i))):

Related

Boolean True or False upon finding two consecutive digits in a given integer without using any built-in functions

To find two consecutive digits(D) in a given integer(N) without using any built-in functions and returning True or False, the following code seems to be exiting when coming across one D, however it works if there are two Ds. Why is it not working as it is and how to fix it? Thanks!
def double_digits(n, d):
"""Return True if N has two Ds in a row otherwise return False.
int, int -> Boolean
>>> double_digits(91019, 1)
False
>>> double_digits(88, 8)
True
>>> double_digits(2772, 7)
True
>>> double_digits(88108, 0)
False
>>> double_digits(12345, 4)
False
>>> double_digits(81811081, 1)
True
"""
while n > 0:
remainder = n % 10
n = n // 10
if remainder == d:
if n % 10 == d:
return True
else:
remainder, n = n % 10, n // 10
return False
The last return statement should be out of the loop.
Below is the correct code:
def double_digits(n, d):
while n > 0:
remainder = n % 10
n = n // 10
if remainder == d:
if n % 10 == d:
return True
else:
remainder, n = n % 10, n // 10
return False
You must de-indent the last statement : return False, 4 spaces to the left. That must help you.
Alternatively you can convert the input to a string and then the character matching would be easy.
def double_digits(n: int, d: int)->bool:
n = str(n)
d = str(d)
i = 0
while i < len(n)-1:
if n[i] == d and n[i+1] == d:
return True
i+=1
return False
Here I have used some builtin fuctions like len and str, but if you explicitly
want to avoid using them, just go with your original approach and just de-indent the return statement once

How to write a function wrong_prime that takes no arguement and should return the first prime p for which q = 2^p - 1 is not prime

500 years ago it was believed that all numbers q of the form q = 2^p -
1 are prime if p is prime.
Write a function wrong_prime that takes no arguments. It should return
the first prime p for which q = 2^p - 1 is not prime.
Hint: this number should be between 10 and 15.
The code I have come up with is the following but does not work
def isprime(n):
if n < 2:
return False
elif n == 2:
return True
else:
if n % 2 == 0:
return False
for i in range(3,n,2):
if n % i == 0:
return False
return True
def wrong_prime():
for i in range(1,):
q = 2**i-1
if isprime(i) and isprime(q) == False:
print(q)
When inputting wrong_function there is no output
Can anyone help produce a working function?
range(1,) is range(0, 1), i.e. you are only testing 0. If you want an open-ended range, you can use e.g. a while loop with an extra counting variable.
def wrong_prime():
i = 1
while True: # fix loop
q = 2**i-1
if isprime(i) and isprime(q) == False:
return i # return p, not print q
i += 1 # remember to increment manually
Or using next with itertools.count:
import itertools
def wrong_prime():
return next(p for p in itertools.count(1) if isprime(p) and not isprime(2**p-1))
(Of course, given the "Hint" you can also just use a range(10, 16))
Also, note that the function should return the value of p, whereas yours prints q. For testing, call as print(wrong_prime()) to see the value.

Fast Prime Generator besides Sieve

I recently made this bit of code, but wonder if there is a faster way to find primes (not the Sieve; I'm still trying to make that). Any advice? I'm using Python and I'm rather new to it.
def isPrime(input):
current = 0
while current < repetitions:
current = current + 2
if int(input) % current == 0:
if not current == input:
return "Not prime."
else:
return "Prime"
else:
print current
return "Prime"
i = 1
primes = []
while len(primes) < 10001:
repetitions = int(i)-1
val = isPrime(i)
if val == "Prime":
primes.append(i)
i = i + 2
print primes[10000]
here is a function that detect if x is prime or not
def is_prime(x):
if x == 1 or x==0:
return False
elif x == 2:
return True
if x%2 == 0:
return False
for i in range(3, int((x**0.5)+1), 2):
if x%i == 0:
return False
return True
and another implementation that print prime numbers < n
def prime_range(n):
print(2)
for x in range(3, n, 2):
for i in range(3, int((x**0.5)+1), 2):
if x%i == 0:
break
else:
print(x)
hope it helps !
If you are not using a sieve, then the next best are probably wheel methods. A 2-wheel checks for 2 and odd numbers thereafter. A 6-wheel checks for 2, 3 and numbers of the form (6n +/- 1), that is numbers with no factors of 2 or 3. The answer from taoufik A above is a 2-wheel.
I cannot write Python, so here is the pseudocode for a 6-wheel implementation:
function isPrime(x) returns boolean
if (x <= 1) then return false
// A 6-wheel needs separate checks for 2 and 3.
if (x MOD 2 == 0) then return x == 2
if (x MOD 3 == 0) then return x == 3
// Run the wheel for 5, 7, 11, 13, ...
step <- 4
limit <- sqrt(x)
for (i <- 5; i <= limit; i <- i + step) do
if (x MOD i == 0) then return false
step <- (6 - step) // Alternate steps of 2 and 4.
end for
return true
end function
I leave it to you to convert that into Python.
As in
n = 10000
for p in range(2, n+1):
for i in range(2, p):
if p % i == 0:
break
else:
print p
print 'Done'
?

List with multiple conditions

I am creating a boolean function that checks if each element in my list is greater than 1 and less than 6. Also the list is positive integers and not negative, 0, string or anything else.
I feel like I have tried almost everything and cant find my solution. This is what I tentatively have right now.
def checkList(aList):
for i in aList:
if i < 1:
return False
elif i > 6:
return False
else:
return True
Use a generator expression to build an intermediate list of elements, then check them against all to see if they fit in your constraints.
def check_list(li):
return all((type(i) == int and 1 < i < 6 for i in li))
A single generator expression with all using isinstance(i, int) to check is each element is an int and 1 <= i <= 6 to make sure each int is in the range 1-6:
def checkList(aList):
return all(isinstance(i, int) and 1 < i < 6 for i in aList)
all will short circuit and return False for any non int or any value less than 1 or greater the 5 or return True if all the elements are ints and in the range 1-6.
In your own code you are checking for if i < 1 and elif i > 6 when you say you want to check if each element in my list is greater than 1 and less than 6 so it would be if i < 2 and elif i > 5 return False, 1 is not less than 1 and 6 is greater than 5, so to correct your own logic and to check for ints.
def checkList(aList):
for i in aList:
if not isinstance(i, int):
return False
if i < 2:
return False
if i > 5:
return False
return True # outside loop
You need to move the return True outside loop, just because one is an int
between 1 - 6 does not mean they all are.
Which could be rewritten as:
def checkList(aList):
for i in aList:
if not isinstance(i, int):
return False
if 2 > i > 5:
return False
return True
This will work for you
def check_sequence(sequence, low, high):
if all(isinstance(i, int) for i in sequence):
return all(low < i < high for i in sequence)
else:
return False
print(check_sequence([2, 5, 4, 3, 3], 1, 6))
>> True
The important concept here is low < i < high. In Python, you can specify a range of values that the variable should be between.
You could just use filter it would go something like this
filtered = filter(seq, lambda x: 1 < int(x) < 6)
That will return a filtered list containing only items which were between 1 and 6. You can then check if the list has the same length to see if anything was removed.
Going this route, your function would look like this:
def checkList(seq):
filtered = filter(seq, lambda x: 1 < int(x) < 6)
return len(filteted) == len(seq)

Find permutation list

I have a task. I know this task is really easy but..
A non-empty zero-indexed array A consisting of N integers is given.
A permutation is a sequence containing each element from 1 to N once, and only once.
For example, array A such that:
A[0] = 4
A[1] = 1
A[2] = 3
A[3] = 2
is a permutation, but array A such that:
A[0] = 4
A[1] = 1
A[2] = 3
is not a permutation.
The goal is to check whether array A is a permutation.
I implemented this solution, but I think this isn't the best solution.
def solution(A):
# write your code in Python 2.6
maxN = max(A)
B = list(xrange(1,maxN+1))
if sorted(A) == sorted(B):
return 1
else:
return 0
Do you have any ideas how I should solve this problem?
def solution(A):
N = len(A)
return min(A) == 1 and max(A) == N and len(set(A)) == N
That takes (expected) time linear in N, so is expected to be faster than sorting. But it does rely on the stated assumption that all list entries are in fact integers. If they're not, then, for example,
>>> solution([1, 2, 4, 3.14159])
True
If both arrays should have all numbers from 1 to N you shouldn't sort them, all you need to do is make sure they actually have all the numbers.
so this is your algorithm (working solution soon):
make sure the list is of size N, otherwise return false.
generate list of N zeros (We will refer to it as zero-list)
for each member (say it's value is m) of the list (both) increase the zero-list[m-1] by 1
make sure all members of zero-list is 1
This is O(n) as opposed to O(nlogn) that you have right now.
What you are doing verifies that each member from 1 to N exists in the list you're given
#!/usr/bin/env python
def is_perm(l, n):
# make sure initial criterias are met
if len(l) != max(l) or len(l) != n:
return False
# make room for a list that verifies existence from 1 to N
zero_list = [0 for i in l]
# "mark" using the new list each member of the input list
for i in l:
try:
zero_list[i-1] += 1
except:
return False
# make sure all members have been "marked"
for i in zero_list:
if zero_list[i] != 1:
return False
# if all is good, the earth is saved
return True
if "__main__" == __name__:
print is_perm([1, 2, 3, 3], 4) # false
print is_perm([1, 2, 4, 3], 4) # true
print is_perm([1, 2, 4, 300000000000000], 4) # false
def is_perm(lst):
n = len(lst)
if min(lst) != 1 or max(lst) != n:
return False
cnt = [1] * n
for e in lst:
cnt[e - 1] -= 1
return not any(cnt)
It has a worst case complexity O(n) which is of course optimal.
Just check if all elements are present ...
In [36]: all(map(lambda p: p in [1,4,3,2], range(1,5)))
Out[36]: True
In [37]: all(map(lambda p: p in [1,4,3,2444], range(1,5)))
Out[37]: False
In [38]: all(map(lambda p: p in [1,4,3], range(1,5)))
Out[38]: False
So I found solution which works great:
def solution(A):
A.sort(reverse=True)
return 1 if A[0] == len(A) and len(set(A)) == len(A) else 0
What about this?:
>>> def is_permutation(a, n):
if len(a) != n: return False
h = {}
for i in a:
if not 1 <= i <= n: return False
if h.setdefault(i, False): return False
h[i] = True
return True
>>> is_permutation([1,4,2,3], 4)
True
>>> is_permutation([1,4,2,0], 4)
False
>>> is_permutation([1,4,2],4)
False
Here is fastest way:
def solution(A):
'''
>>> solution([4,1,3,2])
1
>>> solution([2,3])
0
'''
suma = sum(A)
N = len(A)
if (N*(N+1)/2)!=suma:
return 0
return 1

Categories