Incorrect output Project Euler #50 - python

Project Euler problem 50 reads as follows:
The prime 41, can be written as the sum of six consecutive primes:
41 = 2 + 3 + 5 + 7 + 11 + 13
This is the longest sum of consecutive primes that adds to a prime below one-hundred.
The longest sum of consecutive primes below one-thousand that adds to a prime, contains 21 terms, and is equal to 953.
Which prime, below one-million, can be written as the sum of the most consecutive primes?
In my approach I pregenerate a list of primes using sieve of eratosthenes, then
in the function itself I keep adding succeeding elements of my prime number list
and each time i do that I check if the sum itself is prime and if it is I keep track of it as the biggest one and return it. Well that should work i guess ? Obviously the answer is incorrect, but the interesting thing is that when i change the sieve to generate primes below 100000 it doesn't give an index error but gives another result.
from algorithms import gen_primes
primes = [i for i in gen_primes(1000000)]
def main(n):
idx, total, maximum = 0, 0, 0
while total < n:
total += primes[idx]
idx += 1
if total in primes:
maximum = total
return maximum
print(main(1000000))

Your program doesn't solve the general problem: you always start your list of consecutive primes at the lowest, 2. Thus, what you return is the longest consecutive list starting at 2*, rather than any consecutive list of primes.
In short, you need another loop ...
start_idx = 0
while start_idx < len(primes) and best_len*primes[start_idx] < n:
# find longest list starting at primes[start_idx]
start_idx += 1
In case it's any help, the successful sequence begins between 1500 and 2000.

Related

Finding nth Palindromic Prime - Where did I go wrong?

Given integer n (1<=n <=300), the code needs to return the nth Palindromic Prime.
I have written the below block of code to achieve the above, but for the life of me I cannot understand why my code isn't outputting the given expected value.
Indeed I don't even know if it is my code that is wrong, or the given expected value is just bull. Would really appreciate some guidance.
Expected output: symmetricPrime2(72) returns 70507
Actual output: symmetricPrime2(72) returns 30103
def symmetricPrime2(n,candidate=2):
primes = [2]
counter = 1
while True:
i = 0
prep = 0
candidate = candidate + 1
candidate_sr = str(candidate)
#test if candidate is prime
for prime in primes:
if candidate%prime == 0:
prep += 1
#test if candidate is palindromic
candidate_sr_rev = candidate_sr[len(candidate_sr)::-1]
if prep == 0 and candidate_sr == candidate_sr_rev:
primes.append(candidate)
if len(primes) == n:
break
return primes[-1]
You're testing numbers for primality based on whether or not they are divisible by numbers in the primes list, but you only add numbers to primes if they are palindromic. As a result, once you start encountering composite numbers with prime factors greater than 11, you're going to start identifying primes incorrectly.
According to your function, symmetricPrime2(12) == 323, but 323 is composite (17 × 19).
There are several things that either are wrong or can be improved in your code.
You can initialize primes with [2, 3] rather than [2], which allows you to start from candidate=3 and to increment it by 2 instead of 1, since 2 is the only even prime number.
i = 0 has no point in your code
prep is only used to test if candidate is prime. As soon as you find that candidate % prime is True, you can break out of your for loop, there is no need to continue to test it if you've already found out a divisor.
The biggest mistake in your code: all primes are not palindromic. Of course you know this, but this is what you've written. Starting from 11, you only add primes to your list that are palindromic (you can test and see that 13 is not in primes for instance). Remove the and candidate_sr == candidate_sr_rev in your if so that you add primes to your list correctly. Since you want the n-th prime, you have two choices:
either you define a second list palindromic_primes to which you add every palindromic prime you've encountered and test its length to be equal to n
or you just keep the number of palindromic primes you've encountered and when this number is equal to n, you can return this palindromic prime number.
Your primality test is wrong as you add to primes only palindromic primes.
def symmetricPrime2(n,candidate=1):
primes = []
counter = 0
while True:
i = 0
prep = 0
candidate = candidate + 1
candidate_sr = str(candidate)
#test if candidate is prime
for prime in primes:
if candidate%prime == 0:
prep += 1
#test if candidate is palindromic
candidate_sr_rev = candidate_sr[len(candidate_sr)::-1]
if prep == 0:
primes.append(candidate)
if candidate_sr == candidate_sr_rev:
counter += 1
if counter == n:
return candidate

Prime checker including non primes

I am trying to solve Project Euler number 7.
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
What is the 10 001st prime number?
First thing that came into my mind was using length of list. This was very ineffective solution as it took over a minute. This is the used code.
def ch7():
primes = []
x = 2
while len(primes) != 10001:
for i in range(2, x):
if x % i == 0:
break
else:
primes.append(x)
x += 1
print(primes[-1])
ch7()
# Output is: 104743.
This works well but I wanted to reach faster solution. Therefore I did a bit of research and found out that in order to know if a number is a prime, we need to test whether it is divisible by any number up to its square root e.g. in order to know if 100 is a prime we dont need to divide it by every number up to 100, but only up to 10.
When I implemented this finding weird thing happened. The algorithm included some non primes. To be exact 66 of them. This is the adjusted code:
import math
primes = []
def ch7():
x = 2
while len(primes) != 10001:
for i in range(2, math.ceil(math.sqrt(x))):
if x % i == 0:
break
else:
primes.append(x)
x += 1
print(primes[-1])
ch7()
# Output is 104009
This solution takes under a second but it includes some non primes. I used math.ceil() in order to get int instead of float but I figured it should not be a problem since it still tests by every int up to square root of x.
Thank you for any suggestions.
Your solution generates a list of primes, but doens't use that list for anything but extracting the last element. We can toss that list, and cut the time of the code in half by treating 2 as a special case, and only testing odd numbers:
def ch7(limit=10001): # assume limit is >= 1
prime = 2
number = 3
count = 1
while count < limit:
for divisor in range(3, int(number ** 0.5) + 1, 2):
if number % divisor == 0:
break
else: # no break
prime = number
count += 1
number += 2
return prime
print(ch7())
But if you're going to collect a list of primes, you can use that list to get even more speed out of the program (about 10% for the test limits in use) by using those primes as divisors instead of odd numbers:
def ch7(limit=10001): # assume limit is >= 1
primes = [2]
number = 3
while len(primes) < limit:
for prime in primes:
if prime * prime > number: # look no further
primes.append(number)
break
if number % prime == 0: # composite
break
else: # may never be needed but prime gaps can be arbitrarily large
primes.append(number)
number += 2
return primes[-1]
print(ch7())
BTW, your second solution, even with the + 1 fix you mention in the comments, comes up with one prime beyond the correct answer. This is due to the way your code (mis)handles the prime 2.

What is the complexity of this Prime finding algorithm?

My dad and I are trying to determine the algorithmic complexity of this prime finding function that my dad came up with as a youngster.
The first loop is obviously n since it sets up the dictionary. The trickier part is the nested loops. The outer loop runs n/4 times: 0 to n/2, step=2. The inner loop only runs if the number is considered prime which happens a lot at the beginning but happens less and less as the numbers increase.
def primesV2(n):
count = 0 # count is for counting the number of iterations done
# set all even numbers (and 1) to False, else assume prime
x = {}
for i in range(n):
if (i != 2 and i % 2 == 0) or i==1:
x[i] = False
else:
x[i] = True
# start at 3 because its the first odd prime
i=3
while i < n/2: # loop until halfway to n
if x[i]: # if the number is considered prime
for j in range(3*i,n,i*2): # if i=3, j will be 9,15,21 (odd multiples of 3)
x[j] = False # these are not prime
count = count + 1
else:
count = count + 1
i = i+2
return x, count
What you have here is a modified Sieve of Eratosthenes. Without any optimizations the complexity would be O(n log log n). Check out this wikipedia article why that is.
Your optimizations speed it up by a total factor of 4. You only go up to n/2 (you could stop at sqrt n) and you skip half the multiples. While this will make the code faster the complexity remains unchanged (constant factors are ignored). So it will still be O(n log log n).

Project Euler 23 - No matter what I do, my answer is far too big

Problem 23 asks for the sum of all numbers "not the sum of two abundant numers". We need only check numbers smaller than 28123. An integer n is abundant if its proper divisors sum to an integer greater than n - 12 is the first abundant number since 1+2+3+4+6 = 16 > 12. This makes 24 the smallest number which is a sum of 2 abundant numbers, so we want to exclude it from the sum.
Here's my python code, which I've tried many variations of, but using the same idea:
import math
def divisors_of(n):
divs=[1]
for i in range(2,int(math.sqrt(n))+1):
if i**2 == n:
divs.append(i)
elif n%i == 0:
divs.append(i)
divs.append(n//i)
return divs
def is_abun(n):
return sum(divisors_of(n))>n
numbers=[i for i in range(1,28123)]
for i in numbers:
for j in range(1,i):
if is_abun(j) and is_abun(i-j):
numbers.remove(i)
break
print(sum(numbers))
The idea is simply that if j and i-j are abundant numbers, then of course j + (i-j) = i is a sum of abundant numbers, so I remove it from my list of numbers. Then I sum over all remaining numbers. I don't see how this could possibly fail. However, the result should be (gotten from another source) 4179871, while I always get something in the ballpark of 197711987, which is over 47 times too large.
I really cannot understand how this fails to generate the correct result. There must be some line which isn't doing what I think it is, but everything here is just too simple for me to be suspicious of them. I thought the most likely error would be in calculating the abundant numbers, but I am almost completely sure that part's correct (if I get the code as is to generate a list of abundant numbers less than 28123, I get a list of length 6965, which is how many abundant numbers less than 28123 WolframAlpha claims).
The problem is that you are modifying the list numbers while you are iterating over it. This results in you removing less numbers than what you expect.
You should iterate over a copy of it:
for i in list(numbers):
for j in range(1,i):
if is_abun(j) and is_abun(i-j):
numbers.remove(i)
break
To understand what's happening consider this:
>>> numbers = list(range(10))
>>> for i in numbers:
... if i%2 == 0:
... numbers.remove(i)
... print(i)
...
0
2
4
6
8
As you can see the for loop only iterates over even numbers. That's because the for loop above is roughly equivalent to the following:
i = 0
while i < len(numbers):
number = numbers[i]
if number % 2 == 0:
numbers.remove(number)
print(number)
i += 1
So the first time you have i = 0 and thus number = 0, you remove the 0 from numbers so now you have numbers = [1,2,3,4,..., 9]. Note how the value 1 ended up in position 0, so the next iteration we have i = 1 and numbers[1] == [1,2,3,4,...,9][1] == 2 so we have skipped the number 1!
Everytime you remove a number the list is modified and if this modification leads to the items being shifted to the left you are effectively skipping them. That's why you want to iterate over a copy of the list: in this way the modifications to the original list do not affect the numbers "yielded" during iteration..

Project Euler #23 Incorrect Answer

I have just completed my solution for project euler problem number 23 which states:
A perfect number is a number for which the sum of its proper divisors
is exactly equal to the number. For example, the sum of the proper
divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28
is a perfect number.
A number n is called deficient if the sum of its proper divisors is
less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the
smallest number that can be written as the sum of two abundant numbers
is 24. By mathematical analysis, it can be shown that all integers
greater than 28123 can be written as the sum of two abundant numbers.
However, this upper limit cannot be reduced any further by analysis
even though it is known that the greatest number that cannot be
expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as
the sum of two abundant numbers.
This is my solution:
from math import sqrt
def divisors(n):
for i in range(2, 1 + int(sqrt(n))):
if n % i == 0:
yield i
yield n / i
def is_abundant(n):
return 1 + sum(divisors(n)) > n
abundants = [x for x in range(1, 28123 + 1) if is_abundant(x)]
abundants_set = set(abundants)
def is_abundant_sum(n):
for i in abundants:
if i > n: # assume "abundants" is ordered
return False
if (n - i) in abundants_set:
return True
return False
sum_of_non_abundants = sum(x for x in range(1, 28123 + 1) if not is_abundant_sum(x))
print(sum_of_non_abundants)
My answer is: 3906313
Explanation of my code:
The divisors generator pretty much returns all nontrivial divisors of an integer, but makes no guarantees on the order. It loops through 1 to square root of n and yields the divisor and its quotient. The next function is_abundant actually checks if sum of divisors of n is less than n then return False else return True. Next is the list abundants which holds all the abundant numbers from 1 to 28123 and abundants_set is just like abundants but instead it's a set not a list. The next function is is_abundant_**sum** which pretty much checks if the sum given to the functions is itself abundant or not and in the end the sum of numbers which are not is_abundant_sum is printed.
Where did I do wrong? What's the problem in my code?
Any help would be appreciated.
The divisors generator double counts the factor f of f**2. This bug affects the computed list of abundant numbers.

Categories

Resources