У меня есть номер в базе 10. Нужно ли перевести его на базу 62?
Пример:
echo convert(12324324); // returns Yg3 (fantasy example here)
base_convert()
PHP может конвертировать до основания 36.
быстрым и грязным решением может быть использование такой функции:
function toChars($number) { $res = base_convert($number, 10,26); $res = strtr($res,'0123456789','qrstuvxwyz'); return $res; }
Базовый конвертер преобразует ваш номер в базу, где цифры 0-9a-p, тогда вы избавитесь от оставшихся цифр с быстрой заменой символов.
Как вы можете заметить, функция легко обратима.
function toNum($number) { $res = strtr($number,'qrstuvxwyz','0123456789'); $res = base_convert($number, 26,10); return $res; }
Кстати, для чего бы вы использовали эту функцию?
Редактировать:
Основываясь на изменении вопроса и ответе @jnpcl, набор функций выполняет базовое преобразование без использования pow и log (они занимают половину времени для завершения тестов).
Функции работают только для целочисленных значений
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; }
Тест
for ($i = 0; $i<1000000; $i++) { $x = toBase($i); $y = to10($x); if ($i-$y) echo "\n$i -> $x -> $y"; }
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 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")
Для больших чисел вы можете использовать библиотеку PHP BC
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; }
function convertBase10ToBase62($num){ $charset="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $endChar=$charset[$num%62]; $rtn=""; if ( $num == "62" ) { $rtn=$rtn.$charset[1]; } else if ( $num >= 62 ) { $rtn=$rtn.$charset[intval($num/62)%62+1]; } $num=intval($num/62); while ($num > 61) { if ( is_int($num/62) == true ) { $rtn=$rtn.$charset[0]; } else { $rtn=$rtn.$charset[$num%62]; } $num=intval($num/62); } $rtn=$rtn.$endChar; echo "\n"; echo $rtn; return $rtn; }
Более простая (и, возможно, более быстрая) реализация, которая не использует pow
и log
:
function base62($num) { $index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $res = ''; do { $res = $index[$num % 62] . $res; $num = intval($num / 62); } while ($num); return $res; }
Эта функция выводит то же, что и GNU Multiple Precision, если это возможно …
<?php function base_convert_alt($val,$from_base,$to_base){ static $gmp; static $bc; static $gmp62; if ($from_base<37) $val=strtoupper($val); if ($gmp===null) $gmp=function_exists('gmp_init'); if ($gmp62===null) $gmp62=version_compare(PHP_VERSION,'5.3.2')>=0; if ($gmp && ($gmp62 or ($from_base<37 && $to_base<37))) return gmp_strval(gmp_init($val,$from_base),$to_base); if ($bc===null) $bc=function_exists('bcscale'); $range='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($from_base==10) $base_10=$val; else { $n=strlen(($val="$val"))-++$ratio; if ($bc) for($i=$n;$i>-1;($ratio=bcmul($ratio,$from_base)) && $i--) $base_10=bcadd($base_10,bcmul(strpos($range,$val[$i]),$ratio)); else for($i=$n;$i>-1;($ratio*=$from_base) && $i--) $base_10+=strpos($range,$val[$i])*$ratio; } if ($bc) do $result.=$range[bcmod($base_10,$to_base)]; while(($base_10=bcdiv($base_10,$to_base))>=1); else do $result.=$range[$base_10%$to_base]; while(($base_10/=$to_base)>=1); return strrev($to_base<37?strtolower($result):$result); } echo base_convert_alt('2661500360',7,51); // Output Hello
в<?php function base_convert_alt($val,$from_base,$to_base){ static $gmp; static $bc; static $gmp62; if ($from_base<37) $val=strtoupper($val); if ($gmp===null) $gmp=function_exists('gmp_init'); if ($gmp62===null) $gmp62=version_compare(PHP_VERSION,'5.3.2')>=0; if ($gmp && ($gmp62 or ($from_base<37 && $to_base<37))) return gmp_strval(gmp_init($val,$from_base),$to_base); if ($bc===null) $bc=function_exists('bcscale'); $range='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($from_base==10) $base_10=$val; else { $n=strlen(($val="$val"))-++$ratio; if ($bc) for($i=$n;$i>-1;($ratio=bcmul($ratio,$from_base)) && $i--) $base_10=bcadd($base_10,bcmul(strpos($range,$val[$i]),$ratio)); else for($i=$n;$i>-1;($ratio*=$from_base) && $i--) $base_10+=strpos($range,$val[$i])*$ratio; } if ($bc) do $result.=$range[bcmod($base_10,$to_base)]; while(($base_10=bcdiv($base_10,$to_base))>=1); else do $result.=$range[$base_10%$to_base]; while(($base_10/=$to_base)>=1); return strrev($to_base<37?strtolower($result):$result); } echo base_convert_alt('2661500360',7,51); // Output Hello
в<?php function base_convert_alt($val,$from_base,$to_base){ static $gmp; static $bc; static $gmp62; if ($from_base<37) $val=strtoupper($val); if ($gmp===null) $gmp=function_exists('gmp_init'); if ($gmp62===null) $gmp62=version_compare(PHP_VERSION,'5.3.2')>=0; if ($gmp && ($gmp62 or ($from_base<37 && $to_base<37))) return gmp_strval(gmp_init($val,$from_base),$to_base); if ($bc===null) $bc=function_exists('bcscale'); $range='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($from_base==10) $base_10=$val; else { $n=strlen(($val="$val"))-++$ratio; if ($bc) for($i=$n;$i>-1;($ratio=bcmul($ratio,$from_base)) && $i--) $base_10=bcadd($base_10,bcmul(strpos($range,$val[$i]),$ratio)); else for($i=$n;$i>-1;($ratio*=$from_base) && $i--) $base_10+=strpos($range,$val[$i])*$ratio; } if ($bc) do $result.=$range[bcmod($base_10,$to_base)]; while(($base_10=bcdiv($base_10,$to_base))>=1); else do $result.=$range[$base_10%$to_base]; while(($base_10/=$to_base)>=1); return strrev($to_base<37?strtolower($result):$result); } echo base_convert_alt('2661500360',7,51); // Output Hello
имеют массив символов, таких как:
$chars = array( 1 => 'a', 2 => 'b', //.... 27 => 'A', 28 => 'B' ); function getCharacter($key) { if(array_key_exists($key, $chars[$key])) return $chars[$key]; return false; } function getNumber($char) { return array_search($char, $chars); }
Он почти не тестировался и работает на реальном большом продукте. Просто скопируйте эти функции и используйте. При необходимости вы можете упорядочить $ baseChars последовательно, мне нужно это для смешанного.
/** * decToAny converter * * @param integer $num * @param string $baseChars * @param integer $base * @return string */ function decToAny($num, $baseChars = '', $base = 62, $index = false) { $baseChars = empty($baseChars) ? 'HbUlYmGoAd0ScKq6Er5PuZp3OsQCh4RfNMtV8kJiLv9yXeI1aWgFj2zTx7DnBw' : $baseChars; if (!$base) { $base = strlen($index); } else if (!$index) { $index = substr($baseChars, 0, $base); } $out = ""; 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; }
Обратный метод
/** * anyTodec converter * * @param string $num * @param string $baseChars * @param integer $base * @return string */ function anyToDec($num, $baseChars = '', $base = 62, $index = false) { $baseChars = empty($baseChars) ? 'HbUlYmGoAd0ScKq6Er5PuZp3OsQCh4RfNMtV8kJiLv9yXeI1aWgFj2zTx7DnBw' : $baseChars; if (!$base) { $base = strlen($index); } else if (!$index) { $index = substr($baseChars, 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; }
function convertBase10ToBase62($num){ $charset="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $rtn=""; $n=$num;$base=62; while($n>0){ $temp=$n%$base; $rtn=$charset[$temp].$rtn; $n=intval($n/$base); } return $rtn; }
вfunction convertBase10ToBase62($num){ $charset="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $rtn=""; $n=$num;$base=62; while($n>0){ $temp=$n%$base; $rtn=$charset[$temp].$rtn; $n=intval($n/$base); } return $rtn; }