Как преобразовать целое число в любой базе в строку?
Python позволяет легко создавать целое число из строки данной базы через
int(str, base). 
Я хочу выполнить обратное:создание строки из целого числа, 
т. е. я хочу некоторую функцию int2base(num, base), таких, что:
int(int2base(x, b), b) == x
порядок имени функции / аргумента не имеет значения.
для любого числа x и основание b это int() будет принимать.
это простая функция записи: на самом деле это проще, чем описывая его в этом вопросе. Однако мне кажется, что я что-то упускаю.
Я знаю о функции bin, oct, hex, но я не могу их использовать по нескольким причинам:
- эти функции недоступны в старых версиях Python, с которыми мне нужна совместимость с (2.2) 
- Я хочу общее решение, которое можно назвать одинаково для разных баз 
- Я хочу разрешить базы, отличные от 2, 8, 16 
по теме
21 ответов
Если вам нужна совместимость с древними версиями Python, вы можете использовать gmpy (который включает в себя быструю, полностью общую функцию преобразования int-to-string и может быть построен для таких древних версий - вам может потребоваться попробовать более старые версии, так как последние не были протестированы для почтенных выпусков Python и GMP, только несколько последних), или, для меньшей скорости, но большего удобства, используйте код Python - например, проще всего:
import string
digs = string.digits + string.ascii_letters
def int2base(x, base):
    if x < 0:
        sign = -1
    elif x == 0:
        return digs[0]
    else:
        sign = 1
    x *= sign
    digits = []
    while x:
        digits.append(digs[int(x % base)])
        x = int(x / base)
    if sign < 0:
        digits.append('-')
    digits.reverse()
    return ''.join(digits)
def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])
ref: http://code.activestate.com/recipes/65212/
имейте в виду, что это может привести к
RuntimeError: maximum recursion depth exceeded in cmp
для очень больших чисел.
Удивительно, но люди давали только решения, которые преобразуются в небольшие базы (меньше, чем длина английского алфавита). Не было попытки дать решение, которое преобразуется в любую произвольную базу от 2 до бесконечности.
Итак, вот супер простое решение:
def numberToBase(n, b):
    if n == 0:
        return [0]
    digits = []
    while n:
        digits.append(int(n % b))
        n //= b
    return digits[::-1]
Итак, если вам нужно преобразовать какое-то супер огромное число в базу 577,
numberToBase(67854 ** 15 - 102, 577), передаст вам правильное решение:
[4, 473, 131, 96, 431, 285, 524, 486, 28, 23, 16, 82, 292, 538, 149, 25, 41, 483, 100, 517, 131, 28, 0, 435, 197, 264, 455],
что вы можете позже конвертировать в любую базу вы хотите
хорошие ответы! Думаю, ответ на мой вопрос был "Нет", я не упускал какого-то очевидного решения. Вот функция, которую я буду использовать, которая конденсирует хорошие идеи, выраженные в ответах.
- разрешить вызывающему абоненту отображение символов (позволяет кодировать base64)
- проверка на отрицательный и нулевой
- отображает комплексные числа в кортежи строк
def int2base(x,b,alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
    'convert an integer to its string representation in a given base'
    if b<2 or b>len(alphabet):
        if b==64: # assume base64 rather than raise error
            alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
        else:
            raise AssertionError("int2base base out of range")
    if isinstance(x,complex): # return a tuple
        return ( int2base(x.real,b,alphabet) , int2base(x.imag,b,alphabet) )
    if x<=0:
        if x==0:
            return alphabet[0]
        else:
            return  '-' + int2base(-x,b,alphabet)
    # else x is non-negative real
    rets=''
    while x>0:
        x,idx = divmod(x,b)
        rets = alphabet[idx] + rets
    return rets
Python не имеет встроенной функции для печати целого числа в произвольной базе. Если хочешь, можешь написать свою.
вы могли бы использовать baseconv.py из моего проекта: https://github.com/semente/python-baseconv
пример использования:
>>> from baseconv import BaseConverter
>>> base20 = BaseConverter('0123456789abcdefghij')
>>> base20.encode(1234)
'31e'
>>> base20.decode('31e')
'1234'
>>> base20.encode(-1234)
'-31e'
>>> base20.decode('-31e')
'-1234'
>>> base11 = BaseConverter('0123456789-', sign='$')
>>> base11.encode('34')
'$-22'
>>> base11.decode('$-22')
'34'
есть некоторые bultin преобразователей, как, например,baseconv.base2, baseconv.base16 и baseconv.base64.
http://code.activestate.com/recipes/65212/
def base10toN(num,n):
    """Change a  to a base-n number.
    Up to base-36 is supported without special notation."""
    num_rep={10:'a',
         11:'b',
         12:'c',
         13:'d',
         14:'e',
         15:'f',
         16:'g',
         17:'h',
         18:'i',
         19:'j',
         20:'k',
         21:'l',
         22:'m',
         23:'n',
         24:'o',
         25:'p',
         26:'q',
         27:'r',
         28:'s',
         29:'t',
         30:'u',
         31:'v',
         32:'w',
         33:'x',
         34:'y',
         35:'z'}
    new_num_string=''
    current=num
    while current!=0:
        remainder=current%n
        if 36>remainder>9:
            remainder_string=num_rep[remainder]
        elif remainder>=36:
            remainder_string='('+str(remainder)+')'
        else:
            remainder_string=str(remainder)
        new_num_string=remainder_string+new_num_string
        current=current/n
    return new_num_string
вот еще один из той же ссылке
def baseconvert(n, base):
    """convert positive decimal integer n to equivalent in another base (2-36)"""
    digits = "0123456789abcdefghijklmnopqrstuvwxyz"
    try:
        n = int(n)
        base = int(base)
    except:
        return ""
    if n < 0 or base < 2 or base > 36:
        return ""
    s = ""
    while 1:
        r = n % base
        s = digits[r] + s
        n = n / base
        if n == 0:
            break
    return s
Я сделал пакет pip для этого.
Я рекомендую вам использовать мой bases.py https://github.com/kamijoutouma/bases.py который был вдохновлен основаниями.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 для каких баз можно использовать
изменить: pip link https://pypi.python.org/pypi/bases.py/0.2.2
def base(decimal ,base) :
    list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    other_base = ""
    while decimal != 0 :
        other_base = list[decimal % base] + other_base
        decimal    = decimal / base
    return other_base
print base(31 ,16)
выход:
"1F"
>>> import string
>>> def int2base(integer, base):
        if not integer: return '0'
        sign = 1 if integer > 0 else -1
        alphanum = string.digits + string.ascii_lowercase
        nums = alphanum[:base]
        res = ''
        integer *= sign
        while integer:
                integer, mod = divmod(integer, base)
                res += nums[mod]
        return ('' if sign == 1 else '-') + res[::-1]
>>> int2base(-15645, 23)
'-16d5'
>>> int2base(213, 21)
'a3'
рекурсивное решение для тех, кто заинтересован. Конечно, это не будет работать с отрицательными двоичными значениями. Вам нужно будет реализовать дополнение Two.
def generateBase36Alphabet():
    return ''.join([str(i) for i in range(10)]+[chr(i+65) for i in range(26)])
def generateAlphabet(base):
    return generateBase36Alphabet()[:base]
def intToStr(n, base, alphabet):
    def toStr(n, base, alphabet):
        return alphabet[n] if n < base else toStr(n//base,base,alphabet) + alphabet[n%base]
    return ('-' if n < 0 else '') + toStr(abs(n), base, alphabet)
print('{} -> {}'.format(-31, intToStr(-31, 16, generateAlphabet(16)))) # -31 -> -1F
def int2base(a, base, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    baseit = lambda a=a, b=base: (not a) and numerals[0]  or baseit(a-a%b,b*base)+numerals[a%b%(base-1) or (a%b) and (base-1)]
    return baseit()
объяснение
в любой базе каждое число равно a1+a2*base**2+a3*base**3... "миссию" найти "эйс".
для каждогоN=1,2,3... код изолирование aN*base**N по "mouduling" по b для b=base**(N+1) которые нарезают все a ' S больше, чем N, и нарезая все a 's, что их серийный меньше, чем N, уменьшая каждый раз, когда func вызывается текущим aN*base**N .  
Base%(base-1)==1 для основания * * p%(base-1)==1 и для этого q * base^p%(base-1)==q с единственным исключением, когда q=base-1, который возвращает 0. Чтобы исправить это, если он возвращает 0, func проверяет, является ли это 0 от начала.
преимущества
в этом примере есть только одно умножение (вместо деления) и некоторые moudulueses, которые относительно занимают небольшое количество времени.
def dec_to_radix(input, to_radix=2, power=None):
    if not isinstance(input, int):
        raise TypeError('Not an integer!')
    elif power is None:
        power = 1
    if input == 0:
        return 0
    else:
        remainder = input % to_radix**power
        digit = str(int(remainder/to_radix**(power-1)))
        return int(str(dec_to_radix(input-remainder, to_radix, power+1)) + digit)
def radix_to_dec(input, from_radix):
    if not isinstance(input, int):
        raise TypeError('Not an integer!')
    return sum(int(digit)*(from_radix**power) for power, digit in enumerate(str(input)[::-1]))
def radix_to_radix(input, from_radix=10, to_radix=2, power=None):
    dec = radix_to_dec(input, from_radix)
    return dec_to_radix(dec, to_radix, power)
еще один короткий (и легче понять imo):
def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
    return (int_to_str(n/b, b, symbols) if n >= b else "") + symbols[n%b]
и с правильной обработкой исключений:
def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
    try:
        return (int_to_str(n/b, b) if n >= b else "") + symbols[n%b]
    except IndexError:
        raise ValueError(
            "The symbols provided are not enough to represent this number in "
            "this base")
другое решение, работает с базой 2 до 10, нуждается в модификации для более высоких баз:
def n2b(n, b):
    if n == 0:
        return 0
    d = []
    while n:
        d.append(int(n % b))
        n /= b
    return ''.join(map(str,d[::-1]))
пример:
n2b(10,2) => '10100'
int(n2b(10,2),2) => 10
вот рекурсивная версия, которая обрабатывает целые числа со знаком и пользовательские цифры.
import string
def base_convert(x, base, digits=None):
    """Convert integer `x` from base 10 to base `base` using `digits` characters as digits.
    If `digits` is omitted, it will use decimal digits + lowercase letters + uppercase letters.
    """
    digits = digits or (string.digits + string.ascii_letters)
    assert 2 <= base <= len(digits), "Unsupported base: {}".format(base)
    if x == 0:
        return digits[0]
    sign = '-' if x < 0 else ''
    x = abs(x)
    first_digits = base_convert(x // base, base, digits).lstrip(digits[0])
    return sign + first_digits + digits[x % base]
def baseConverter(x, b):
    s = ""
    d = string.printable.upper()
    while x > 0:
        s += d[x%b]
        x = x / b
    return s[::-1]
строки-не единственный выбор для представления чисел: вы можете использовать список целых чисел для представления порядка каждой цифры. Их можно легко преобразовать в строку.
ни один из ответов не отклоняет базу очень большие номера (например, 56789 * * 43210). Чтобы избежать таких сбоев, уменьшите быстро вот так:
def n_to_base(n, b):
    if b < 2: raise # invalid base
    if abs(n) < b: return [n]
    ret = [y for d in n_to_base(n, b*b) for y in divmod(d, b)]
    return ret[1:] if ret[0] == 0 else ret # remove leading zeros
def base_to_n(v, b):
    h = len(v) // 2
    if h == 0: return v[0]
    return base_to_n(v[:-h], b) * (b**h) + base_to_n(v[-h:], b)
assert ''.join(['0123456789'[x] for x in n_to_base(56789**43210,10)])==str(56789**43210)
Speedwise, n_to_base сопоставимо с str для большого номера (около 0.3 s на моей машине), но если вы сравните с hex вы можете быть удивлены (около 0,3 МС на моей машине или 1000x быстрее). Причина в том, что большое целое число хранится в памяти в базе 256 (байт). Каждый байт можно просто преобразовать в двухсимвольную шестнадцатеричную строку. Это выравнивание происходит только для баз, которые являются степенями двух, поэтому существуют специальные случаи для 2,8 и 16 (и base64, ascii, utf16, utf32).
рассмотрим последнюю цифру десятичной строки. Как это связано с последовательностью байтов, которая образует целое число? Давайте пометим байты s[i] С s[0] быть наименее значительным (маленький эндиан). Тогда последняя цифра sum([s[i]*(256**i) % 10 for i in range(n)]). Ну, бывает, что 256 * * i заканчивается на 6 для i > 0 (6*6=36), так что последняя цифра (s[0]*5 + sum(s)*6)%10. Из этого вы можете видеть, что последняя цифра зависит от суммы всех байтов. Это нелокальное свойство делает преобразование в decimal сложнее.
Я не видел здесь никаких преобразователей float. И я пропустил группировку всегда на три цифры.
TODO:
-числа в научном выражении (n.nnnnnn*10**(exp) -- в '10' и self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))
-from_string-функция.
- база 1 - > римские номера?
-repr комплекса с agles
Итак, вот мое решение:
DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
# note that the order of the digits is reversed for digits before the point
NO_GROUPING = lambda g: g
concat = "".join
concat_backwards = lambda g: concat(e for e in reversed(list(g)))
def grouping(length = 3, char = '_'):
    def yieldor(digits):
        i = 0
        for d in digits:
            if i == length:
                yield char
                i = 0
            yield d
            i+=1
    return yieldor
class Converter:
    def __init__(self, baseDigits: (int, str), beforePoint = NO_GROUPING, afterPoint = NO_GROUPING, decimalPoint = '.', digitPrecision = 16, trimZeros = True):
        if isinstance(baseDigits, int):
            baseDigits = DIGITS[:baseDigits]
        self.baseDigits = baseDigits
        self.beforePoint = beforePoint
        self.afterPoint  = afterPoint
        self.decimalPoint = decimalPoint
        self.digitPrecision = digitPrecision
        self.trimZeros = trimZeros
    def to_string(self, number: (int, float, complex)) -> str:
        if isinstance(number, complex):
            if number.imag == 0:
                return self.to_string(number.real)
            if number.real == 0:
                return self.to_string(number.imag) + 'j'
            return "({}+{}j)".format(self.to_string(number.real), self.to_string (number.imag))
        if number < 0:
            return '-' + self.to_string(-number)
        digitCount = len(self.baseDigits)
        if isinstance(number, float):
            # round correctly
            precError=digitCount**-self.digitPrecision
            number+=0.5*precError
            if self.trimZeros:
                def yieldor(n):
                    p = precError
                    for i in range(self.digitPrecision):
                        if n <= p:
                            return
                        p *= digitCount
                        n *= digitCount
                        digit = int(n)
                        n -= digit
                        yield self.baseDigits[digit]
            else:
                def yieldor(n):
                    for i in range(self.digitPrecision):
                        n *= digitCount
                        digit = int(n)
                        n -= digit
                        yield self.baseDigits[digit]
            a = concat(self.afterPoint(yieldor(number%1)))
            return (
                self.to_string(int(number)) + (a and self.decimalPoint + a)
            )
        else: #is int
            if not number: return self.baseDigits[0]
            def yieldor(n):
                while n:
                    n, digit = divmod(n, digitCount)
                    yield self.baseDigits[digit]
            return concat_backwards(self.beforePoint(yieldor(number)))
# some tests:
if __name__ == "__main__":
    def conv_test(num, digits, *argv, **kwv):
        print(num, "->", digits if isinstance(digits, int) else "{} ({})".format(len(digits), digits), Converter(digits, *argv, **kwv).to_string(num))
    conv_test(True, "ft")
    conv_test(123, 12, grouping(2))
    conv_test(-0xf00d, 16)
    conv_test(1000, True<<True, grouping(4))
    conv_test(1_000_000, "0+-", beforePoint = grouping(2, '|'))
    conv_test(1.5, 10)
    conv_test(0.999999999, 10, digitPrecision = 8)
    conv_test(-0.1, 10)
    import math
    conv_test(math.pi, 10, afterPoint = grouping(5, ' '))
    conv_test(0.123456789, 10, digitPrecision = 6)
    grSpc = grouping(1, ' ')
    conv_test(math.e, ["off", "on"], grSpc, grSpc, " dot ", digitPrecision = 7)
    conv_test(1 + 1.5j, 10)
    conv_test(50j, 10)
    conv_test(10.01, '-<>')
    # and generate some brainfuck-code here:
    conv_test(1701**42, '+-<>,.][', digitPrecision = 32)
def bn(x,b,ab="0123456789abcdefghijklmnopqrstuvwxyz..."
    a = ""
    while (x>0):
        x,r = divmod(x,n)
        a += ab[r]
    return a[::-1]
bn(2**100, 36)
выход:
3ewfdnca0n6ld1ggvfgg
чтобы преобразовать в любую базу, обратный тоже легко.
