Not an answer.
This is not an answer to the question, but to other answers...
This is not regular on SE, but I think this could be usefull in the meaning of PPCG.
First
I've found 12 495 numbers which are palindrome in at least two of all five representations and containing at least three characters, between 1
and 99 999 999 999
Some samples:
printf "%o\n" 94466666449
1277651567721
echo $(( 64#1rIGIr1 ))
98459895489
printf "%o\n" 0x1557557551
1252725272521
My modified (perl) version of Dom Hastings's answer took ~90 seconds on my desktop...
Of course original version of same script is able to browse approx from 1
to 60 000
by using the same time, on same pc.
Here is a need to think out of the box...
My point of view:
My system is not so small:
CPU Intel(R) Core(TM)2 Duo CPU E8400 @ 3.00GHz
Dual core run on 64 bits GNU/Linux
With 4Gb RAM.
Comparissions.
For making this something equitable, I will render outputs there to be able to make some comparissions.
For rendering output, I will use the following command:
time ( /tmp/palin-XXX.YY 20000000 2 2 | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
I will try to respect publication order.
First: WolframH's Python answer
@@ -1 +1,3 @@
-mindigits, minpalins = 2, 3
+mindigits, minpalins, maxint = 2, 2, 20000000
@@ -4 +6 @@
-from itertools import count
@@ -15 +17 @@
-for i in count(8**(mindigits-1)):
+for i in range(8**(mindigits-1), maxint ):
Than:
time ( ./palin.py 20000000 2 2 | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
2 19119 1241049 19140681 111010111 1001001000001000001001001
2 19899 1248249 19169865 111101111 1001001001000001001001001
2 19999 1249249 19173961 111111111 1001001001001001001001001
1150
real 3m0.768s
user 2m48.895s
sys 0m0.560s
Second: Dom Hastings's perl answer
@@ -15 +15 @@
-while (++$i) {
+while (++$i<20000000) {
Give:
time ( perl ./palin-dom-b.pl | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
2 19119 1241049 19140681 111010111 1001001000001000001001001
2 19899 1248249 19169865 111101111 1001001001000001001001001
2 19999 1249249 19173961 111111111 1001001001001001001001001
1149
real 14m58.174s
user 14m54.512s
sys 0m0.412s
Third: David Carraher's Mathematica
Unfortunely, I don't know how to test this, but as I could read, there seem not to be different algorithm...
That's little a surprise, becaud David warned my about maximum and this is an important notion in this question.
Fourth: MtnViewMark's Haskell
Whithout any modification and after compilation conforming to answer indication:
time ( ./palin.haskel | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
2 19119 1241049 19140681 111010111 1001001000001000001001001
2 19899 1248249 19169865 111101111 1001001001000001001001001
2 19999 1249249 19173961 111111111 1001001001001001001001001
1149
real 0m26.631s
user 0m24.802s
sys 0m0.124s
Hmm, this seem to be very interesting, in that: I didn't know haskell
before.
But this miss something in them of operation/output
My results:
I've wrote two versions. The first prototype was bash (yes I could be quicker using bash than a python version;-):
time ( ./palin.sh 20000000 2 2 | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
2 (19899) (1248249) (19169865) 111101111 1001001001000001001001001
2 (19999) (1249249) (19173961) 111111111 1001001001001001001001001
List len: 46572, Output len: 1149, Operations: 173548: ratio: 151.04
1150
real 1m40.052s
user 1m9.588s
sys 0m13.249s
... and this version is penalized by an operation counter: I count 173 548 operation for computing all values under all 5 base, from 1
to 20 000 000
!!!
And there is same work with a perl version:
time ( perl ./palin-d-shrink.pl 20000000 2 2 | tee >(tail -n3 >/dev/fd/3) | wc -l ) 3>&1
2 (19119) (1241049) (19140681) 111010111 1001001000001000001001001
2 (19899) (1248249) (19169865) 111101111 1001001001000001001001001
2 (19999) (1249249) (19173961) 111111111 1001001001001001001001001
1149
real 0m0.631s
user 0m0.612s
sys 0m0.016s
Recap:
First python 3m0.768s
First perl 14m58.174s
Mathematica -- unknow (but seem not having less oper/lines ) --
Haskel 0m26.631s (this is compiled)
My bash prototyp 1m40.052s -> 1m16.067s see Nota under bash explanation
My perl (shrinked) 0m0.631s
I insist: here is a need to think out of the box!!
You was the first to rewrite your code to build all possible values in a drawing fashion first before searching for values that match requirement.
There is my perl shrinked version:
#!/usr/bin/perl -w
use strict;my%vars;my$maxr=999999;my$minn=3;my$minl=3;$maxr=$1if$ARGV[0]&&$ARGV
[0]=~/^(\d+)$/;$minn=$1if$ARGV[1]&&$ARGV[1]=~/^(\d+)$/;my%value;$minl=$1if$ARGV
[2]&&$ARGV[2]=~/^(\d+)$/;my@digits=("0".."9","a".."z","A".."Z","@","_");my$i=0;
map{$value{$_}=$i++}@digits;sub base{my($n,$base)=@_;my$str='';do{$str=$digits[
$n%$base].$str}while$n=int($n/$base);$str}sub rbase{my($num,$base)=@_;my$out=0;
$out = $out * $base + $value{$1} while $num =~ s /^(.)//;$out}sub ebase{defined
($vars{$_[1]}{$_[0]})?$vars{$_[1]}{$_[0]}:"(".base(@_).")"}sub palinlst{my$base
=pop;my$maxst=base($maxr,$base);my$half=int(length($maxst)/2+.5);my($lasteven,$
lastodd)=do{length($maxst)%2? (rbase($digits[$base-1]x ($half-1), $base),rbase(
substr($maxst,0,$half), $base)): (rbase(substr($maxst, 0,$half),$base), rbase($
digits[$base-1]x$half, $base))}; foreach my$p($lasteven+1..$lastodd){ my$part1=
base($p,$base); my$part2=reverse $part1; my$str=$part1.substr($part2,1);my$val=
rbase($str,$base);if(length($str)>=$minl){$vars{$base}{$val}=$str;$vars{'count'
}{$val}++}};foreach my$p(1..$lasteven){my$part1=base($p,$base);my$part2=reverse
$part1;my$str=$part1.$part2; my$val=rbase($str,$base);if(length($str)>=$minl){$
vars{$base}{$val}=$str;$vars{'count'}{$val}++};$part2=reverse$part1;$str=$part1
.substr($part2,1);$val=rbase($str,$base);if(length($str)>=$minl){$vars{$base}{$
val}=$str;$vars{'count'}{$val}++;}}};for my$b(qw(2 8 10 16 64)){palinlst$b;}map
{printf"%-5s %-6s %-6s %-8s %-8s %s\n",$vars{'count'}{$_},ebase($_,64),ebase($_
,16),ebase($_,10),ebase($_,8),ebase($_,2);}grep{$vars{'count'}{$_}>=$minn}sort{
$a<=>$b}keys$vars{'count'}; # (C) 2013 - LGPL v2 - palindrome@F-Hauri.ch
This is shrinked, not obfuscated! You could make them readable by running perltidy
. I use this form to make cut'n paste easier.
perltidy < ./palin-shrinked.pl
I think this become readable.
Explanation (using bash for readabilty;-) 1m40.052s 1m16.067s
As the final version is 139 lines long, I will split them there in order to explain each part:
#!/bin/bash
printf -v RANGEND "%u" ${1:-1000} # Max decimal value of palin to compute
MINCNT=${2:-2} # Minimum number of palindrome by output line default:2
MINLEN=${3:-2} # Minimum length of palindrome default:2
OUTFMT="%-5s %-6s %-6s %-8s %-8s %s\n"
opcount=0
shopt -s extglob
int2b64 () { local _i _func='{ local _lt=({0..9} {a..z} {A..Z} @ _) _var _out;
printf -v _var "%022o" $1 ;_out="'
for ((_i=0; _i<22; _i+=2)) ;do
printf -v _func '%s${_lt[8#${_var:%d:2}]}' "$_func" $_i
done
_func+='";printf ${2:+-v} $2 "%s" ${_out##*(0)}; }'
eval "${FUNCNAME}()" $_func
$FUNCNAME $@ ; }
int2bin () { local _fmt="" _i
for _i in {0..7}
do
fmt+="_var=\${_var//$_i/%s};"
done
printf -v fmt "$fmt\n" 000 001 010 011 100 101 110 111
eval "${FUNCNAME}()"'{ local _var;printf -v _var "%o" $1;
'${fmt}'printf ${2+-v} $2 "%s" ${_var##*(0)}; }'
$FUNCNAME $@ ; }
There is the first functions, I use the trick to re-define $FUNCNAME
before first use.
rev() { local _func='{ printf ${2+-v} $2 "%s" ' _i
for ((_i=64;_i--;)) do _func+='${1:'$_i':1}' ; done
eval "rev()" $_func ';}'; rev $@ ; }
int2hex() { printf ${2+-v} $2 "%x" $1 ; }
int2oct() { printf ${2+-v} $2 "%o" $1 ; }
int2dec() { printf ${2+-v} $2 "%u" $1 ; }
Some more function, easy to understand, The hard part is comming now:
makePalinList() {
local max=${1:-1000} vmax palin
local forms=('' '' bin '' '' '' '' '' oct '' dec '' '' '' '' '' hex
''{,,,}{,,}{,,}{,}b64)
local digmax=('' '' 1 '' '' '' '' '' 7 '' 9 '' '' '' '' '' f
''{,,,}{,,}{,,}{,}_)
local cmd=${forms[$2]}
((opcount+=5))
int2$cmd $max vmax
with this, I make that int$form[64]
will be expanded before execution to intb64
(under bash, there is no need to use eval
there).
vmax
is the max value to render. So now we have to compute each case (even and odd string length):
local vpart=$((5*${#vmax}+5))
local vpskel=${vmax:0:${vpart%?}}
local lasteven lastodd
if ((${#vmax}%2)) ;then
lastodd=$(($2#$vpskel))
vpskel=${vpskel:1}
lasteven=$(($2#${vpskel//?/${digmax[$2]}}))
else
lasteven=$(($2#$vpskel))
lastodd=$(($2#${vpskel//?/${digmax[$2]}}))
fi
I will let you think about all this... Now we have to compute start of ranges
local firsteven=$(((MINLEN+1)/2)) firstodd=$((MINLEN/2+1))
printf -v firsteven "%${firsteven}s" ""
printf -v firstodd "%${firstodd}s" ""
firsteven=${firsteven// /0}
firsteven=${firsteven/0/1}
firsteven=$(($2#$firsteven))
firstodd=${firstodd// /0}
firstodd=${firstodd/0/1}
firstodd=$(($2#$firstodd))
Wow.
Nota: My previous (not published) version was not using firsteven
and firstodd
but simply 1..
and a in loop condition: ${#palin} -ge $MAXLEN
before each printf -v lst$cmd[...
.
Adding this and the following if
at end of next part improved my script a lot: from ~100 seconds, my script only take 76 seconds now for computing previous test 20000000 2 2
Now there could be an issue where next if
could not match:
if [ $firstodd -lt $lasteven ] ;then
for ((i=firsteven;i<firstodd;i++)) ;do
int2$cmd $i b
rev $b r
palin=$b$r
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
((opcount+=3))
done
for ((i=firstodd;i<=lasteven;i++)) ;do
int2$cmd $i b
rev $b r
palin=$b$r
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
palin=$b${r:1}
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
((opcount+=4))
done
for ((i=lasteven+1;i<=lastodd;i++)) ;do
int2$cmd $i b
rev $b r
palin=$b${r:1}
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
((opcount+=4))
done
And if else:
else
for ((i=firsteven;i<=lasteven;i++)) ;do
int2$cmd $i b
rev $b r
palin=$b$r
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
((opcount+=3))
done
for ((i=firstodd;i<=lastodd;i++)) ;do
int2$cmd $i b
rev $b r
palin=$b${r:1}
printf -v lst$cmd[$(($2#$palin))] $palin
((counts[$(($2#$palin))]++))
done
fi
That's all (for now)...
}
for base in 2 8 10 16 64; do makePalinList $RANGEND $base ;done
Now we just have to print all values (adding parenthesis if value have to be computer -> ie not already exist -> ie not match requirement.)...
lines=0;for i in ${!counts[@]};do
[ ${counts[i]} -ge $MINCNT ] && \
printf "%-5s %-6s %-6s %-8s %-8s %s\n" ${counts[i]} \
${lstb64[i]:-($(int2b64 $i))} \
${lsthex[i]:-($(int2hex $i))} \
${lstdec[i]:-($i)} \
${lstoct[i]:-($(int2oct $i))} \
${lstbin[i]:-($(int2bin $i))} && ((lines++))
done
if [ $lines -gt 0 ] ;then ratio=000$((opcount*1000/lines))
printf "List len: %s, Output len: %d, Operations: %d: ratio: %.2f\n" \
${#counts[@]} $lines $opcount ${ratio:0:${#ratio}-3}.${ratio:${#ratio}-3}
else echo no output; fi
Ok, there is a zipped version of this bash script, to make cut'n paste easy:
#!/usr/bin/perl -w
my$u="";open my$st,"|gunzip";sub d{my$l=pack("c",32+.75*length($_[0]));print$st
unpack("u",$l.$_[0]);"";};while(<DATA>){tr#A-Za-z0-9+/##cd; tr#A-Za-z0-9+/# -_#
;$u.=$_;while($u=~s/(.{80})/d($1)/egx){};};d($u);__DATA__
H4sIAHg8jlICA9VX227bRhB9Fr9iQq1C0jLFiy9xxBCx0TpFgdgpCueltiBQ0soiwosgUoJbYv+9s7u8
iZbjh6JFKkgid3fmzMzZ4XC2/8aahYk1C7KVoqw3YZIvwdzB71e3v1zf/gzqYKsCKZyx6di2zaAPN8ET
LOg8jIMIdkG0pZAuYR1EYQJ5CvM0Xm9zCsrNr7c/3d75pHDHpiv0wiSMtzEk23hGN7XSYpPGFGZ/QrrN
URNwiiL+MthG+djlMJ+vbxHmpAMT0eQxX3VgWopfvt59urnz1YF5lsHAPK//Lqq/7CFRlXQ9T7dJ7ttK
tkrXOZgZ0Kf8MUpnioJcuLPzU9ANKCBK5xjwNITpcpvMfa2eiXJfL+zR6D2DIhiN/sLL1Wj0B4NLmBow
3QUbmGJsngKvfBryhZI6sF03RfId8DiAr2oCYpluQNenoW976M4H1+WXoe8aBniLVDmAxh0GbZCRAp29
v+jjFQ2MB4uxyyZMA5UIETQ1DYX+Ik2ouBHzQ19TvRKOb+fQ3DEgLjqY8dTgvvX7R7ptMA+Y9JFiYiBs
8ekrJsHVzTXTDY7O0cQ6qRaAXAJqSaoxg9pUL2OMWcXglB6PGalHAc70O6b0WpEKUuJ86Ks8LP9BhmdZ
GI01yJincmkMqNdQguLoHv5jCgDmNf4csB28Og5gouMPrzh2HEfpHYqm2X+05T3bOrFv391yjRRon2kN
sV1eEanNa+8ZacqG7vYIKxPzBUgNyu2tM+j81JuGpulh6izSerP5064hedrYweTwmmyQPAij1W6C5jHN
A5xr7+SKPgm/DjnyJDO6lEzn+YuS6Z4klpwXJbe1pBIH3+hvvCB8DjMBLTyXBMXBk98qZTscy+LRkkFu
4szXNQ3wyzNS3tVfdJhf0JvuCgat9DStOD4+ZkX9Y1hAjBb+InzkbpQGnC7IO/73vju7PASMxQU/Leh5
vMDwRAD3xJ0wRdfL6jb0zwzpBGeSoCAQHjxnQGkh7NbBJveJrp8dkaLPV9mw0qwksm80QjN8cWyP8Ybr
DD4y1pKKgiynO5qIm3SxkKaXmHQV7ECUq3xFE6VXCnG7xO0TaQKt9hpj4gbTUQpz6Eq6XLOsjxYpJLki
eFa6TaOMPtdqbHSMvwa3lE9QO2HCTYOty9fV0DEsHqBYK9HliuXimoRqVaMKAlMZN7AaMXxmVfWQLELi
ZC2Mw5Zsy6EGy7LAstl31m3LebYsKaknagqqoGrrXfTO6j52i+1qXCJXaXIP9QqYUQ6kzieYyKQBpSdf
Croe+rWDXvih0vPC4bB8Ifb28z6EmZwSNWsGGzkSdcAnM1KNa8ajLOea96XPQtAwJiDvpLSui+cs6wpx
JyqJ+lk84XPyjdQJQjj+wa/C7Qbxg4RS4xcb8Uj+y3SdHqKromjoVIT9o03/7yNpatPhRH4xCf53mfwj
bkxrE7CiM4X7jicgyjtMFy6Ad37ngO0R74v2Wgog1cmICAVPQCn81JJhR86BRJ9KijelA5cTVvXlWNiK
cjacMDAfKRB5TsLC9vYtPNQ96ivHln0cVCMFkoKtBo7Hpk706uhCQsNoBLBFaQvgsCOAbU0pELZmsedp
q/EWaF8N+6Q9wzx+IYAx6brgRvBeniwUWePFPJKQg13X9U2Qh6mP7Rmp0+yId2uWEN5/c6piP/AcOEZS
juFLeX4U4wWO11SgJRkfjyU03o7cJWfwQR5Ein6zTZVLpLSMq1KJ9zl9ccvMEzaqptuTCn+Ygc5XKSRp
eZb1eHL9DRLsWT9gDwAA
You could simply run:
perl >/tmp/palin-bash.sh
than paste previous script into your console.
1
Better use “base 64” instead of “base64”. At least for me base64(65)="NjU=" vs. 65₆₄=11 are different.
– manatwork – 2013-11-12T14:44:13.997@manatwork: The base64 encoding that many programming languages use, encodes the ASCII values of the characters. – Qqwy – 2013-11-12T15:29:29.433
1“This have to be explained” Indeed. Please explain. – J B – 2013-11-12T15:59:48.077
@manatwork If you use regular base64 for encoding 65 (as ascii value of
A
) you becomeQQ
, if you use bash's base64, you obtain11
(echo $((64#11))
). Both result are palindrome. At this level (two bytes, upto 16 bits, it's easy to understand: 11 are translation of QQ. Other sample:Dec 4097 = 10001 octal = 00010000 00000001 binary = $'\020\1' ascii to uuencode to "EAE"
palindromic too.) – F. Hauri – 2013-11-12T16:35:58.763@JB If your algo have a better ratio operation count / output line count than others, you have to explain how. – F. Hauri – 2013-11-12T16:43:51.127
1And how do you count operations? – J B – 2013-11-12T16:44:51.823
@JB As natural I can, but mainly in term of algorithm! I know this could be discussed. But I will try to stay honest (and listen for comment before making a wrong vote ;) For sample: swapping two variable is one operation, regardless of how this is effectively done (using a third variable or a language syntaxe like
(a,b)=(b,a)
). – F. Hauri – 2013-11-12T17:07:20.843Is there some upper bound on the size of the numbers here, or are we supposed to carry on all the way up to infinity? – Gareth – 2013-11-12T18:48:43.903
@Gareth He wants an enumeration. That is, for each number that fits this palindromic criteria, there exists some amount of time, such that your program will output it eventually. Also, each natural number should correspond to a unique palindromic number – Cruncher – 2013-11-12T19:26:05.873
What do you mean by "Finding all numbers"? Is it sufficient to provide a test for any positive integer? – DavidC – 2013-11-13T02:48:52.157
@DavidCarraher I'm not totally sure about the answer to this question, but in my mind, I believe no (I have to think a little more). – F. Hauri – 2013-11-13T03:15:00.540
1Then what is the range of numbers we should examine? – DavidC – 2013-11-13T03:37:18.923
@DavidCarraher You're right! I add this on question. Hopefull, no one for now do consideration of this. – F. Hauri – 2013-11-13T07:12:21.247
Sample:
dec 719848917 = oct 5272002725 = bin 101010111010000000010111010101
was found in browsing from0
to1.000.000.000
in less than a 30 secs on a Pentium 4 @3Ghz (but taked 3 minutes on my old PentiumIII 500MHz). – F. Hauri – 2013-11-18T10:38:21.793I'm not sure I see where there can be any significant variation in operations/output: The computation required is very rigidly specified, and there is no room for algorithmic variation. – MtnViewMark – 2013-11-19T03:50:11.897
@MtnViewMark Having a maximum of range is an important thing. If your script require, to make the job for a range from 0 to 20'000'000, more than 5 seconds, there is something to improve... – F. Hauri – 2013-11-19T04:12:52.357
I think I got it now. Thank you for forcing us all to think out of the box! – MtnViewMark – 2013-11-21T15:52:04.277