lenguaje - Función inversa elegante de Python de int(cadena, base)
python tutorial (9)
Aquí está mi solución:
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()
Explicación
En cualquier base, cada número es igual a a1+a2*base**2+a3*base**3...
La "misión" es encontrar todos los a''s.
Para cada N=1,2,3...
, el código está aislando la aN*base**N
por "mouduling" por b para b=base**(N+1)
que corta todas las a más que N, y cortando todos los a''s que su serie es más pequeña que N disminuyendo cada vez que la función es llamada por la aN*base**N
actual aN*base**N
Base%(base-1)==1
por lo tanto base**p%(base-1)==1
y por lo tanto q*base^p%(base-1)==q
con solo una excepción cuando q=base-1
que devuelve 0
. Para solucionarlo, en caso de que devuelva 0
, la función es verificar si es 0
desde el principio.
Ventajas
En esta muestra, solo hay una multiplicación (en lugar de división) y algunas instancias de módulo que toman cantidades relativamente pequeñas de tiempo.
python permite conversiones de cadena a entero usando cualquier base en el rango [2,36] usando:
int(string,base)
Estoy buscando una función inversa elegante que toma un número entero y una base y devuelve una cadena
por ejemplo
>>> str_base(224,15)
''ee''
tengo la siguiente solución:
def digit_to_char(digit):
if digit < 10: return chr(ord(''0'') + digit)
else: return chr(ord(''a'') + digit - 10)
def str_base(number,base):
if number < 0:
return ''-'' + str_base(-number,base)
else:
(d,m) = divmod(number,base)
if d:
return str_base(d,base) + digit_to_char(m)
else:
return digit_to_char(m)
nota: digit_to_char () funciona para bases <= 169 arbitrariamente usando caracteres ascii después de ''z'' como dígitos para bases por encima de 36
¿Existe una función integrada de python, función de biblioteca o una función inversa más elegante de int (cadena, base)?
Las respuestas anteriores son realmente agradables. Me ayudó mucho prototipar un algortitmo que tuve que implementar en C
Me gustaría crear un pequeño cambio (lo usé) para convertir decimal a una base de espacio de símbolos
También ignoré los valores negativos solo por falta de precisión y el hecho de que es matemáticamente incorrecto -> otras reglas para aritmética modular -> otras matemáticas si usa binario, oct o hexadecimal -> diff en valores sin firmar y firmados
def str_base(number, base):
(d,m) = divmod(number,len(base))
if d > 0:
return str_base(d,base)+base[m]
return base[m]
esa ventaja es la siguiente producción
>>> str_base(13,''01'')
''1101''
>>> str_base(255,''01'')
''11111111''
>>> str_base(255,''01234567'')
''377''
>>> str_base(255,''0123456789'')
''255''
>>> str_base(255,''0123456789abcdef'')
''ff''
>>> str_base(1399871903,''_helowrd'')
''hello_world''
si quieres rellenar con el símbolo de cero adecuado puedes usar
symbol_space = ''abcdest''
>>> str_base(734,symbol_space).rjust(0,symbol_space[0])
''catt''
>>> str_base(734,symbol_space).rjust(6,symbol_space[0])
''aacatt''
Parece que este podría ser mi momento para brillar. Lo creas o no, el siguiente es un código de Scratch portado y modificado que escribí hace casi tres años para ver qué tan rápido podía convertir de denario a hexadecimal.
En pocas palabras, funciona tomando primero un número entero y una base opcional y la cadena de numerales que lo acompaña, y luego calcula cada dígito del entero convertido comenzando con el menos significativo.
def int2base(num, base, abc="0123456789abcdefghijklmnopqrstuvwxyz"):
if num < 0:
return ''-'' + int2base(-num)
else:
output = abc[num % base] # rightmost digit
while num >= base:
num //= base # move to next digit to the left
output = abc[num % base] + output # this digit
return output
En mi propia PC, este código fue capaz de completar 10 millones de iteraciones usando el rango de entrada, 0-9999, y base, 36, en consistentemente por debajo de 5 segundos. Utilizando la misma prueba, he encontrado que es al menos 4 segundos más rápido que cualquier otra respuesta hasta el momento.
>>> timeit.timeit(lambda: [int2base(n, 36) for n in range(10000)], number=1000)
4.883068453882515
Si usa Numpy, existe numpy.base_repr
.
Puede leer el código en numpy/core/numeric.py
. Corto y elegante
Tal vez esto no debería ser una respuesta, pero podría ser útil para algunos: la función de format
integrado convierte los números a cadena en unas pocas bases:
>>> format(255, ''b'') # base 2
''11111111''
>>> format(255, ''d'') # base 10
''255''
>>> format(255, ''o'') # base 8
''377''
>>> format(255, ''x'') # base 16
''ff''
Una vez escribí mi propia función con el mismo objetivo, pero ahora es embarazosamente complicado.
from math import log, ceil, floor
from collections import deque
from itertools import repeat
from string import uppercase, digits
import re
__alphanumerals = (digits + uppercase)
class InvalidBaseError(ValueError): pass
class FloatConvertError(ValueError): pass
class IncorrectBaseError(ValueError): pass
def getbase(number, base=2, frombase = 10):
if not frombase == 10:
number = getvalue(number, frombase)
#getvalue is also a personal function to replicate int(number, base)
if 1 >= base or base >= len(__alphanumerals) or not floor(base) == base:
raise InvalidBaseError("Invalid value: {} entered as base to convert
to. /n{}".format(base,
"Assert that the base to convert to is a decimal integer."))
if isinstance(number, str):
try:
number = atof(number)
except ValueError:
#The first check of whether the base is 10 would have already corrected the number
raise IncorrectBaseError("Incorrect base passed as base of number -> number: {} base: {}".format(number, frombase))
#^ v was supporting float numbers incase number was the return of another operation
if number > floor(number):
raise FloatConvertError("The number to be converted must not be a float. {}".format(number))
isNegative = False
if number < 0:
isNegative = True
number = abs(number)
logarithm = log(number, base) if number else 0 #get around number being zero easily
ceiling = int(logarithm) + 1
structure = deque(repeat(0, ceiling), maxlen = ceiling)
while number:
if number >= (base ** int(logarithm)):
acceptable_digit = int(number / (base ** floor(logarithm)))
structure.append(acceptable_digit if acceptable_digit < 10 else __alphanumerals[acceptable_digit])
number -= acceptable_digit * (base ** floor(logarithm))
else:
structure.append(0)
logarithm -= 1
while structure[0] == 0:
#the result needs trailing zeros
structure.rotate(-1)
return ("-" if isNegative and number else "") + reduce(lambda a, b: a + b, map(lambda a: str(a), structure))
Aunque creo que la función strbase solo debería soportar bases> = 2 y <= 36 para evitar conflictos con otras herramientas en python, como int. Además, creo que solo se debe usar un caso de alfabetos preferiblemente en mayúsculas nuevamente para evitar conflictos con otras funciones como int, ya que considerará que tanto "a" como "A" son 10.
from string import uppercase
dig_to_chr = lambda num: str(num) if num < 10 else uppercase[num - 10]
def strbase(number, base):
if not 2 <= base <= 36:
raise ValueError("Base to convert to must be >= 2 and <= 36")
if number < 0:
return "-" + strbase(-number, base)
d, m = divmod(number, base)
if d:
return strbase(d, base) + dig_to_chr(m)
return dig_to_chr(m)
revisa esto.
def int2str(num, base=16, sbl=None):
if not sbl:
sbl = ''0123456789abcdefghijklmnopqrstuvwxyz''
if len(sbl) < 2:
raise ValueError, ''size of symbols should be >= 2''
if base < 2 or base > len(sbl):
raise ValueError, ''base must be in range 2-%d'' % (len(sbl))
neg = False
if num < 0:
neg = True
num = -num
num, rem = divmod(num, base)
ret = ''''
while num:
ret = sbl[rem] + ret
num, rem = divmod(num, base)
ret = (''-'' if neg else '''') + sbl[rem] + ret
return ret
Este hilo tiene algunas implementaciones de ejemplo.
De hecho, creo que su solución parece bastante agradable, incluso recursiva, que de alguna manera es agradable aquí.
Todavía lo simplificaría para eliminar el else
, pero eso probablemente sea algo personal. Creo que if foo: return
es muy claro, y no necesita else
más para dejarlo en claro, es una rama separada.
def digit_to_char(digit):
if digit < 10:
return str(digit)
return chr(ord(''a'') + digit - 10)
def str_base(number,base):
if number < 0:
return ''-'' + str_base(-number, base)
(d, m) = divmod(number, base)
if d > 0:
return str_base(d, base) + digit_to_char(m)
return digit_to_char(m)
Simplifiqué el caso 0-9 en digit_to_char()
, creo que str()
es más claro que el constructo chr(ord())
. Para maximizar la simetría con el caso >= 10
podía factorizar un ord()
, pero no me molesté, ya que añadiría una línea y la brevedad se sentiría mejor. :)
digit_to_char
podría implementarse así:
def digit_to_char(digit):
return (string.digits + string.lowercase)[digit]