如何将任何基地的整数转换为字符串?
Python允许从一个给定的基础通道的字符串中轻松创建一个整数
int(str, base).
我想执行相反的: 从一个整数 ,即我想要一个函数int2base(num, base)
,从而创建一个字符串 :
int(int2base(x, b), b) == x
函数名称/参数顺序不重要。
对于int()
将接受的任何数字x
和基数b
。
这是一个简单的函数:实际上这比在这个问题中描述它容易。 不过,我觉得我一定会错过一些东西。
我知道功能bin
, oct
, hex
,但是我不能使用它们有几个原因:
-
这些函数在旧版本的Python上不可用,我需要与(2.2)
-
我想要一个通用的解决方案,可以被称为不同的基地相同的方式
-
我想允许除了2,8,16以外的基数
有关
- Python优雅的反函数int(string,base)
- 在Python中使用递归整数到base-x系统
- Python中的Base 62转换
- 如何将整数转换为Python中最短的url安全字符串?
如果你需要与古代版本的Python兼容,你可以使用gmpy (它包括一个快速的,完全一般的int到string的转换函数,并且可以为古老的版本构建 – 你可能需要尝试旧版本最近的那些还没有经过严格的Python和GMP版本的测试,只是最近的一些),或者为了更少的速度,更方便的使用Python代码 – 例如,最简单的方法是:
import string digs = string.digits + string.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[x % base]) 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
对于非常大的整数。
"{0:b}".format(100) # bin: 1100100 "{0:x}".format(100) # hex: 64 "{0:o}".format(100) # oct: 144
令人惊讶的是,人们只给出了解决方案,转换为小型基地(小于英文字母的长度)。 没有试图给出任何从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
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('$1234') '$-22' >>> base11.decode('$-22') '$1234'
有一些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
我为此做了一个点子包。
我建议你使用我的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什么基地可用;
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's Complement。
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(aa%b,b*base)+numerals[a%b%(base-1) or (a%b) and (base-1)] return baseit()
说明
在任何基数中,每个数字都等于a1+a2*base**2+a3*base**3...
“任务”是查找所有的a。
对于每个N=1,2,3...
代码是通过对于b=base**(N+1)
的b来“模糊”来隔离aN*base**N
,其中所有的a都大于N,并且通过每次减少当前的aN*base**N
调用func来将所有a的序列小于aN*base**N
基数%(base-1)== 1基数** p%(base-1)== 1且因此q * base ^ p%(base-1)== q只有当q = base-1返回0.为了解决这个问题,以防万一它返回0 func正在检查它是从0开始。
优点
在这个样本中只有一个乘法(而不是除法)和一些相对需要少量时间的模拟。
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]
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
转换到任何基地,倒也很容易。
字符串不是表示数字的唯一选择:您可以使用整数列表来表示每个数字的顺序。 这些可以很容易地转换为一个字符串。
没有答案拒绝基地<2; 并且大多数将会非常缓慢地运行,或者对于很大数量的堆栈溢出(例如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.3s),但是如果你比较一下hex
你可能会感到惊讶(我的机器上大约是0.3ms,或者快了1000倍)。 原因是因为大整数以256(字节)为单位存储在内存中。 每个字节可以简单地转换成一个两个字符的十六进制字符串。 这种对齐只发生在两个幂的基础上,这就是为什么有2,8和16(以及base64,ascii,utf16,utf32)的特殊情况。
考虑一个十进制字符串的最后一位数字。 它与形成其整数的字节序列有什么关系? 我们用s[0]
是最不重要的(小端)来标记字节s[i]
。 然后最后一位数字是sum([s[i]*(256**i) % 10 for i in range(n)])
。 那么,256 ** i以i> 0(6 * 6 = 36)结尾为6,所以最后一位数字是(s[0]*5 + sum(s)*6)%10
。 从这里可以看到最后一位数字取决于所有字节的总和。 这非本地属性是什么使转换为十进制更难。
我还没有看到任何漂浮转换器。 我错过了总是三位数的分组。
去做:
(n.nnnnnn*10**(exp)
– '10'
是self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))
-from_string功能。
– 基地1 – >罗马数字?
与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)