【问题标题】:Base 62 conversionBase 62 转换
【发布时间】:2010-11-10 07:43:10
【问题描述】:

如何将整数转换为以 62 为基数(类似于十六进制,但使用以下数字:'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')。

我一直在尝试为它找到一个好的 Python 库,但它们似乎都忙于转换字符串。 Python base64 模块仅接受字符串并将单个数字转换为四个字符。我正在寻找类似于 URL 缩短器使用的东西。

【问题讨论】:

  • 听起来有人刚刚发现了一个开源项目的想法 :) 如果您发现任何东西或决定创建自己的项目,请告诉我...
  • 如果您想创建短 URL,您可能需要使用不需要编码的整个字符集:en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters。那是 66 个字符。
  • 我想我会传递点和波浪号,以避免用户混淆,但破折号和下划线应该是值得添加的,谢谢。
  • Base64 怎么样?找到相应的库可能会更好。
  • 这个问题有很多适用的答案:stackoverflow.com/questions/561486/…

标签: python math base62


【解决方案1】:

对此没有标准模块,但我已经编写了自己的函数来实现这一点。

BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def encode(num, alphabet):
    """Encode a positive number into Base X and return the string.

    Arguments:
    - `num`: The number to encode
    - `alphabet`: The alphabet to use for encoding
    """
    if num == 0:
        return alphabet[0]
    arr = []
    arr_append = arr.append  # Extract bound-method for faster access.
    _divmod = divmod  # Access to locals is faster.
    base = len(alphabet)
    while num:
        num, rem = _divmod(num, base)
        arr_append(alphabet[rem])
    arr.reverse()
    return ''.join(arr)

def decode(string, alphabet=BASE62):
    """Decode a Base X encoded string into the number

    Arguments:
    - `string`: The encoded string
    - `alphabet`: The alphabet to use for decoding
    """
    base = len(alphabet)
    strlen = len(string)
    num = 0

    idx = 0
    for char in string:
        power = (strlen - (idx + 1))
        num += alphabet.index(char) * (base ** power)
        idx += 1

    return num

请注意,您可以为其指定任何字母表以用于编码和解码。如果您不使用 alphabet 参数,您将获得在第一行代码中定义的 62 个字符的字母表,从而对 62 基进行编码/解码。

希望这会有所帮助。

PS - 对于 URL 缩短器,我发现最好省略一些令人困惑的字符,例如 0Ol1oI 等。因此,我使用这个字母来满足我的 URL 缩短需求 - "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

玩得开心。

【讨论】:

  • +1:很好!这可以使用更多对 URL 友好的字符进行扩展,以可能在这里和那里保存一个字符。我知道安全的字符是:$-_.+!*'(),;/?:@&= 您也可以使用其他字符,例如 []~ 等。
  • 命名错误:它不是 base 62,因为字母表是可定制的。
  • 对于解码,最好不要计算幂(节省时间,写的更短,但更重要的是避免一个错误),因此: num=0;对于字符串中的字符: num = num*base + alphabet.index(char)
  • @ShreevatsaR:使用 str.index() 而不是字典查找有什么特别的原因吗?看我的回答...
  • 乔纳森 - Python可以处理任意长度的号码 - 没有溢出:>>> 256 *(62 ** 100)44402652562862911414971048359760030835982580330786570771137804709455598239929932673552190201125730101070867075377228748911717860448985185350731601887476350502973424822800696272224256L 跨度>
【解决方案2】:

我也曾经写过一个脚本来做这个,我觉得它很优雅:)

import string
# Remove the `_@` below for base62, now it has 64 characters
BASE_LIST = string.digits + string.letters + '_@'
BASE_DICT = dict((c, i) for i, c in enumerate(BASE_LIST))

def base_decode(string, reverse_base=BASE_DICT):
    length = len(reverse_base)
    ret = 0
    for i, c in enumerate(string[::-1]):
        ret += (length ** i) * reverse_base[c]

    return ret

def base_encode(integer, base=BASE_LIST):
    if integer == 0:
        return base[0]

    length = len(base)
    ret = ''
    while integer != 0:
        ret = base[integer % length] + ret
        integer /= length

    return ret

示例用法:

for i in range(100):                                    
    print i, base_decode(base_encode(i)), base_encode(i)

【讨论】:

  • 这个版本比 Baishampayan 接受的解决方案要快得多。我通过在函数之外计算长度进一步优化。测试结果(100,000 次迭代):版本-WoLpH:.403 .399 .399 .398 .398 |版本-Baishampayan:1.783 1.785 1.782 1.788 1.784。这个版本的速度大约是原来的 4 倍。
  • 如果在 base_decode 函数中使用reversed(string) 比切片string[::-1] 更快。
  • 我花了很长时间才找到这个问题。从来不知道这被称为base62转换。很好的答案。
  • 我必须将 integer /= length 更改为 integer //=length 才能得到正确的余数
【解决方案3】:

以下解码器制造商适用于任何合理的基础,具有更整洁的循环,并在遇到无效字符时给出明确的错误消息。

def base_n_decoder(alphabet):
    """Return a decoder for a base-n encoded string
    Argument:
    - `alphabet`: The alphabet used for encoding
    """
    base = len(alphabet)
    char_value = dict(((c, v) for v, c in enumerate(alphabet)))
    def f(string):
        num = 0
        try:
            for char in string:
                num = num * base + char_value[char]
        except KeyError:
            raise ValueError('Unexpected character %r' % char)
        return num
    return f

if __name__ == "__main__":
    func = base_n_decoder('0123456789abcdef')
    for test in ('0', 'f', '2020', 'ffff', 'abqdef'):
        print test
        print func(test)

【讨论】:

  • 虽然我可能永远不会使用它,但我也不得不为你的创造力竖起大拇指。这段代码让我笑了。 :)
  • @Sepero:有什么好笑的?这是一款非常强大的工业级软件。没有 Micky-Mouse 在循环中使用 ** 运算符进行反转。
  • 冷静一下朋友。你说得对。我错过了你的内部循环的真正优点,因为它被埋在与问题无关的东西(包装、错误检查、单元测试)中。
  • 看起来不错,但您是否忘记了一个“工业强度”编码器,它采用整数加字母来生成字符串?
  • 最后一个值中的 q 是否有意展示引发的 ValueError?
【解决方案4】:

如果您正在寻找最高效率(如 django),您将需要类似以下内容。这段代码结合了 Baishampayan Ghose 和 WoLpH 以及 John Machin 的有效方法。

# Edit this list of characters as desired.
BASE_ALPH = tuple("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
BASE_DICT = dict((c, v) for v, c in enumerate(BASE_ALPH))
BASE_LEN = len(BASE_ALPH)

def base_decode(string):
    num = 0
    for char in string:
        num = num * BASE_LEN + BASE_DICT[char]
    return num

def base_encode(num):
    if not num:
        return BASE_ALPH[0]

    encoding = ""
    while num:
        num, rem = divmod(num, BASE_LEN)
        encoding = BASE_ALPH[rem] + encoding
    return encoding

您可能还想提前计算您的字典。 (注意:使用字符串进行编码比使用列表更高效,即使是很长的数字。)

>>> timeit.timeit("for i in xrange(1000000): base.base_decode(base.base_encode(i))", setup="import base", number=1)
2.3302059173583984

在 2.5 秒内编码和解码 100 万个数字。 (2.2Ghz i7-2670QM)

【讨论】:

  • 一开始不一定需要tuple() 周围的BASE_ALPH。在 Python 中,每个字符串都是可迭代的。该功能当然被enumerate() 所利用。所以代码变得更加精简:)
  • 嘿origiNell,你说得对,不需要tuple(),但在我的系统上,它使代码运行速度提高了大约20%。尝试在没有 tuple() 的情况下对其进行测试,看看哪种方法最适合您。干杯:)
  • 有趣的地方。完全有意义,因为元组比字符串更轻量级。感谢您的启发:)!
  • @Sepero 我在格式、命名、测试和功能方面进一步改进了你的版本(支持负数):pastebin.com/4uket7iu(你可以用这个更新你的答案)
  • @Joschua -- 你的 URL 上的代码对我不起作用。 base_encode() 似乎只为我测试的数字生成一个编码数字。
【解决方案5】:

如果你使用 django 框架,你可以使用 django.utils.baseconv 模块。

>>> from django.utils import baseconv
>>> baseconv.base62.encode(1234567890)
1LY7VK

除了base62,baseconv还定义了base2/base16/base36/base56/base64。

【讨论】:

    【解决方案6】:

    如果您只需要生成一个短 ID(因为您提到了 URL 缩短器)而不是编码/解码某些内容,那么这个模块可能会有所帮助:

    https://github.com/stochastic-technologies/shortuuid/

    【讨论】:

    • 我不确定这是否适合短 URL。 UUID 通常是一个非常大的数字,因此即使像他那样对它进行 base57 编码,对于短 URL 来说也必然会相当长。
    • 你可以随心所欲地切割,碰撞仍然不太可能,因为它是纯随机的,但不再是唯一的 id。
    【解决方案7】:

    您可能需要 base64,而不是 base62。它有一个与 URL 兼容的版本,所以额外的两个填充字符应该不是问题。

    过程相当简单;考虑 base64 代表 6 位,一个常规字节代表 8。为所选的 64 个字符中的每一个分配一个从 000000 到 111111 的值,并将 4 个值放在一起以匹配一组 3 个 base256 字节。对每组 3 个字节重复此操作,并在末尾使用您选择的填充字符进行填充(0 通常很有用)。

    【讨论】:

    • 标准的 Python base64 编码方法并不真正适合短 URL,因为它针对编码字节(即字符串/字母)进行了优化,并且会产生比仅仅对数值进行基数移位更长的输出.
    • @mikl 当然,Python 的 base64 模块可能不适合生成短 URL,但 Python 的所有编码方法实际上都适用于 base-256 数字序列。字节实际上是 base-256 编码的“字符串”。 Python 2.x 将字符串视为字节序列,而 Python 3.x(它做了正确的事情)将字符串视为 Unicode。所以 b'foobar' 实际上只是一种奇特的写法 [102, 111, 111, 98, 97, 114] 或 [0x66,0x6f,0x6f,0x62,0x61,0x72] 或 b'\x66\x6f\x6f\ x62\x61\x72' 不出所料是 base-256 表示。字节不是字符串或字母。字节就是字节。 =)
    • @yesudeep:所以,字节就是字节......你的意思到底是什么?
    【解决方案8】:

    现在有一个 python 库。

    我正在为此制作一个 pip 包。

    我建议你使用我的 bases.py https://github.com/kamijoutouma/bases.py,它的灵感来自于 bases.js

    from bases import Bases
    bases = Bases()
    
    bases.toBase16(200)                // => 'c8'
    bases.toBase(200, 16)              // => 'c8'
    bases.toBase62(99999)              // => 'q0T'
    bases.toBase(200, 62)              // => 'q0T'
    bases.toAlphabet(300, 'aAbBcC')    // => 'Abba'
    
    bases.fromBase16('c8')               // => 200
    bases.fromBase('c8', 16)             // => 200
    bases.fromBase62('q0T')              // => 99999
    bases.fromBase('q0T', 62)            // => 99999
    bases.fromAlphabet('Abba', 'aAbBcC') // => 300
    

    参考https://github.com/kamijoutouma/bases.py#known-basesalphabets 什么基础是可用的

    【讨论】:

      【解决方案9】:

      你可以从pypi下载zbase62模块

      例如

      >>> import zbase62
      >>> zbase62.b2a("abcd")
      '1mZPsa'
      

      【讨论】:

      • 是的,我之前看过,但它转换的是字符串,而不是数字 :)
      【解决方案10】:

      我从其他人的帖子中受益匪浅。我最初需要用于 Django 项目的 python 代码,但从那以后我转向 node.js,所以这是代码(编码部分)的 javascript 版本提供了 Baishampayan Ghose。

      var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
      
      function base62_encode(n, alpha) {
        var num = n || 0;
        var alphabet = alpha || ALPHABET;
      
        if (num == 0) return alphabet[0];
        var arr = [];
        var base = alphabet.length;
      
        while(num) {
          rem = num % base;
          num = (num - rem)/base;
          arr.push(alphabet.substring(rem,rem+1));
        }
      
        return arr.reverse().join('');
      }
      
      console.log(base62_encode(2390687438976, "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"));
      

      【讨论】:

      【解决方案11】:

      我希望下面的 sn-p 可以提供帮助。

      def num2sym(num, sym, join_symbol=''):
          if num == 0:
              return sym[0]
          if num < 0 or type(num) not in (int, long):
              raise ValueError('num must be positive integer')
      
          l = len(sym)  # target number base
          r = []
          div = num
          while div != 0: # base conversion
              div, mod = divmod(div, l)
              r.append(sym[mod])
      
          return join_symbol.join([x for x in reversed(r)])
      

      用于您的案例:

      number = 367891
      alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
      print num2sym(number, alphabet)  # will print '1xHJ'
      

      显然,您可以指定另一个字母,由更少或更多的符号组成,然后它将您的数字转换为更少或更大的数字基数。例如,将 '01' 提供为字母表将输出表示输入数字的二进制字符串。

      您最初可以将字母表打乱,以获得您对数字的独特表示。如果您正在制作 URL 缩短服务,它会很有帮助。

      【讨论】:

      • 不错。您可能想使用if num &lt; 0 or type(num) not in (int, long):
      • 这样更好,但它有点复杂,因为 long 在 Py 3.x 中不存在——所以可能需要使用 this answer
      • 或者使用我自己的便携版:isinstance(x, (type(1), type(2**32))).
      【解决方案12】:

      这是我的解决方案:

      def base62(a):
          baseit = (lambda a=a, b=62: (not a) and '0' or
              baseit(a-a%b, b*62) + '0123456789abcdefghijklmnopqrstuvwxyz'
                                    'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[a%b%61 or -1*bool(a%b)])
          return baseit()
      

      解释

      在任何基数中,每个数字都等于 a1+a2*base**2+a3*base**3... 所以目标是找到所有as。

      对于每个N=1,2,3...,代码通过“取模”bb=base**(N+1) 隔离aN*base**N,将所有大于Nas 切片,然后切片所有as,以便每次当前aN*base**N 递归调用函数时,通过减小a,它们的序列号小于N

      Base%(base-1)==1 因此base**p%(base-1)==1 因此q*base^p%(base-1)==q 只有一个例外,当q==base-1 返回0。为了解决这种情况,它返回0。该函数从头开始检查0


      优点

      在这个示例中,只有一次乘法(而不是除法)和一些模运算,它们都相对较快。

      【讨论】:

        【解决方案13】:

        我个人喜欢 Baishampayan 的解决方案,主要是因为剥离了令人困惑的字符。

        为了完整性和性能更好的解决方案,this post 展示了一种使用 Python base64 模块的方法。

        【讨论】:

        • 正如我在对 Williham Totland 的评论中提到的,Python 的 base64 对数字编码不是最理想的,因为它针对字符串进行了优化。
        【解决方案14】:

        我前段时间写了这个,效果很好(包括负面和所有内容)

        def code(number,base):
            try:
                int(number),int(base)
            except ValueError:
                raise ValueError('code(number,base): number and base must be in base10')
            else:
                number,base = int(number),int(base)
            if base < 2:
                base = 2
            if base > 62:
                base = 62
            numbers = [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j",
                       "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
                       "z","A","B","C","D","E","F","G","H","I","J","K","L","M","N",
                       "O","P","Q","R","S","T","U","V","W","X","Y","Z"]
            final = ""
            loc = 0
            if number < 0:
                final = "-"
                number = abs(number)
            while base**loc <= number:
                loc = loc + 1
            for x in range(loc-1,-1,-1):
                for y in range(base-1,-1,-1):
                    if y*(base**x) <= number:
                        final = "{}{}".format(final,numbers[y])
                        number = number - y*(base**x)
                        break
            return final
        
        def decode(number,base):
            try:
                int(base)
            except ValueError:
                raise ValueError('decode(value,base): base must be in base10')
            else:
                base = int(base)
            number = str(number)
            if base < 2:
                base = 2
            if base > 62:
                base = 62
            numbers = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
                       "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v",
                       "w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L",
                       "M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
            final = 0
            if number.startswith("-"):
                neg = True
                number = list(number)
                del(number[0])
                temp = number
                number = ""
                for x in temp:
                    number = "{}{}".format(number,x)
            else:
                neg = False
            loc = len(number)-1
            number = str(number)
            for x in number:
                if numbers.index(x) > base:
                    raise ValueError('{} is out of base{} range'.format(x,str(base)))
                final = final+(numbers.index(x)*(base**loc))
                loc = loc - 1
            if neg:
                return -final
            else:
                return final
        

        对不起,篇幅太长了

        【讨论】:

          【解决方案15】:
          BASE_LIST = tuple("23456789ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz")
          BASE_DICT = dict((c, v) for v, c in enumerate(BASE_LIST))
          BASE_LEN = len(BASE_LIST)
          
          def nice_decode(str):
              num = 0
              for char in str[::-1]:
                  num = num * BASE_LEN + BASE_DICT[char]
              return num
          
          def nice_encode(num):
              if not num:
                  return BASE_LIST[0]
          
              encoding = ""
              while num:
                  num, rem = divmod(num, BASE_LEN)
                  encoding += BASE_LIST[rem]
              return encoding
          

          【讨论】:

          • 这修复了 BASE_LIST 的名称,并且还反转了在 Spero 的其他出色答案中省略的解码字符串
          【解决方案16】:

          这是一种递归和迭代的方法。迭代的速度会快一些,具体取决于执行次数。

          def base62_encode_r(dec):
              s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
              return s[dec] if dec < 62 else base62_encode_r(dec / 62) + s[dec % 62]
          print base62_encode_r(2347878234)
          
          def base62_encode_i(dec):
              s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
              ret = ''
              while dec > 0:
                  ret = s[dec % 62] + ret
                  dec /= 62
              return ret
          print base62_encode_i(2347878234)
          
          def base62_decode_r(b62):
              s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
              if len(b62) == 1:
                  return s.index(b62)
              x = base62_decode_r(b62[:-1]) * 62 + s.index(b62[-1:]) % 62
              return x
          print base62_decode_r("2yTsnM")
          
          def base62_decode_i(b62):
              s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
              ret = 0
              for i in xrange(len(b62)-1,-1,-1):
                  ret = ret + s.index(b62[i]) * (62**(len(b62)-i-1))
              return ret
          print base62_decode_i("2yTsnM")
          
          if __name__ == '__main__':
              import timeit
              print(timeit.timeit(stmt="base62_encode_r(2347878234)", setup="from __main__ import base62_encode_r", number=100000))
              print(timeit.timeit(stmt="base62_encode_i(2347878234)", setup="from __main__ import base62_encode_i", number=100000))
              print(timeit.timeit(stmt="base62_decode_r('2yTsnM')", setup="from __main__ import base62_decode_r", number=100000))
              print(timeit.timeit(stmt="base62_decode_i('2yTsnM')", setup="from __main__ import base62_decode_i", number=100000))
          
          0.270266867033
          0.260915645986
          0.344734796766
          0.311662500262
          

          【讨论】:

          • 我真的很喜欢你的递归方法。我正在学习 AP Comp Sci 的女儿已经为我找到了同样的解决方案,可以在 C++ 中实现“base25”(使用“ABCDEFHJKMNPQRTUVWXY34789”)。我去把它转换成 Python 并且作为一个使用该语言的新手遇到了一些绊脚石——你在一行代码中优雅地解决了这些问题!您甚至可以避免将 0 转换为不以 0-9 开头的字母表中的空字符串的常见问题。做得好! (我不需要负数,但是您的方法非常好,可以为将来的浏览器添加它)
          【解决方案17】:

          Python 3.7.x

          我在寻找existing base62 script 时发现了一些算法的PhD github。目前它不适用于 Python 3 的当前最大版本,所以我继续并修复了需要的地方并进行了一些重构。我通常不使用 Python 并且一直使用它,所以 YMMV。所有功劳归Dr. Zhihua Lai。我刚刚解决了这个版本的 Python 的问题。

          文件base62.py

          #modified from Dr. Zhihua Lai's original on GitHub
          from math import floor
          base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
          b = 62;
          def toBase10(b62: str) -> int:
              limit = len(b62)
              res = 0
              for i in range(limit):
                  res = b * res + base.find(b62[i])
              return res
          def toBase62(b10: int) -> str:
              if b <= 0 or b > 62:
                  return 0
              r = b10 % b
              res = base[r];
              q = floor(b10 / b)
              while q:
                  r = q % b
                  q = floor(q / b)
                  res = base[int(r)] + res
              return res
          

          文件try_base62.py

          import base62
          print("Base10 ==> Base62")
          for i in range(999):
              print(f'{i} => {base62.toBase62(i)}')
          base62_samples = ["gud", "GA", "mE", "lo", "lz", "OMFGWTFLMFAOENCODING"]
          print("Base62 ==> Base10")
          for i in range(len(base62_samples)):
              print(f'{base62_samples[i]} => {base62.toBase10(base62_samples[i])}')
          

          try_base62.py 的输出

          Base10 ==&gt; Base62
          0 =&gt; 0
          [...]
          998 =&gt; g6
          Base62 ==&gt; Base10
          gud =&gt; 63377
          GA =&gt; 2640
          mE =&gt; 1404
          lo =&gt; 1326
          lz =&gt; 1337
          OMFGWTFLMFAOENCODING =&gt; 577002768656147353068189971419611424

          由于 repo 中没有许可信息,我确实提交了PR,因此原作者至少知道其他人正在使用和修改他们的代码。

          【讨论】:

            【解决方案18】:

            抱歉,我无法在此处为您提供图书馆。我更喜欢使用 base64 并在您的选择中添加额外的字符——如果可能的话!

            然后就可以使用base64模块了。

            如果这真的,真的不可能:

            你可以这样自己做(这是伪代码):

            base62vals = []
            myBase = 62
            while num > 0:
               reminder = num % myBase
               num = num / myBase
               base62vals.insert(0, reminder)
            

            【讨论】:

              【解决方案19】:

              简单的递归

              """
              This module contains functions to transform a number to string and vice-versa
              """
              BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
              LEN_BASE = len(BASE)
              
              
              def encode(num):
                  """
                  This function encodes the given number into alpha numeric string
                  """
              
                  if num < LEN_BASE:
                      return BASE[num]
              
                  return BASE[num % LEN_BASE] + encode(num//LEN_BASE)
              
              
              def decode_recursive(string, index):
                  """
                  recursive util function for decode
                  """
              
                  if not string or index >= len(string):
                      return 0
              
                  return (BASE.index(string[index]) * LEN_BASE ** index) + decode_recursive(string, index + 1)
              
              
              def decode(string):
                  """
                  This function decodes given string to number
                  """
              
                  return decode_recursive(string, 0)
              
              

              【讨论】:

                【解决方案20】:

                最简单的。

                BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
                def encode_base62(num):
                    s = ""
                    while num>0:
                      num,r = divmod(num,62)
                      s = BASE62[r]+s
                    return s
                
                
                def decode_base62(num):
                   x,s = 1,0
                   for i in range(len(num)-1,-1,-1):
                      s = int(BASE62.index(num[i])) *x + s
                      x*=62
                   return s
                
                print(encode_base62(123))
                print(decode_base62("1Z"))
                

                【讨论】:

                  【解决方案21】:

                  适用于 Python3(机器:i7-8565U)的基准测试答案:

                  """
                  us per enc()+dec()  #  test
                  
                  (4.477935791015625, 2, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (6.073190927505493, 5, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (9.051250696182251, 9, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (9.864609956741333, 6, '3Tx16Db2JOOqeo6GCGscmW')
                  (10.868197917938232, 1, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (11.018349647521973, 10, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (12.448230504989624, 4, '03Tx16Db2JPSS4ZdQ4dp6oW')
                  (13.016672611236572, 7, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (13.212724447250366, 8, '3Tx16Db2JPSS4ZdQ4dp6oW')
                  (24.119479656219482, 3, '3tX16dB2jpss4zDq4DP6Ow')
                  """
                  
                  from time import time
                  
                  half = 2 ** 127
                  results = []
                  
                  
                  def bench(n, enc, dec):
                      start = time()
                      for i in range(half, half + 1_000_000):
                          dec(enc(i))
                      end = time()
                      results.append(tuple([end - start, n, enc(half + 1234134134134314)]))
                  
                  
                  BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
                  
                  
                  def encode(num, alphabet=BASE62):
                      """Encode a positive number into Base X and return the string.
                  
                      Arguments:
                      - `num`: The number to encode
                      - `alphabet`: The alphabet to use for encoding
                      """
                      if num == 0:
                          return alphabet[0]
                      arr = []
                      arr_append = arr.append  # Extract bound-method for faster access.
                      _divmod = divmod  # Access to locals is faster.
                      base = len(alphabet)
                      while num:
                          num, rem = _divmod(num, base)
                          arr_append(alphabet[rem])
                      arr.reverse()
                      return ''.join(arr)
                  
                  
                  def decode(string, alphabet=BASE62):
                      """Decode a Base X encoded string into the number
                  
                      Arguments:
                      - `string`: The encoded string
                      - `alphabet`: The alphabet to use for decoding
                      """
                      base = len(alphabet)
                      strlen = len(string)
                      num = 0
                  
                      idx = 0
                      for char in string:
                          power = (strlen - (idx + 1))
                          num += alphabet.index(char) * (base ** power)
                          idx += 1
                  
                      return num
                  
                  
                  bench(1, encode, decode)
                  ###########################################################################################################
                  # Remove the `_@` below for base62, now it has 64 characters
                  BASE_ALPH = tuple(BASE62)
                  BASE_LIST = BASE62
                  BASE_DICT = dict((c, v) for v, c in enumerate(BASE_ALPH))
                  
                  ###########################################################################################################
                  BASE_LEN = len(BASE_ALPH)
                  
                  
                  def decode(string):
                      num = 0
                      for char in string:
                          num = num * BASE_LEN + BASE_DICT[char]
                      return num
                  
                  
                  def encode(num):
                      if not num:
                          return BASE_ALPH[0]
                  
                      encoding = ""
                      while num:
                          num, rem = divmod(num, BASE_LEN)
                          encoding = BASE_ALPH[rem] + encoding
                      return encoding
                  
                  
                  bench(2, encode, decode)
                  
                  ###########################################################################################################
                  from django.utils import baseconv
                  
                  bench(3, baseconv.base62.encode, baseconv.base62.decode)
                  
                  
                  ###########################################################################################################
                  def encode(a):
                      baseit = (lambda a=a, b=62: (not a) and '0' or
                                                  baseit(a - a % b, b * 62) + '0123456789abcdefghijklmnopqrstuvwxyz'
                                                                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[
                                                      a % b % 61 or -1 * bool(a % b)])
                      return baseit()
                  
                  
                  bench(4, encode, decode)
                  
                  
                  ###########################################################################################################
                  def encode(num, sym=BASE62, join_symbol=''):
                      if num == 0:
                          return sym[0]
                  
                      l = len(sym)  # target number base
                      r = []
                      div = num
                      while div != 0:  # base conversion
                          div, mod = divmod(div, l)
                          r.append(sym[mod])
                  
                      return join_symbol.join([x for x in reversed(r)])
                  
                  
                  bench(5, encode, decode)
                  
                  ###########################################################################################################
                  from math import floor
                  
                  base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                  b = 62;
                  
                  
                  def decode(b62: str) -> int:
                      limit = len(b62)
                      res = 0
                      for i in range(limit):
                          res = b * res + base.find(b62[i])
                      return res
                  
                  
                  def encode(b10: int) -> str:
                      if b <= 0 or b > 62:
                          return 0
                      r = b10 % b
                      res = base[r];
                      q = floor(b10 / b)
                      while q:
                          r = q % b
                          q = floor(q / b)
                          res = base[int(r)] + res
                      return res
                  
                  
                  bench(6, encode, decode)
                  
                  
                  ###########################################################################################################
                  def encode(dec):
                      s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
                      return s[dec] if dec < 62 else encode(dec // 62) + s[int(dec % 62)]
                  
                  
                  def decode(b62):
                      s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
                      if len(b62) == 1:
                          return s.index(b62)
                      x = decode(b62[:-1]) * 62 + s.index(b62[-1:]) % 62
                      return x
                  
                  
                  bench(7, encode, decode)
                  
                  
                  def encode(dec):
                      s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
                      ret = ''
                      while dec > 0:
                          ret = s[dec % 62] + ret
                          dec //= 62
                      return ret
                  
                  
                  def decode(b62):
                      s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
                      ret = 0
                      for i in range(len(b62) - 1, -1, -1):
                          ret = ret + s.index(b62[i]) * (62 ** (len(b62) - i - 1))
                      return ret
                  
                  
                  bench(8, encode, decode)
                  
                  
                  ###########################################################################################################
                  
                  def encode(num):
                      s = ""
                      while num > 0:
                          num, r = divmod(num, 62)
                          s = BASE62[r] + s
                      return s
                  
                  
                  def decode(num):
                      x, s = 1, 0
                      for i in range(len(num) - 1, -1, -1):
                          s = int(BASE62.index(num[i])) * x + s
                          x *= 62
                      return s
                  
                  
                  bench(9, encode, decode)
                  
                  
                  ###########################################################################################################
                  
                  def encode(number: int, alphabet=BASE62, padding: int = 22) -> str:
                      l = len(alphabet)
                      res = []
                      while number > 0:
                          number, rem = divmod(number, l)
                          res.append(alphabet[rem])
                          if number == 0:
                              break
                      return "".join(res)[::-1]  # .rjust(padding, "0")
                  
                  
                  def decode(digits: str, lookup=BASE_DICT) -> int:
                      res = 0
                      last = len(digits) - 1
                      base = len(lookup)
                      for i, d in enumerate(digits):
                          res += lookup[d] * pow(base, last - i)
                      return res
                  
                  
                  bench(10, encode, decode)
                  
                  ###########################################################################################################
                  
                  for row in sorted(results):
                      print(row)
                  

                  【讨论】:

                  • 我希望优化的 Django 是最差的。
                  • Numba 应该会有所帮助,尽管增加了加热开销,但它在 python 3.8.5 中不起作用
                  【解决方案22】:

                  原始 javascript 版本:

                  var hash = "", alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", alphabetLength = 
                  alphabet.length;
                  do {
                    hash = alphabet[input % alphabetLength] + hash;
                    input = parseInt(input / alphabetLength, 10);
                  } while (input);
                  

                  来源:https://hashids.org/

                  蟒蛇:

                  def to_base62(number):
                    alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
                    alphabetLength = len(alphabet)
                    result = ""
                    while True:
                      result = alphabet[number % alphabetLength] + result
                      number = int(number / alphabetLength)
                      if number == 0:
                        break
                    return result
                  
                  print to_base62(59*(62**2) + 60*(62) + 61)
                  # result: XYZ
                  

                  【讨论】:

                    猜你喜欢
                    • 2013-01-24
                    • 2013-01-06
                    • 2014-11-22
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-07-30
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多