development

기본 62 변환

big-blog 2020. 10. 13. 08:07
반응형

기본 62 변환


정수를 기본 62로 변환하는 방법 (16 진수와 같지만 다음 숫자 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ').

나는 그것에 대한 좋은 파이썬 라이브러리를 찾으려고 노력해 왔지만 모두 문자열 변환으로 가득 찬 것처럼 보입니다. Python base64 모듈은 문자열 만 받아들이고 한 자리를 4 자로 바꿉니다. URL 단축기가 사용하는 것과 유사한 것을 찾고있었습니다.


이를위한 표준 모듈은 없지만이를 달성하기 위해 자체 함수를 작성했습니다.

BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def encode(num, alphabet=BASE62):
    """Encode a positive number in Base X

    Arguments:
    - `num`: The number to encode
    - `alphabet`: The alphabet to use for encoding
    """
    if num == 0:
        return alphabet[0]
    arr = []
    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 encoding
    """
    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 기지에서 /로 디코딩 / 인코딩.

도움이 되었기를 바랍니다.

추신-URL 단축기의 경우 0Ol1oI 등과 같은 몇 가지 혼란스러운 문자를 생략하는 것이 더 낫다는 것을 알았습니다. 따라서 URL 단축 요구에이 알파벳을 사용합니다. "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

즐기세요.


한 번도이 작업을 수행하는 스크립트를 작성했는데 꽤 우아하다고 생각합니다. :)

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)

다음 디코더 제작자는 합리적인 기준으로 작업하고 훨씬 더 깔끔한 루프를 가지며 유효하지 않은 문자를 만나면 명시적인 오류 메시지를 제공합니다.

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)

가장 높은 효율성 (예 : 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 초 이내에 백만 개의 숫자를 인코딩 및 디코딩했습니다. (2.2Ghz i7-2670QM)


아마도 base62가 아닌 base64를 원할 것입니다. URL 호환 버전이 떠 다니므로 추가로 두 개의 필러 문자가 문제가되지 않습니다.

과정은 매우 간단합니다. base64는 6 비트를 나타내고 일반 바이트는 8을 나타냅니다. 선택한 64 개의 문자 각각에 000000에서 111111 사이의 값을 할당하고 3 개의 base256 바이트 세트와 일치하도록 4 개의 값을 합칩니다. 3 바이트의 각 세트에 대해 반복하고 마지막에 선택한 패딩 문자로 패딩합니다 (일반적으로 0이 유용함).


무언가를 인코딩 / 디코딩하는 대신 짧은 ID (URL 단축기 언급 이후)를 생성하기 만하면이 모듈이 도움이 될 수 있습니다.

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


pypi 에서 zbase62 모듈을 다운로드 할 수 있습니다.

예 :

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

나는 여기에 다른 사람들의 게시물을 통해 많은 혜택을 받았습니다. 원래 Django 프로젝트를 위해 파이썬 코드가 필요했지만 그 이후로 node.js로 전환했습니다. 여기 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"));

다음 스 니펫이 도움이되기를 바랍니다.

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 단축 서비스를 만드는 경우 도움이 될 수 있습니다.


내 해결책은 다음과 같습니다.

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...코드는를 분리 aN*base**N하여 "moduloing"에 의해 b를 위해 b=base**(N+1)하는 모든 조각 a보다 더이야 N, 모든이 슬라이스 a자신의 시리얼보다 작은 너무 s의 N감소에 의해 a기능이 전류에 의해 반복적으로 호출 매번 aN*base**N.

Base%(base-1)==1그러므로 base**p%(base-1)==1따라서 q*base^p%(base-1)==q단 하나 개의 예외로 q==base-1하는 반환 0. 이 경우를 수정하려면을 반환합니다 0. 이 기능 0은 처음부터 확인합니다 .


장점

이 샘플에는 하나의 곱셈 (나눗셈 대신)과 일부 모듈러스 연산 만 있으며, 모두 상대적으로 빠릅니다.


django 프레임 워크를 사용하는 경우 django.utils.baseconv 모듈을 사용할 수 있습니다.

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

base62 외에도 baseconv는 base2 / base16 / base36 / base56 / base64도 정의했습니다.


개인적으로 저는 Baishampayan의 솔루션을 좋아하는데, 대부분 혼란스러운 문자를 제거했기 때문입니다.

완전성과 더 나은 성능을위한 솔루션을 위해이 게시물 은 Python base64 모듈을 사용하는 방법을 보여줍니다.


나는 이것을 얼마 전에 썼고 꽤 잘 작동했습니다 (음수 및 모두 포함)

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

모든 길이에 대해 죄송합니다


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

이를 수행하는 반복적이고 반복적 인 방법이 있습니다. 반복적 인 것은 실행 횟수에 따라 조금 더 빠릅니다.

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

이제이를위한 파이썬 라이브러리가 있습니다.

나는 이것을 위해 pip 패키지를 만드는 중입니다.

bases.js 에서 영감을 얻은 bases.py https://github.com/kamijoutouma/bases.py 를 사용하는 것이 좋습니다.

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참조 하세요.


죄송합니다. 여기서 도서관을 도와 드릴 수 없습니다. 가능한 경우 base64를 사용하고 선택에 추가 문자를 추가하는 것을 선호합니다!

그런 다음 base64 모듈을 사용할 수 있습니다.

이것이 정말로 가능하지 않다면 :

이 방법으로 직접 할 수 있습니다 (이것은 의사 코드입니다) :

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

간단한 재귀로

"""
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)


파이썬 3.7.x

I found a PhD's github for some algorithms when looking for an existing base62 script. It didn't work for the current max-version of Python 3 at this time so I went ahead and fixed where needed and did a little refactoring. I don't usually work with Python and have always used it ad-hoc so YMMV. All credit goes to Dr. Zhihua Lai. I just worked the kinks out for this version of Python.

file 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

file 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])}')

output of try_base62.py

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

Since there was no licensing info in the repo I did submit a PR so the original author at least knows other people are using and modifying their code.

참고URL : https://stackoverflow.com/questions/1119722/base-62-conversion

반응형