HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Finding palindromic numbers most efficiently

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
efficientlynumbersfindingmostpalindromic

Problem

After reading why is my project euler #4 program not working?, I was contemplating the most efficient way to approach this (finding the target palindrome as early as possible).

Is this snippet about as efficient as it gets? I am only considering the merits of the logic, not the choice of language. It would be easier (but not as efficient) to brute-force it with two nested loops.

Also, is this logic common enough for it to have a name?

#!/usr/bin/perl
use strict;
$^W = 1;
$\ = "\n";

my ($n) = @ARGV;
$n = 3 if (! defined $n || $n  8);
my ($lo,$hi,$lm);
$lo = substr('10000000000',0,$n);
$hi = substr('99999999999',0,$n);
$lm = substr('100000000000000000000',0,$n*2);

my ($a,$b,$x) = ($hi,$hi);
for (;;) {
    $x = $a*$b;
    if ($x eq reverse $x) {
        print "$x is $a * $b" if ($x > $lm);
        last if ($n > 3); # show them all for small numbers
    }
    next if (!(--$a  $hi)); # no short-cut operator here
    ++$a; --$b;
    print "  edge $a $b" if ($n < 3); # show edge for small numbers
    $a += $b; $b = int --$a/2; $a -= $b;
    last if ($a*$b < $lm);
}

Solution

Here, you first check if $x is palindrome, but then you will only print it if $x > $lm:

if ($x eq reverse $x) {
    print "$x is $a * $b" if ($x > $lm);
    last if ($n > 3); # show them all for small numbers
}


It would be more efficient to do the other way around, checking $x > $lm first:

if ($x > $lm) {
    if ($x eq reverse $x) {
        print "$x is $a * $b";
        last if ($n > 3); # show them all for small numbers
    }
}


With \$n = 3\$, this makes a big difference: it saves reversing $x 23602 times.

I also tried rewriting the reverse operation,
somewhat inspired by the the duck's comment:
instead of comparing an entire string with its reverse,
it should be more efficient to compare its first half with the reverse of its second half:

sub is_palindrome {
    my ($word) = (@_);
    my $l = length($word);
    foreach my $i (0 .. $l / 2) {
        if (substr($word, $i, 1) ne substr($word, $l - $i - 1, 1)) {
            return;
        }
    }
    return 1;
}


Although this function does only half of the comparisons as in $x eq reverse $x,
at least as interpreted code, this cannot compete with the speed of the compiled reverse function. Using this function the script runs much slower, unfortunately.

Code Snippets

if ($x eq reverse $x) {
    print "$x is $a * $b" if ($x > $lm);
    last if ($n > 3); # show them all for small numbers
}
if ($x > $lm) {
    if ($x eq reverse $x) {
        print "$x is $a * $b";
        last if ($n > 3); # show them all for small numbers
    }
}
sub is_palindrome {
    my ($word) = (@_);
    my $l = length($word);
    foreach my $i (0 .. $l / 2) {
        if (substr($word, $i, 1) ne substr($word, $l - $i - 1, 1)) {
            return;
        }
    }
    return 1;
}

Context

StackExchange Code Review Q#63304, answer score: 2

Revisions (0)

No revisions yet.