Как преобразовать целое число в любой базе в строку? - PullRequest
177 голосов
/ 15 февраля 2010

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

Связанные

Ответы [ 24 ]

3 голосов
/ 04 октября 2014
def base(decimal ,base) :
    list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    other_base = ""
    while decimal != 0 :
        other_base = list[decimal % base] + other_base
        decimal    = decimal / base
    if other_base == "":
        other_base = "0"
    return other_base

print base(31 ,16)

выход:

"1F"

3 голосов
/ 03 июня 2019

>>> numpy.base_repr(10, base=3) '101'

1 голос
/ 15 ноября 2018
num = input("number")
power = 0
num = int(num)
while num > 10:
    num = num / 10
    power += 1

print(str(round(num, 2)) + "^" + str(power))
1 голос
/ 15 февраля 2010
>>> 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'
1 голос
/ 24 апреля 2016
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 больше N, и вырезает все a, что их серийный номер меньше N уменьшая каждый раз, когда функция вызывается текущим aN*base**N.

Base% (base-1) == 1 для этого base ** p% (base-1) == 1 и для этого q * base ^ p% (base-1) == q только с одним исключением, когда q = Base-1, который возвращает 0. Чтобы исправить это в случае, если он возвращает 0, функция проверяет, равен ли он 0 с самого начала.


Преимущества

в этом примере есть только одно умножение (вместо деления) и несколько moudulueses, что относительно занимает небольшое количество времени.

1 голос
/ 27 января 2015

Рекурсивное решение для тех, кто заинтересован. Конечно, это не будет работать с отрицательными двоичными значениями. Вам нужно будет реализовать дополнение к двум.

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
0 голосов
/ 11 августа 2015
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)
0 голосов
/ 06 июня 2017

Я не видел здесь никаких преобразователей с плавающей точкой. И я пропустил группировку для всегда трех цифр.

TODO:

- номера в научном выражении (n.nnnnnn*10**(exp) - '10' равно self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-from_string-функция.

-основание 1 -> римские цифры?

-репр комплекс с аглом

Так вот мое решение:

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)
0 голосов
/ 19 января 2017

Строки не единственный выбор для представления чисел: вы можете использовать список целых чисел для представления порядка каждой цифры. Их можно легко преобразовать в строку.

Ни один из ответов не отклоняет базу <2; и большинство будет работать очень медленно или падать с переполнением стека для <em>очень больших чисел (таких как 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)

По скорости n_to_base сравнимо с str для больших чисел (примерно 0,3 с на моей машине), но если вы сравните с hex, вы можете быть удивлены (примерно 0,3 мс на моей машине или в 1000 раз быстрее) , Причина в том, что большое целое число хранится в памяти в базе 256 (байт). Каждый байт может быть просто преобразован в двухсимвольную шестнадцатеричную строку. Это выравнивание происходит только для оснований, имеющих степени двойки, поэтому существуют специальные случаи для 2,8 и 16 (и base64, ascii, utf16, utf32).

Рассмотрим последнюю цифру десятичной строки. Как это связано с последовательностью байтов, которая формирует его целое число? Давайте обозначим байты s[i], где s[0] является наименее значимым (little-endian). Тогда последняя цифра - 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. Отсюда видно, что последняя цифра зависит от суммы всех байтов. Именно это нелокальное свойство затрудняет преобразование в десятичное число.

0 голосов
/ 10 января 2017
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

конвертировать в любую базу, обратное тоже легко.

...