Как преобразовать целое число в любой базе в строку?
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 больше, чем N, и нарезает все a, что их серийный номер меньше N, уменьшая a каждый раз, когда функция вызывается текущимaN*base**N.база%(база-1)==1 для базы**п%(база-1)==1 и поэтому q * base^p%(base-1)==q только с одним исключением, когда q=base-1, который возвращает 0. Чтобы исправить это, если он возвращает 0, функция проверяет, является ли она 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)
еще один короткий (и легче понять ИМО):
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 МС на моей машине, или в 1000 раз быстрее). Причина в том, что большое целое число хранится в памяти в базе 256 (байт). Каждый байт может быть просто преобразован в двухсимвольную шестнадцатеричную строку. Это выравнивание происходит только для баз, которые являются степенями двух, поэтому существуют специальные случаи для 2,8 и 16 (и base64, ascii, utf16, utf32).рассмотрим последнюю цифру десятичной строки. Как это связано с последовательностью байтов, которая образует его целое число? Давайте пометим байты
s[i]Сs[0]будучи наименее значимым (маленький эндиан). Тогда последняя цифраsum([s[i]*(256**i) % 10 for i in range(n)]). Ну, бывает, что 256**у меня заканчивается 6 при i > 0 (6*6=36), так что последняя цифра(s[0]*5 + sum(s)*6)%10. Из этого видно, что последняя цифра зависит от суммы всех байтов. Это нелокальное свойство делает преобразование в десятичное более сложным.
Я не видел здесь никаких преобразователей поплавка. И я пропустил группировку для всегда трех цифр.
TODO:
- числа в научном выражении
(n.nnnnnn*10**(exp)-- the'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чтобы преобразовать в любую базу, инверсия тоже легко.
Comments