Homework for Uni, so I'm not asking anyone to solve it for me but any tips would be really appreciated.
I have to find all 'quite good' (not perfect) numbers between 1-1000000. A 'quite good' number is one which is close to perfect, with a 'badness' of anywhere between 1-10 (i.e. 6 is perfect because its factors 1+2+3 = 6; 15 is 'quite good', because its factors 1+3+5 = 9 so it has a 'badness of 6; 32 is 'quite good' because its factors 1+2+4+8+16 = 31 so it has a 'badness' of 1).
The problem I'm having is that the test environment has a time limit of ~10 seconds before it times out.
With a 'badness' of 0 I have no problems, because only the actual perfect numbers are required and I get them in about 100ms.
With a 'badness' of 1, I also have no problems, because only the actual perfect numbers _and_ powers of 2 are required, that takes about 200ms.
Anything beyond that, I'm stumped. Up to about 100,000 I seem to be fine but then the code just takes forever (easily more than a minute at 300,000). I can't think of any algorithm to speed it up, because the 'badness' can vary hugely. I can't make my for statement (i = 0; i <= arga; i+= 2), because some numbers in the desired solution are multiples of 3 and 5.
HELP!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
void func2(int arga, int argb) {
int a = 0;
for (int i = 2; i < arga; i++) {
for (int j = 1; j < i; j++) {
if (i % j == 0) {
a += j;
}
}
if ((i > 1) && (a >= (i - argb)) && (a <= (i + argb))) {
cout << i << " ";
}
a = 0;
}
cout << endl;
}
printing is very slow. Redirect to a file might save you.
still thinking on the actual algorithm.
you should be able to re-arrange the 2 loops so that the if statement inside is more often true (I%j == 0)
if I > 1 is not possible to fail. remove it. (I starts at 2)
make arga and argb constants.
I%1 == 0, always. Is that exploitable?
if I is prime, the inner loop only hits on 1.
one stupid way to do this is to have a list of the first N primes and skip them (do the %1 increment though). Even if its just the first 10000 or something, the time save would be notable. If your check for prime is slow, you don't gain anything.
can the inner loop be up to sqrt I? I think this is true, and I think it is the key to getting this done.
Tip: For fast repeated prime factorisation, first generate all prime numbers up to the square root of the upper bound, say, 1'000'000 (use a sieve algorithm)
The "square root thing" is the maximum value you need to check to determine whether it is PRIME. However, that's not what you are doing here. Your method is a brute-force search for proper divisors, and the largest proper divisor of any even number n is n/2, not sqrt(n).
You can, however, stop searching once your sum exceeds "maximum badness", and you will save a lot of computer resources not having to work out the pow() function.
so take 100.
100/2 is 2 and 50 paired, so yes, n/2 is correct to pick up the divisors. But you already saw 50 when you checked 2, if set up your algorithm to look in pairs.
if you check up to its square root using that concept, I think you get additional lift.
the factors are 1, 2, 4,5,10 and their "others" 100,50,25,20,10
Does that cut the work down farther or am I misunderstanding the actual problem at hand? On even a very low value like 100, that saves 40 iterations!
Ah yes - I missed that: you have a good point @jonnin. As long as you take the divisors in pairs it would take less iterations. Perhaps to avoid the overhead of computing sqrt(n) you could look out for when the divisors "crossed" each other.
sqrt is built into x86 FPU. It does not cost all that much. Its a good idea but I would bet the FPU can outperform any logic you spend avoiding it. The only time ive avoided a root and boosted performance successfully was by comparing the squared numbers instead of the roots of the numbers when checking distances. I don't see a way around it here. Even a lookup table of roots (and sin/cos) had worse performance than the FPU (yes, I tried it, lol).
You're correct as far as I can see. The algorithm I ended up using finds the lowest divisor, then works out the inverse (e.g. 36 can be divided by 2, which also gives the inverse that 18 is a factor of 36).
No need for me to calculate all the iterations up to i <= (n/2), since I already know the value of n/2 from my first calculation.
Oh, and thanks again for your advice, I was stuck in laborious for() loops that were killing my time limit!
@lastchance thanks, but the real honour goes to Eratosthenes, all I did was adapt his sieve for primes - that bloke was smart!
( I had to delete my earlier post but the problem I had was eventually so simple to solve - I had one too many zeroes in the 1'0000'000 - hard for me to pick at the time. )