I'm working on problem 46 from project euler:
It was proposed by Christian Goldbach that every odd composite number can be written as the sum of a prime and twice a square.
9 = 7 + ×ばつ1^2 15 = 7 + ×ばつ2^2 21 = 3 + ×ばつ3^2 25 = 7 + ×ばつ3^2 27 = 19 + ×ばつ2^2 33 = 31 + ×ばつ1^2
It turns out that the conjecture was false. What is the smallest odd composite that cannot be written as the sum of a prime and twice a square?
My code is so ridiculously inefficient and it takes minutes before an answer pops up. (Boundary numbers are just rough estimates)
numbers = [(2*x**2) for x in list(range(1, 1000))]
normal = list(range(1, 10000000))
#produces primes under n
def primes(n):
sieve = [True] * n
for i in range(3, int(n**0.5) + 1, 2):
if sieve[i]:
sieve[i * i:: 2 * i] = [False] * int((n - i * i - 1)/(2 * i) + 1)
return [2] + [i for i in range(3, n, 2) if sieve[i]]
primes = primes(1000)
final = []
#add the two lists in every way possible
added = [x + y for x in numbers for y in primes]
#if it does not appear in a normal number line send it to final
for x in added:
if x not in normal:
final.append(x)
print(min(final))
I also lack the knowledge to use any mathematical tricks or algorithms. Where can I start learning code efficiency/performance and simple algorithms to use in my code?
-
\$\begingroup\$ One thing to keep in mind is that normal python isn't the fastest language around. Would implementing this in something like C++ be an option? \$\endgroup\$yuri– yuri2017年06月30日 12:22:00 +00:00Commented Jun 30, 2017 at 12:22
-
\$\begingroup\$ Yes, how would I do that and how much faster would it be? \$\endgroup\$Po Chen Liu– Po Chen Liu2017年06月30日 12:26:15 +00:00Commented Jun 30, 2017 at 12:26
-
1\$\begingroup\$ The increase in performance differs based on what you are doing. Here is a comparison of various python and C++ programs to give you an idea. \$\endgroup\$yuri– yuri2017年06月30日 12:43:17 +00:00Commented Jun 30, 2017 at 12:43
1 Answer 1
Optimization
It seems that you have tried to optimize your code using the sieve of Eratosthenes; however, the sieve of Eratosthenes is only useful if there is a given upper bound in the problem statement.
An optimal solution can be achieved for this problem via brute-force in the following manner (pseudocode):
n = ... # the smallest odd composite number
loop forever:
solution_found = False
for all possible x:
possible_prime = n - 2 * x ** 2
if is_prime(possible_prime):
solution_found = True
break
if not solution_found:
print("Answer: " + n)
break
n = n + ... # what to increment by?
Note that the above is merely pseudocode. For the line for all possible x
, it is possible to bound x
. Also (and most importantly), the is_prime()
method needs to be cleverly thought out, possibly caching values.
Note: I just tested my implementation based on the above pseudocode and got the answer instantaneously. Too bad Goldbach didn't have a computer!
-
2\$\begingroup\$ So, you need to solve
n = p + 2x^2
. One possible way (there are others) is to write the equation asp = n - 2x^2
. Now, try plugging inx = 0, 1, 2, ...
untilp
is prime. We can stop searching forx
whenp
becomes negative, hence the highestx
possible causesn - 2x^2 = 0
. Then, solving forx
givesx = sqrt(n/2)
. So if there is nox
in the range0
tosqrt(n/2)
satisfying the previously mentioned equation, then there is no solution, meaningn
is the answer. \$\endgroup\$ljeabmreosn– ljeabmreosn2017年07月01日 03:23:09 +00:00Commented Jul 1, 2017 at 3:23 -
2\$\begingroup\$ It's not at all true that "the sieve of Eratosthenes is only useful if there is a given upper bound". Sieving is useful whenever you want to test primality of a large number of numbers in a (relatively) small range. If you don't know the exact range of the numbers, that isn't an obstacle in and of itself; you can always use a strategy where you double the size of the range and recompute each time you need a larger range (similar to how dynamic arrays are allocated). This will be asymptotically faster. \$\endgroup\$jschnei– jschnei2017年07月03日 00:37:41 +00:00Commented Jul 3, 2017 at 0:37
-
2\$\begingroup\$ Also I think you should be very careful when calling solutions "optimal" unless you can actually prove they are optimal. (There are faster solutions for this problem, especially if you're willing to trade some time for memory). \$\endgroup\$jschnei– jschnei2017年07月03日 00:40:57 +00:00Commented Jul 3, 2017 at 0:40
-
1\$\begingroup\$ @jschnei The sieve of Eratosthenes is useful when the numbers in are in some range. But suppose we need to determine if
n
is prime. Okay, run the sieve. Then, determine ifn^2 + 2
is prime. You would then need to rebuild the table which is very inefficient. The trial division algorithm is O(sqrt(n)), Omega(1) whereas the sieve is Theta(nloglogn). It would be nice if you could point me to the dynamic sieve implementation you mentioned! \$\endgroup\$ljeabmreosn– ljeabmreosn2017年07月03日 01:04:33 +00:00Commented Jul 3, 2017 at 1:04 -
1\$\begingroup\$ I can't find an implementation that does exactly what I described, but there is a similar algorithm that goes by names like "segmented sieve" which works via similar principles (e.g. primesieve.org/segmented_sieve.html). The important thing to realize is that you definitely don't have to rebuild the table every time you want to do a new check. You only ever have to extend the table. Of course, if you extend it by a factor of 2 each time, it doesn't matter asymptotically if you just rebuild the whole table instead (and you'll do at most log(max) rebuilds). \$\endgroup\$jschnei– jschnei2017年07月03日 01:13:48 +00:00Commented Jul 3, 2017 at 1:13
Explore related questions
See similar questions with these tags.