Much of this comes from what 1201ProgramAlarm mentioned.
Notably, changing:
if (is_palindrome(product) && product > largest_palindrome)
Into:
if ((product > largest_palindrome) && is_palindrome(product))
produces an 8x speedup!
This is because, now, the "lightweight" test is performed first, and, due to "short circuit" evaluation of the if
, the [heavyweight] is_palindrome
call is suppressed.
To minimize the effects of outputing to std::cout
, I've added a "solution" struct that remembers i
, j
, and product
and prints all results at the end of a given test. (i.e.) We're timing just the algorithm and not the algorithm plus the I/O time.
I've also added a timeit
function and moved the actual code into functions so multiple algorithms can be compared. timeit
also runs the test algorithm multiple times and takes the shortest time to minimize the effects of timeslicing.
I've shown a progression of improvements.
Anyway, here's the refactored code:
// Largest palindrome product (4)
#include <iostream>
#include <time.h>
struct sol {
int i;
int j;
int product;
};
int solcnt;
sol solvec[100];
#define SAVE \
do { \
sol *sp = &solvec[solcnt++]; \
sp->i = i; \
sp->j = j; \
sp->product = product; \
} while (0)
// Determine if a given number is a palindrome or not
bool
is_palindrome(int num)
{
int reverse = 0,
copy = num,
digit;
do {
digit = copy % 10;
reverse = reverse * 10 + digit;
copy /= 10;
} while (copy != 0);
return (reverse == num);
}
void
timeit(void (*fnc)(void),const char *reason)
{
clock_t best = 0;
for (int iter = 0; iter <= 100; ++iter) {
clock_t begin = clock();
solcnt = 0;
fnc();
clock_t end = clock();
clock_t dif = end - begin;
if (iter == 0)
best = dif;
if (dif < best)
best = dif;
}
std::cout << '\n';
std::cout << reason << ':' << '\n';
for (sol *sp = solvec; sp < &solvec[solcnt]; ++sp) {
std::cout << "Found palindrome! " << sp->i << " * " << sp->j << " == "
<< sp->product << '\n';
}
double time_spent = double(best) / CLOCKS_PER_SEC;
std::cout << "Elapsed time is " << time_spent << " seconds." << std::endl;
}
void
fix1(void)
{
int largest_palindrome = 0;
for (int i = 999; i > 99; i--) {
for (int j = 999; j > 99; j--) {
// Optimalization
if (i < largest_palindrome / 1000) {
//std::cout << "No larger palindromes possible." << '\n';
i = 0;
j = 0;
}
else {
int product = i * j;
if (is_palindrome(product) && product > largest_palindrome) {
SAVE;
largest_palindrome = product;
// More optimalization (no product of the current iteration
// of i can be larger than the current palindrome,
// so skip to next iteration)
j = 0;
}
}
}
}
}
void
fix2(void)
{
int largest_palindrome = 0;
for (int i = 999; i > 99; i--) {
// Optimalization
if (i < largest_palindrome / 1000) {
//std::cout << "No larger palindromes possible." << '\n';
break;
}
for (int j = 999; j > 99; j--) {
int product = i * j;
if (is_palindrome(product) && product > largest_palindrome) {
SAVE;
largest_palindrome = product;
// More optimalization (no product of the current iteration
// of i can be larger than the current palindrome,
// so skip to next iteration)
j = 0;
}
}
}
}
void
fix3(void)
{
int largest_palindrome = 0;
for (int i = 999; i > 99; i--) {
// Optimalization
if (i < largest_palindrome / 1000) {
//std::cout << "No larger palindromes possible." << '\n';
break;
}
for (int j = 999; j > 99; j--) {
int product = i * j;
if ((product > largest_palindrome) && is_palindrome(product)) {
SAVE;
largest_palindrome = product;
// More optimalization (no product of the current iteration
// of i can be larger than the current palindrome,
// so skip to next iteration)
j = 0;
}
}
}
}
void
fix4(void)
{
int largest_palindrome = 0;
int largest_div1000 = 0;
for (int i = 999; i > 99; i--) {
// Optimalization
if (i < largest_div1000) {
//std::cout << "No larger palindromes possible." << '\n';
break;
}
for (int j = 999; j > 99; j--) {
int product = i * j;
if ((product > largest_palindrome) && is_palindrome(product)) {
SAVE;
largest_palindrome = product;
largest_div1000 = product / 1000;
// More optimalization (no product of the current iteration
// of i can be larger than the current palindrome,
// so skip to next iteration)
j = 0;
}
}
}
}
int
main(void)
{
timeit(fix1,"fix1 -- original");
timeit(fix2,"fix2 -- moved i < largest_palidrome out of j loop");
timeit(fix3,"fix3 -- reversed order of inner if");
timeit(fix4,"fix4 -- removed divide by 1000 in loop");
return 0;
}
Note that solvec
is a simple array. It might be replaced with std::vector
[or std::array
], but since it's just test jig code, I didn't bother.
Here's the program output:
fix1 -- original:
Found palindrome! 995 * 583 == 580085
Found palindrome! 993 * 913 == 906609
Elapsed time is 0.001755 seconds.
fix2 -- moved i < largest_palidrome out of j loop:
Found palindrome! 995 * 583 == 580085
Found palindrome! 993 * 913 == 906609
Elapsed time is 0.001668 seconds.
fix3 -- reversed order of inner if:
Found palindrome! 995 * 583 == 580085
Found palindrome! 993 * 913 == 906609
Elapsed time is 0.000219 seconds.
fix4 -- removed divide by 1000 in loop:
Found palindrome! 995 * 583 == 580085
Found palindrome! 993 * 913 == 906609
Elapsed time is 0.000222 seconds.