converting a number base 10 to base 62 (a-zA-Z0-9)
OLD: A quick and dirty solution can be to use a function like this:
function toChars($number) {
$res = base_convert($number, 10,26);
$res = strtr($res,'0123456789','qrstuvxwyz');
return $res;
}
The base convert translate your number to a base where the digits are 0-9a-p then you get rid of the remaining digits with a quick char substitution.
As you may observe, the function is easily reversible.
function toNum($number) {
$res = strtr($number,'qrstuvxwyz','0123456789');
$res = base_convert($number, 26,10);
return $res;
}
By the way, what would you use this function for?
Edit:
Based on the question change and on the @jnpcl answer, here is a set of functions that performs the base conversion without using pow and log (they take half the time to complete the tests).
The functions work for integer values only.
function toBase($num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$r = $num % $b ;
$res = $base[$r];
$q = floor($num/$b);
while ($q) {
$r = $q % $b;
$q =floor($q/$b);
$res = $base[$r].$res;
}
return $res;
}
function to10( $num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$limit = strlen($num);
$res=strpos($base,$num[0]);
for($i=1;$i<$limit;$i++) {
$res = $b * $res + strpos($base,$num[$i]);
}
return $res;
}
The test:
for ($i = 0; $i<1000000; $i++) {
$x = toBase($i);
$y = to10($x);
if ($i-$y)
echo "\n$i -> $x -> $y";
}
A simpler (and possibly faster) implementation that does not use pow
nor log
:
function base62($num) {
$index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$res = '';
do {
$res = $index[$num % 62] . $res;
$num = intval($num / 62);
} while ($num);
return $res;
}
http://us3.php.net/manual/en/function.base-convert.php#52450
<?php
// Decimal > Custom
function dec2any( $num, $base=62, $index=false ) {
if (! $base ) {
$base = strlen( $index );
} else if (! $index ) {
$index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ,0 ,$base );
}
$out = "";
// this fix partially breaks when $num=0, but fixes the $num=238328 bug
// also seems to break (adds a leading zero) at $num=226981 through $num=238327 *shrug*
// for ( $t = floor( log10( $num ) / log10( $base - 1 ) ); $t >= 0; $t-- ) {
// original code:
for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
$a = floor( $num / pow( $base, $t ) );
$out = $out . substr( $index, $a, 1 );
$num = $num - ( $a * pow( $base, $t ) );
}
return $out;
}
?>
Parameters:
$num
- your decimal integer
$base
- base to which you wish to convert$num
(leave it 0 if you are providing$index
or omit if you're using the default (62))
$index
- if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "zyxwvu")
<?php
// Custom > Decimal
function any2dec( $num, $base=62, $index=false ) {
if (! $base ) {
$base = strlen( $index );
} else if (! $index ) {
$index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $base );
}
$out = 0;
$len = strlen( $num ) - 1;
for ( $t = 0; $t <= $len; $t++ ) {
$out = $out + strpos( $index, substr( $num, $t, 1 ) ) * pow( $base, $len - $t );
}
return $out;
}
?>
Parameters:
$num
- your custom-based number (string) (ex.: "11011101")
$base
- base with which$num
was encoded (leave it 0 if you are providing$index
or omit if you're using default (62))
$index
- if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "abcdef")
For big numbers, you might want to use the PHP BC library
function intToAny( $num, $base = null, $index = null ) {
if ( $num <= 0 ) return '0';
if ( ! $index )
$index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
if ( ! $base )
$base = strlen( $index );
else
$index = substr( $index, 0, $base );
$res = '';
while( $num > 0 ) {
$char = bcmod( $num, $base );
$res .= substr( $index, $char, 1 );
$num = bcsub( $num, $char );
$num = bcdiv( $num, $base );
}
return $res;
}