【问题标题】:converting a number base 10 to base 62 (a-zA-Z0-9)将基数 10 转换为基数 62 (a-zA-Z0-9)
【发布时间】:2011-06-25 06:03:33
【问题描述】:

我有一个以 10 为底的数字。有没有办法将其转换为以 62 为底的数字?

例子:

echo convert(12324324);
// returns Yg3 (fantasy example here)

PHP 的 base_convert() 最多可以转换为基数 36。

【问题讨论】:

  • 所以您想使用 A-Z 中的所有字符将 base 10 转换为 base 24?
  • 应该将“11”转换为“aa”还是“k”?
  • @jnpcl 我需要节省空间(例如:bit.ly urls)
  • 问题#2985316 可能有一些有用的信息。
  • @yes123 @Pekka 你知道正常的英文字母有 26 个字符吗?

标签: php encoding character-encoding numeric alphanumeric


【解决方案1】:

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;
}
?>

参数:

$num - 你的十进制整数

$base - 您希望将$num 转换为的基数(如果您提供$index,则将其保留为0,如果您使用默认值(62)则省略)

$index - 如果您希望使用默认数字列表 (0-1a-zA-Z),请忽略此选项,否则提供字符串(例如:“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;
}
?>

参数:

$num - 您的自定义号码(字符串)(例如:“11011101”)

$base - 编码$num 的基数(如果您提供$index,则将其保留为0,如果您使用默认值(62)则省略)

$index - 如果您希望使用默认数字列表 (0-1a-zA-Z),请忽略此选项,否则提供字符串(例如:“abcdef”)

【讨论】:

  • 它仅对 1 个数字不起作用:它是 238328。dec2any(238328) 返回“00”,而 any2dec("00") 返回 0。该死
  • 因为“pow($base, $len - $t);”在 any2dec
  • 是的,我们可以在 any2dec 中添加一个 if 来修复这个错误,但这并不优雅,无论如何我想我找到了另一个 100% 工作的函数,但它似乎更慢。如果你以优雅的方式修复它,请写在这里:)
  • 在我的回答中尝试使用 base10 转换而不使用 pow。我不确定我的 dec2any 版本是否比 jnpci one 快
  • 我找到了238328 案例的“修复”,但它似乎在其他地方中断了。我不了解算法背后的数学原理,所以我不知道它为什么会起作用。编辑了我上面的代码。
【解决方案2】:

有一个字符数组,例如:

$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);
}

【讨论】:

  • $chars = array_merge(range(0,9), range('a','z'), range('A,'Z')); 我喜欢range() :)
【解决方案3】:

OLD:一个快速而肮脏的解决方案可能是使用这样的函数:

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";
}

【讨论】:

  • 您的 toBase 函数似乎返回的结果与 php 内置的用于低于 36 的基数的结果略有不同。为什么会这样?
  • 我刚刚发现了我的问题。 PHP 的 mod 函数显然对超过 2^31 的数字有问题。使用 bcmod 函数解决了这个问题。
  • @ToddB:你是正确的。固定
  • 是的,运行良好:codepad.org/Lj9qRd2n(我删除了if($i-$y) 检查)
  • 这种“快速而肮脏”的解决方案不符合要求并且不可逆。问题是在 Base 62 中编码。
【解决方案4】:

对于大数字,您可能需要使用 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;
}

【讨论】:

  • $res .= substr( $index, $char, 1 );应该是 $res = substr( $index, $char, 1 ) 。 $res;
  • 我尝试将某些内容转换为base62,然后将其转换回base10。它返回一个 0。所以,这显然是单向的。此外,使用它在网站中提供简码会很好,我可以在其中打乱 $index.html。但是,如果没有可逆性,除非我使用短代码作为 ID,否则很难在表中找到 ID。
  • @Lance Rushing 指出你的回报是倒置的,你可以在最终回报中应用他的修复或 strrev
【解决方案5】:
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;
}

【讨论】:

  • 我认为这不起作用:convertBase10ToBase62(11942787908393) -> "qpYq5goH" (应该是 "3og5qYpH")
【解决方案6】:

不使用powlog 的更简单(可能更快)的实现:

function base62($num) {
  $index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  $res = '';
  do {
    $res = $index[$num % 62] . $res;
    $num = intval($num / 62);
  } while ($num);
  return $res;
}

【讨论】:

  • 确实很相似(只需将62 替换为$b),我读的答案太快了,只是不那么冗长了。我的比较是与使用 powlog 的方法进行比较,这些方法已知很慢,并且在 Eineki 的回答中进行了比较。你可以删除这个,因为它没有提供大量的新信息。
  • 对于使用 bcmath 的大数字:bcscale(0); do { $res = $index[bcmod($q,62)].$res; $q = bcdiv($q,62); } while ($q); bcscale(3);
【解决方案7】:
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;
}

【讨论】:

    【解决方案8】:

    如果可能,此函数的输出与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
    

    【讨论】:

      【解决方案9】:

      它几乎没有经过测试,适用于真正的大产品。 只需复制此功能并使用。 如果需要,你可以按顺序排列$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;
          }
      

      【讨论】:

        【解决方案10】:

        如果你有 gmp 扩展:

        gmp_strval(gmp_init($x, 10), 62)
        

        【讨论】:

          猜你喜欢
          • 2015-02-08
          • 2015-01-07
          • 2021-11-09
          • 2010-12-11
          • 2011-11-09
          • 2012-03-26
          • 1970-01-01
          • 2019-11-27
          • 1970-01-01
          相关资源
          最近更新 更多