Does opening a file related to the program also stop the program? - python-2.7

I have this program that is supposed to search for perfect numbers.
(X is a perfect number if the sum of all numbers that divide X, divided by 2 is equal to X)
sum/2 = x
Now It has found the first four, which were known in Ancient Greece, so it's not really a anything awesome.
The next one should be 33550336.
I know it is a big number, but the program has been going for about 50 minutes, and still hasn't found 33550336.
Is it because I opened the .txt file where I store all the perfect numbers while the program was running, or is it because I don't have a PC fast enough to run it*, or because I'm using Python?
*NOTE: This same PC factorized 500 000 in 10 minutes (while also running the perfect number program and Google Chrome with 3 YouTube tabs), also using Python.
Here is the code to the program:
i = 2
a = open("perfect.txt", 'w')
a.close()
while True:
sum = 0
for x in range(1, i+1):
if i%x == 0:
sum += x
if sum / 2 == i:
a = open("perfect.txt", 'a')
a.write(str(i) + "\n")
a.close()
i += 1

The next one should be 33550336.
Your code (I fixed the indentation so that it does in principle what you want):
i = 2
a = open("perfect.txt", 'w')
a.close()
while True:
sum = 0
for x in range(1, i+1):
if i%x == 0:
sum += x
if sum / 2 == i:
a = open("perfect.txt", 'a')
a.write(str(i) + "\n")
a.close()
i += 1
does i divisions to find the divisors of i.
So to find the perfect numbers up to n, it does
2 + 3 + 4 + ... + (n-1) + n = n*(n+1)/2 - 1
divisions in the for loop.
Now, for n = 33550336, that would be
Prelude> 33550336 * (33550336 + 1) `quot` 2 - 1
562812539631615
roughly 5.6 * 1014 divisions.
Assuming your CPU could do 109 divisions per second (it most likely can't, 108 is a better estimate in my experience, but even that is for machine ints in C), that would take about 560,000 seconds. One day has 86400 seconds, so that would be roughly six and a half days (more than two months with the 108 estimate).
Your algorithm is just too slow to reach that in reasonable time.
If you don't want to use number-theory (even perfect numbers have a very simple structure, and if there are any odd perfect numbers, those are necessarily huge), you can still do better by dividing only up to the square root to find the divisors,
i = 2
a = open("perfect.txt", 'w')
a.close()
while True:
sum = 1
root = int(i**0.5)
for x in range(2, root+1):
if i%x == 0:
sum += x + i/x
if i == root*root:
sum -= x # if i is a square, we have counted the square root twice
if sum == i:
a = open("perfect.txt", 'a')
a.write(str(i) + "\n")
a.close()
i += 1
that only needs about 1.3 * 1011 divisions and should find the fifth perfect number in a couple of hours.
Without resorting to the explicit formula for even perfect numbers (2^(p-1) * (2^p - 1) for primes p such that 2^p - 1 is prime), you can speed it up somewhat by finding the prime factorisation of i and computing the divisor sum from that. That will make the test faster for all composite numbers, and much faster for most,
def factorisation(n):
facts = []
multiplicity = 0
while n%2 == 0:
multiplicity += 1
n = n // 2
if multiplicity > 0:
facts.append((2,multiplicity))
d = 3
while d*d <= n:
if n % d == 0:
multiplicity = 0
while n % d == 0:
multiplicity += 1
n = n // d
facts.append((d,multiplicity))
d += 2
if n > 1:
facts.append((n,1))
return facts
def divisorSum(n):
f = factorisation(n)
sum = 1
for (p,e) in f:
sum *= (p**(e+1) - 1)/(p-1)
return sum
def isPerfect(n):
return divisorSum(n) == 2*n
i = 2
count = 0
out = 10000
while count < 5:
if isPerfect(i):
print i
count += 1
if i == out:
print "At",i
out *= 5
i += 1
would take an estimated 40 minutes on my machine.
Not a bad estimate:
$ time python fastperf.py
6
28
496
8128
33550336
real 36m4.595s
user 36m2.001s
sys 0m0.453s

It is very hard to try and deduce why this has happened. I would suggest that you run your program either under a debugger and test several iteration manually to check if the code is really correct (I know you have already calculated 4 numbers but still). Alternatively it would be good to run your program under a python profiler just to see if it hasn't accidentally blocked on a lock or something.

It is possible, but not likely that this is an issue related to you opening the file while it is running. If it was an issue, there would have probably been some error message and/or program close/crash.
I would edit the program to write a log-type output to a file every so often. For example, everytime you have processed a target number that is an even multiple of 1-Million, write (open-append-close) the date-time and current-number and last-success-number to a log file.
You could then Type the file once in a while to measure progress.

Related

find the minimum lucky number that has the sum of digits equal to N

The lucky numbers are the positive integers whose decimal representations contain only the digits 4 or 7 .enter code here`
For example, numbers 47 , 474 , 4 are lucky and 3 , 13 , 567 are not
if there is no such no then output should -1.
input is sum of digits.
i have written this code:
int main(){
long long int s,no=0,minimum=999999999999999999999;
cin>>s;
for(int i=0; i<=s; i++){
for(int j=0; j<=s; j++){
if(i*4+j*7==s){no=0;
for(int k=0; k<i; k++){
no=no*10+4;
}
for(int l=0; l<j; l++){
no=no*10+7;
}if(no<minimum){
minimum=no;}
}
}
}if(minimum==999999999999999999999){cout<<-1;}
else {cout<<minimum;}
}
it is working fine smaller sum values but input is large then no formed is large due to which i am not able to compare them, the constraints for sum is 1<=n<=10^6
This answer shows a process, one of refinement to develop an efficient solution. The most efficient answer can be found in the paragraphs at the bottom, starting with the text "Of course, you can be even more clever ...".
I've left the entire process in so you can understand the sort of thinking that goes into algorithm development. So, let's begin.
First, I wouldn't, in this case, try to compare large numbers, it's totally unnecessary and limits the sort of ranges you want to handle. Instead, you simply have some number of fours and some number of sevens, which you can easily turn into a sum with:
sum = numFours * 4 + numSevens * 7
In addition, realising that the smallest number is, first and foremost, the one with the least number of digits, you want the absolute minimum number of fours and maximum number of sevens. So start with no fours and as many sevens as needed until you're at or just beyond the required sum.
Then, as long as you're not at the sum, perform the following mutually exclusive steps:
If you're over the desired sum, take away a seven if possible. If there are no sevens to take away, you're done, and there's no solution. Log that fact and exit.
Otherwise (i.e., if you're under the sum), add a four.
At this point, you will have a solution (no solution possible means that you would have already performed an exit in the first bullet point above).
Hence you now have a count of the fours and sevens that sum to the desired number, so the lowest number will be the one with all the fours at the left (for example 447 is less than any of {474, 744}). Output that, and you're done.
By doing it this way, the limitation (say, for example, an unsigned 32-bit int) is no longer the number you use (about four billion, so nine digits), instead it is whatever number of fours you can hold in four billion (about a billion digits).
That's an increase of about 11 billion percent, hopefully enough of an improvement for you, well beyond the 106 maximum sum specified.
In reality, you won't get that many fours since any group of seven fours can always be replaced with four sevens, giving a smaller number (a7777b will always be less than a4444444b, where a is zero or more fours and b is zero or more sevens, same counts in both numbers), so the maximum count of fours will always be six.
Here's some pseudo-code (Python code, actually) to show it in action. I've chosen Python, even though you stated C++, for the following reasons:
This is almost certainly an educational question (there's very little call for this sort of program in the real world). That means you're better off doing the heavy lifting of writing the code yourself, to ensure you understand and also to ensure you don't fail for just copying code off the net.
Python is the most awesome pseudo-code language ever. It can easily read like normal English pseudo-code but has the added benefit that a computer can actually run it for testing and validation purposes :-)
The Python code is:
import sys
# Get desired sum from command line, with default.
try:
desiredSum = int(sys.argv[1])
except:
desiredSum = 22
# Init sevens to get at or beyond sum, fours to zero, and the sum.
(numSevens, numFours) = ((desiredSum + 6) // 7, 0)
thisSum = numSevens * 7 + numFours * 4
# Continue until a solution is found.
while thisSum != desiredSum:
if thisSum > desiredSum:
# Too high, remove a seven. If that's not possible, exit.
if numSevens == 0:
print(f"{desiredSum}: no solution")
sys.exit(0)
numSevens -= 1
thisSum -= 7
else:
# Too low, add a four.
numFours += 1
thisSum += 4
# Only get here if solution found, so print lowest
# possible number that matches four/seven count.
print(f"{desiredSum}: answer is {'4' * numFours}{'7' * numSevens}")
And here's a transcript of it in action for a small sample range:
pax:~> for i in {11..20} ; do ./test47.py ${i} ; done
11: answer is 47
12: answer is 444
13: no solution
14: answer is 77
15: answer is 447
16: answer is 4444
17: no solution
18: answer is 477
19: answer is 4447
20: answer is 44444
And here's the (rough) digit count for a desired sum of four billion, well over half a billion digits:
pax:~> export LC_NUMERIC=en_US.UTF8
pax:~> printf "%'.f\n" $(./test47.py 4000000000 | wc -c)
571,428,597
If you really need a C++ solution, see below. I wouldn't advise using this if this is course-work, instead suggesting you convert the algorithm shown above into your own code (for reasons previously mentioned). This is provided just to show the similar approach in C++:
#include <iostream>
int main(int argc, char *argv[]) {
// Get desired sum from command line, defaulting to 22.
int desiredSum = 22;
if (argc >= 2) desiredSum = atoi(argv[1]);
// Init sevens to get at or beyond desired sum, fours to zero,
// and the sum based on that.
int numSevens = (desiredSum + 6) / 7, numFours = 0;
int thisSum = numSevens * 7 + numFours * 4;
// Continue until a solution is found.
while (thisSum != desiredSum) {
if (thisSum > desiredSum) {
// Too high, remove a seven if possible, exit if not.
if (numSevens == 0) {
std::cout << desiredSum << ": no solution\n";
return 0;
}
--numSevens; thisSum -= 7;
} else {
// Too low, add a four.
++numFours; thisSum += 4;
}
}
// Only get here if solution found, so print lowest
// possible number that matches four / seven count.
std::cout << desiredSum << ": answer is ";
while (numFours-- > 0) std::cout << 4;
while (numSevens-- > 0) std::cout << 7;
std::cout << '\n';
}
Of course, you can be even more clever when you realise that the maximum number of fours will be six, and that you can add one to the sum-of-digits by removing one seven and adding two fours.
So simply:
work out the number of sevens required to get at or just below the desired sum;
add a single four if that will still keep you at or below the desired sum;
then adjust by enough actions of "remove one seven and add two fours" until you get to that desired sum (keeping in mind you may already be there). This will be done exactly once for each unit the shortfall in your current sum (how far it is below the desired sum) so, if the shortfall was two, you would remove two sevens and add four fours (- 14 + 16 = 2). That means you can use a simple mathematical formula rather than a loop.
if that formula results in a negative count of sevens, there was no solution, otherwise use the counts as previously mentioned to form the lowest number (fours followed by sevens).
Just Python for this solution, given how easy it is:
import sys
# Get desired number.
desiredNum = int(sys.argv[1])
# Work out seven and four counts as per description in text.
numSevens = int(desiredNum / 7) # Now within six of desired sum.
shortFall = desiredNum - (numSevens * 7)
numFours = int(shortFall / 4) # Now within three of desired sum.
shortFall = shortFall - numFours * 4
# Do enough '+7-4-4's to reach desired sum (none if already there).
numSevens = numSevens - shortFall
numFours = numFours + shortFall * 2
# Done, output solution, if any.
if numSevens < 0:
print(f"{desiredNum}: No solution")
else:
print(f"{desiredNum}: {'4' * numFours}{'7' * numSevens}")
That way, no loop is required at all. It's all mathematical reasoning.
If I understand the question correctly, you are searching for the smallest number x which contains only the numbers 4 and 7 and the sum of its digits N. The smallest number is for sure written as:
4...47...7
and consists of m times 4 and n times 7. So we know that N = n · 4 + m · 7.
Here are a couple of rules that apply:
(n + m) · 7 ≥ N :: This is evident, just replace all 4's by 7's.
(n + m) · 4 ≤ N :: This is evident, just replace all 7's by 4's.
(n + m) · 7 − N = m · (7 − 4) :: in other words (m+n) · 7 − N needs to be divisible by 7 − 4
So with these two conditions, we can now write the pseudo-code very quickly:
# always assume integer division
j = N/7 # j resembles n+m (total digits)
if (N*7 < N) j++ # ensure rule 1
while ( (j*4 <= N) AND ((j*7 - N)%(7-4) != 0) ) j++ # ensure rule 2 and rule 3
m = (j*7 - N)/(7-4) # integer division
n = j-m
if (m>=0 AND n>=0 AND N==m*4 + n*7) result found
Here is a quick bash-awk implementation:
$ for N in {1..30}; do
awk -v N=$N '
BEGIN{ j=int(N/7) + (N%7>0);
while( j*4<=N && (j*7-N)%3) j++;
m=int((j*7-N)/3); n=j-m;
s="no solution";
if (m>=0 && n>=0 && m*4+n*7==N) {
s=""; for(i=1;i<=j;++i) s=s sprintf("%d",(i<=m?4:7))
}
print N,s
}'
done
1 no solution
2 no solution
3 no solution
4 4
5 no solution
6 no solution
7 7
8 44
9 no solution
10 no solution
11 47
12 444
13 no solution
14 77
15 447
16 4444
17 no solution
18 477
19 4447
20 44444
21 777
22 4477
23 44447
24 444444
25 4777
26 44477
27 444447
28 7777
29 44777
30 444477
The constraints for sum are 1 ≤ n ≤ 106
It means that you might have to find and print numbers with more than 105 digits (106 / 7 ≅ 142,857). You can't store those in a fixed-sized integral type like long long, it's better to directly generate them as std::strings composed by only 4 and 7 characters.
Some mathematical properties may help in finding a suitable algorithm.
We know that n = i * 4 + j * 7.
Of all the possible numbers generated by each combination of i digits four and j digits seven, the minimum is the one with all the fours at left of all the sevens. E.g. 44777 < 47477 < 47747 < ... < 77744.
The minimal lucky number has at max six 4 digits, because, even if the sum of their digits is equal, 4444444 > 7777.
Now, let's introduce s = n / 7 (integer division) and r = n % 7 (the remainder).
If n is divisible by 7 (or when r == 0), the lucky number is composed only by exactly s digits (all 7).
If the remainder is not zero, we need to introduce some 4. Note that
If r == 4, we can just put a single 4 at the left of s sevens
Every time we substitute (if we can) a single 7 with two 4s, the sum of the digits increases by 1.
We can calculate exactly how many 4 digits we need (6 at max) without a loop.
This is enough to write an algorithm.
#include <string>
struct lucky_t
{
long fours, sevens;
};
// Find the minimum lucky number (composed by only 4 and 7 digits)
// that has the sum of digits equal to n.
// Returns it as a string, if exists, otherwise return "-1".
std::string minimum_lucky(long n)
{
auto const digits = [multiples = n / 7L, remainder = n % 7L] {
return remainder > 3
? lucky_t{remainder * 2 - 7, multiples - remainder + 4}
: lucky_t{remainder * 2, multiples - remainder};
} ();
if ( digits.fours < 0 || digits.sevens < 0 )
{
return "-1";
}
else
{
std::string result(digits.fours, '4');
result.append(digits.sevens, '7');
return result;
}
}
Tested here.

I'm trying to sum up all the logs of the first 10 primes. Program not working

The result I'm getting after running the code in powershell is three numbers and then a freeze.
The numbers are
1.09861228867,
1.60943791243,
1.94591014906
#summing up all the logs of the first 10 primes, excluding 2.
from math import * # library imports
import math # " "
count = 1 #1-9 gives us 9 primes in total
numb = 3 #3 is the second prime number.
logy_of_prime_sum_total = 0 #sum of log of primes starts at zero
logy = 0 #first value of log is zero
while count != 10: #loops 9 times, for a total of 9 primes.
for k in range(2,numb): #from 2 up to but not including numb.
if numb%k == 0: #purpose is to skip the else if not prime.
break #break takes us out of the for/else disj.
else:
logy_of_prime_sum_total = logy+logy_of_prime_sum_total
logy = math.log(numb) #when the else activates, we are
print(logy) #dealing with a prime, getting a value
numb += 2 #the log of it, incr. numb. by two,
count += 1 #because primes have to be odd,
#increase the count
print logy_of_prime_sum_total #print the sum of all the logs after the
#while end.
The only issue you have is you are not increasing numb if numb happens to be composite. Just move numb += 2 to the end of the loop and unindent. When numb is composite, e.g. 9 (4th number) you are breaking out of the for loop and never incrementing numb (else on a for loop only executes if you don't break):
while count != 10:
for k in range(2,numb): #from 2 up to but not including prime.
if numb%k == 0:
break
else:
logy_of_prime_sum_total = logy+logy_of_prime_sum_total
logy = math.log(numb)
print(logy)
count += 1
numb += 2
print(logy_of_prime_sum_total)
Output:
1.0986122886681098
1.6094379124341003
1.9459101490553132
2.3978952727983707
2.5649493574615367
2.833213344056216
2.9444389791664403
3.1354942159291497
3.367295829986474
18.529951519569238
BTW: You need to start the count = 0 to get 10 numbers, you will currently only get 9.

How to improve the efficiency of c++ code (find the largest prime factor)? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
How to improve the efficiency of this code?
Suppose you have to deal with really big inputs.
‪#‎include‬ <iostream>
using namespace std;
int main()
{ //This program finds out the largest prime factor of given input
long int n,big=1;
cin>>n;
for (long int i=2;i<n;i=i+2)
{
if (n%i==0)
big=i;
}
cout<<big;
return 0;
}
For a start, I don't think the code you have is giving you the largest prime factor anyway, it's simply giving you the largest factor, whether that be prime or composite(1).
If you're after the largest prime factor (or zero if there is none), it can be found by repeated integer division, similar to the way many implementations of the UNIX factor program work, and along the lines of:
def largestPrimeFactor(n):
if n < 2: return 0 # Special case
denom = 2 # Check every denominator
big = 0
while denom * denom <= n: # Beyond sqrt, no factors exist
if n % denom == 0: # Check factor, then divide
big = denom
n = n / denom
else:
denom = denom + 1 # Or advance to next number
if n > big: # What's left may be bigger
big = n
return big
If you wanted even more efficiency, you can change the way you modify the denominator each time through the loop. Once you've checked two, every other prime must be odd so you could avoid rechecking even numbers, effectively halving the time taken:
def largestPrimeFactor(n):
if n < 2: return 0 # Special case
while n % 2 == 0: n = n / 2 # Check and discard twos
if n == 1: return 2 # Return if it was ALL twos
denom = 3 # Check every denominator
big = 0
while denom * denom <= n: # Beyond sqrt, no factors exist
if n % denom == 0: # Check factor, then divide
big = denom
n = n / denom
else:
denom = denom + 2 # Or advance to next odd number
if n > big: # What's left may be bigger
big = n
return big
There's also another method which skips more composites and it relies on the mathematical fact that, other than 2 and 3, every other prime number is of the form 6n±1(2).
Some composites also have this form, such as 25 and 33, but you can wear a small amount of inefficiency here.
While the change to using odd numbers shaved off 50% from the original effort, this one shaves off another 33% from the odd-number variant:
def largestPrimeFactor(n):
if n < 2: return 0 # Special case
while n % 2 == 0: n = n / 2 # Check and discard twos
if n == 1: return 2 # Return if it was ALL twos
while n % 3 == 0: n = n / 3 # Check and discard threes
if n == 1: return 3 # Return if it was ALL threes
denom = 5 # Check every denominator
adder = 2 # Initial value to add
big = 0
while denom * denom <= n: # Beyond sqrt, no factors exist
if n % denom == 0: # Check factor, then divide
big = denom
n = n / denom
else:
denom = denom + adder # Or advance to next denominator,
adder = 6 - adder # alternating +2, +4
if n > big: # What's left may be bigger
big = n
return big
The trick here is to start at five, alternately adding two at four:
vv vv (false positives)
5 7 11 13 17 19 23 25 29 31 35 37 41 ...
9 15 21 27 33 39 (6n+3, n>0)
and you can see it skipping every third odd number (9, 15, 21, 27, ...) since it's a multiple of three, which is where the 33% further reduction comes from. You can also see the false positives for primes (25 and 33 in this case but more will happen).
(1) Your original heading called for the largest even prime factor and the most efficient code for finding that would be the blindingly fast:
if (n % 2 == 0)
cout << 2 << '\n';
else
cout << "None exists\n";
(since there's only one even prime). However, I doubt that's what you really wanted.
(2) Divide any non-negative number by six. If the remainder is 0, 2 or 4, then it's even and therefore non-prime (2 is a special case here):
6n + 0 = 2(3n + 0), an even number.
6n + 2 = 2(3n + 1), an even number.
6n + 4 = 2(3n + 2), an even number.
If the remainder is 3, then it is divisible by 3 and therefore non-prime (3 is a special case here):
6n + 3 = 3(2n + 1), a multiple of three.
That leaves just the remainders 1 and 5, and those numbers are all of the form 6n±1. So, handling 2 and 3 as special cases, start at 5 and alternately add 2 and 4 and you can guarantee that all the primes (and a few composites) will be caught in the net.

Very large execution time differences for virtually same C++ and Python code

I was trying to write a solution for Problem 12 (Project Euler) in Python. The solution was just too slow, so I tried checking up other people's solution on the internet. I found this code written in C++ which does virtually the same exact thing as my python code, with just a few insignificant differences.
Python:
def find_number_of_divisiors(n):
if n == 1:
return 1
div = 2 # 1 and the number itself
for i in range(2, n/2 + 1):
if (n % i) == 0:
div += 1
return div
def tri_nums():
n = 1
t = 1
while 1:
yield t
n += 1
t += n
t = tri_nums()
m = 0
for n in t:
d = find_number_of_divisiors(n)
if m < d:
print n, ' has ', d, ' divisors.'
m = d
if m == 320:
exit(0)
C++:
#include <iostream>
int main(int argc, char *argv[])
{
unsigned int iteration = 1;
unsigned int triangle_number = 0;
unsigned int divisor_count = 0;
unsigned int current_max_divisor_count = 0;
while (true) {
triangle_number += iteration;
divisor_count = 0;
for (int x = 2; x <= triangle_number / 2; x ++) {
if (triangle_number % x == 0) {
divisor_count++;
}
}
if (divisor_count > current_max_divisor_count) {
current_max_divisor_count = divisor_count;
std::cout << triangle_number << " has " << divisor_count
<< " divisors." << std::endl;
}
if (divisor_count == 318) {
exit(0);
}
iteration++;
}
return 0;
}
The python code takes 1 minute and 25.83 seconds on my machine to execute. While the C++ code takes around 4.628 seconds. Its like 18x faster. I had expected the C++ code to be faster but not by this great margin and that too just for a simple solution which consists of just 2 loops and a bunch of increments and mods.
Although I would appreciate answers on how to solve this problem, the main question I want to ask is Why is C++ code so much faster? Am I using/doing something wrongly in python?
Replacing range with xrange:
After replacing range with xrange the python code takes around 1 minute 11.48 seconds to execute. (Around 1.2x faster)
This is exactly the kind of code where C++ is going to shine compared to Python: a single fairly tight loop doing arithmetic ops. (I'm going to ignore algorithmic speedups here, because your C++ code uses the same algorithm, and it seems you're explicitly not asking for that...)
C++ compiles this kind of code down to a relatively few number of instructions for the processor (and everything it does probably all fits in the super-fast levels of CPU cache), while Python has a lot of levels of indirection it's going through for each operation. For example, every time you increase a number it's checking that the number didn't just overflow and need to be moved into a bigger data type.
That said, all is not necessarily lost! This is also the kind of code that a just-in-time compiler system like PyPy will do well at, since once it's gone through the loop a few times it compiles the code to something similar to what the C++ code starts at. On my laptop:
$ time python2.7 euler.py >/dev/null
python euler.py 72.23s user 0.10s system 97% cpu 1:13.86 total
$ time pypy euler.py >/dev/null
pypy euler.py > /dev/null 13.21s user 0.03s system 99% cpu 13.251 total
$ clang++ -o euler euler.cpp && time ./euler >/dev/null
./euler > /dev/null 2.71s user 0.00s system 99% cpu 2.717 total
using the version of the Python code with xrange instead of range. Optimization levels don't make a difference for me with the C++ code, and neither does using GCC instead of Clang.
While we're at it, this is also a case where Cython can do very well, which compiles almost-Python code to C code that uses the Python APIs, but uses raw C when possible. If we change your code just a little bit by adding some type declarations, and removing the iterator since I don't know how to handle those efficiently in Cython, getting
cdef int find_number_of_divisiors(int n):
cdef int i, div
if n == 1:
return 1
div = 2 # 1 and the number itself
for i in xrange(2, n/2 + 1):
if (n % i) == 0:
div += 1
return div
cdef int m, n, t, d
m = 0
n = 1
t = 1
while True:
n += 1
t += n
d = find_number_of_divisiors(t)
if m < d:
print n, ' has ', d, ' divisors.'
m = d
if m == 320:
exit(0)
then on my laptop I get
$ time python -c 'import euler_cy' >/dev/null
python -c 'import euler_cy' > /dev/null 4.82s user 0.02s system 98% cpu 4.941 total
(within a factor of 2 of the C++ code).
Rewriting the divisor counting algorithm to use divisor function makes the run time reduces to less than 1 second. It is still possible to make it faster, but not really necessary.
This is to show that: before you do any optimization trick with the language features and compiler, you should check whether your algorithm is the bottleneck or not. The trick with compiler/interpreter is indeed quite powerful, as shown in Dougal's answer where the gap between Python and C++ is closed for the equivalent code. However, as you can see, the change in algorithm immediately give a huge performance boost and lower the run time to around the level of algorithmically inefficient C++ code (I didn't test the C++ version, but on my 6-year-old computer, the code below finishes running in ~0.6s).
The code below is written and tested with Python 3.2.3.
import math
def find_number_of_divisiors(n):
if n == 1:
return 1
num = 1
count = 1
div = 2
while (n % div == 0):
n //= div
count += 1
num *= count
div = 3
while (div <= pow(n, 0.5)):
count = 1
while n % div == 0:
n //= div
count += 1
num *= count
div += 2
if n > 1:
num *= 2
return num
Here's my own variant built on nhahtdh's factor-counting optimization plus my own prime factorization code:
def prime_factors(x):
def factor_this(x, factor):
factors = []
while x % factor == 0:
x /= factor
factors.append(factor)
return x, factors
x, factors = factor_this(x, 2)
x, f = factor_this(x, 3)
factors += f
i = 5
while i * i <= x:
for j in (2, 4):
x, f = factor_this(x, i)
factors += f
i += j
if x > 1:
factors.append(x)
return factors
def product(series):
from operator import mul
return reduce(mul, series, 1)
def factor_count(n):
from collections import Counter
c = Counter(prime_factors(n))
return product([cc + 1 for cc in c.values()])
def tri_nums():
n, t = 1, 1
while 1:
yield t
n += 1
t += n
if __name__ == '__main__':
m = 0
for n in tri_nums():
d = factor_count(n)
if m < d:
print n, ' has ', d, ' divisors.'
m = d
if m == 320:
break

How to calculate first n prime numbers?

Assume the availability of a function is_prime. Assume a variable n has been associated with a positive integer. Write the statements needed to compute the sum of the first n prime numbers. The sum should be associated with the variable total.
Note: is_prime takes an integer as a parameter and returns True if and only if that integer is prime.
Well, I wrote is_prime function like this:
def is_prime(n):
n = abs(n)
i = 2
while i < n:
if n % i == 0:
return False
i += 1
return True
but it works except for n==0. How can I fix it to make it work for every integer?
I'm trying to find out answers for both how to write function to get the sum of first n prime numbers and how to modify my is_prime function, which should work for all possible input, not only positive numbers.
Your assignment is as follows.
Assume the availability of a function is_prime. Assume a variable n has been associated with a positive integer. Write the statements needed to compute the sum of the first n prime numbers. The sum should be associated with the variable total.
As NVRAM rightly points out in the comments (and nobody else appears to have picked up on), the question states "assume the availability of a function is_prime".
You don't have to write that function. What you do have to do is "write the statements needed to compute the sum of the first n prime numbers".
The pseudocode for that would be something like:
primes_left = n
curr_num = 2
curr_sum = 0
while primes_left > 0:
if is_prime(curr_num):
curr_sum = curr_sum + curr_num
primes_left = primes_left - 1
curr_num = curr_num + 1
print "Sum of first " + n + " primes is " + curr_sum
I think you'll find that, if you just implement that pseudocode in your language of choice, that'll be all you have to do.
If you are looking for an implementation of is_prime to test your assignment with, it doesn't really matter how efficient it is, since you'll only be testing a few small values anyway. You also don't have to worry about numbers less than two, given the constraints of the code that will be using it. Something like this is perfectly acceptable:
def is_prime(num):
if num < 2:
return false
if num == 2:
return true
divisor = 2
while divisor * divisor <= num:
if num % divisor == 0:
return false
divisor = divisor + 1
return true
In your problem statement it says that n is a positive integer. So assert(n>0) and ensure that your program outer-loop will never is_prime() with a negative value nor zero.
Your algorithm - trial division of every successive odd number (the 'odd' would be a major speed-up for you) - works, but is going to be very slow. Look at the prime sieve for inspiration.
Well, what happens when n is 0 or 1?
You have
i = 2
while i < n: #is 2 less than 0 (or 1?)
...
return True
If you want n of 0 or 1 to return False, then doesn't this suggest that you need to modify your conditional (or function itself) to account for these cases?
Why not just hardcode an answer for i = 0 or 1?
n = abs(n)
i = 2
if(n == 0 || n == 1)
return true //Or whatever you feel 0 or 1 should return.
while i < n:
if n % i == 0:
return False
i += 1
return True
And you could further improve the speed of your algorithm by omitting some numbers. This script only checks up to the square root of n as no composite number has factors greater than its square root if a number has one or more factors, one will be encountered before the square root of that number. When testing large numbers, this makes a pretty big difference.
n = abs(n)
i = 2
if(n == 0 || n == 1)
return true //Or whatever you feel 0 or 1 should return.
while i <= sqrt(n):
if n % i == 0:
return False
i += 1
return True
try this:
if(n==0)
return true
else
n = abs(n)
i = 2
while i < n:
if n % i == 0:
return False
i += 1
return True