J'ai un nombre en base 10. Est-il possible de le traduire en base 62?
Exemple:
echo convert(12324324);
// returns Yg3 (fantasy example here)
PHP base_convert()
peut convertir jusqu'à la base 36.
OLD : Une solution rapide et sale peut être d'utiliser une fonction comme celle-ci:
function toChars($number) {
$res = base_convert($number, 10,26);
$res = strtr($res,'0123456789','qrstuvxwyz');
return $res;
}
La conversion de base traduit votre numéro en une base où les chiffres sont 0-9a-p, puis vous vous débarrassez des chiffres restants avec une substitution de caractères rapide.
Comme vous pouvez le constater, la fonction est facilement réversible.
function toNum($number) {
$res = strtr($number,'qrstuvxwyz','0123456789');
$res = base_convert($number, 26,10);
return $res;
}
Au fait, à quoi utiliseriez-vous cette fonction?
Modifier:
Sur la base du changement de question et de la réponse @jnpcl, voici un ensemble de fonctions qui effectue la conversion de base sans utiliser pow et log (elles prennent la moitié du temps pour terminer les tests).
Les fonctions fonctionnent uniquement pour les valeurs entières.
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;
}
Le test:
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#5245
<?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;
}
?>
Paramètres:
$num
- votre entier décimal
$base
- base dans laquelle vous souhaitez convertir$num
(laissez 0 si vous fournissez$index
ou omis si vous utilisez la valeur par défaut (62))
$index
- si vous souhaitez utiliser la liste de chiffres par défaut (0-1a-zA-Z), omettez cette option, sinon fournissez une chaîne (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;
}
?>
Paramètres:
$num
- votre numéro personnalisé (chaîne) (ex.: "11011101")
$base
- base avec laquelle$num
a été encodé (laissez-le 0 si vous fournissez$index
ou omis si vous utilisez la valeur par défaut (62))
$index
- si vous souhaitez utiliser la liste de chiffres par défaut (0-1a-zA-Z), omettez cette option, sinon fournissez une chaîne (ex .: "abcdef")
Cette fonction génère la même chose que GNU Multiple Precision si possible…
<?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
Une implémentation plus simple (et peut-être plus rapide) qui n'utilise ni pow
ni log
:
function base62($num) {
$index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$res = '';
do {
$res = $index[$num % 62] . $res;
$num = intval($num / 62);
} while ($num);
return $res;
}
Pour les grands nombres, vous voudrez peut-être utiliser la bibliothèque 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;
}
Il a été à peine testé et fonctionne sur de très gros produits. Copiez simplement ces fonctions et utilisez-les. Si nécessaire, vous pouvez organiser séquentiellement $ baseChars, j'en ai besoin pour le mélange.
/**
* 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;
}
Méthode inverse
/**
* 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;
}
avoir un tableau de caractères comme:
$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);
}
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;
}
Si vous avez l'extension gmp:
gmp_strval(gmp_init($x, 10), 62)