Domanda

Python consente facile creazione di un numero intero da una stringa di una data base tramite

int(str, base). 

Voglio eseguire l'inverso: creazione di una stringa da un intero , vale a dire che voglio qualche funzione int2base(num, base), in modo tale che:

int(int2base(x, b), b) == x

L'ordine funzione di nome / argomento è irrilevante.

Per qualsiasi numero x e b base che int() accetteranno.

Questa è una funzione facile da scrivere: in realtà è più facile di descrivere in questa domanda. Tuttavia, mi sento come se devo essere perso qualcosa.

So della funzioni bin, oct, hex, ma io non li posso usare per alcuni motivi:

  • Queste funzioni non sono disponibili su versioni precedenti di Python, con i quali ho bisogno di compatibilità con (2.2)

  • Voglio una soluzione generale che può essere chiamato allo stesso modo per le basi differenti

  • Voglio permettere basi diverso da 2, 8, 16

correlati

È stato utile?

Soluzione

Se avete bisogno di compatibilità con le versioni antiche di Python, è possibile utilizzare gmpy (che includono un veloce, la funzione del tutto generale Int-to-stringa di conversione, e può essere costruito per tali versioni antiche - potrebbe essere necessario provare versioni precedenti dal momento che quelli recenti non sono stati testati per la veneranda Python e GMP rilascia, unici un po 'recenti), o, meno velocità, ma più convenienza, utilizzare il codice Python - per esempio, la maggior parte semplicemente:

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)

Altri suggerimenti

Sorprendentemente, le persone stavano dando solo le soluzioni che si convertono in piccole basi (più piccoli allora la lunghezza dell'alfabeto inglese). Non c'era alcun tentativo di dare una soluzione che converte in qualsiasi base arbitraria da 2 a infinito.

Quindi, ecco una soluzione super semplice:

def numberToBase(n, b):
    if n == 0:
        return [0]
    digits = []
    while n:
        digits.append(int(n % b))
        n //= b
    return digits[::-1]

quindi se avete bisogno di convertire un certo numero di super-enorme alla 577 base,

numberToBase(67854 ** 15 - 102, 577), vi darà una soluzione corretta: [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],

Il che in seguito sarà possibile convertire in qualsiasi base che si desidera

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/

Si prega di essere consapevoli del fatto che questo può portare a

RuntimeError: maximum recursion depth exceeded in cmp

per molto grandi interi.

"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144

Grandi risposte! Credo che la risposta alla mia domanda è stata "no" Non mancava qualche soluzione più ovvia. Ecco la funzione userò che condensa le buone idee espresse nelle risposte.

  • consentire la mappatura fornito dal chiamante di caratteri (consente base64 codifica)
  • verifica la presenza di negativo e pari a zero
  • associa i numeri complessi in tuple di stringhe


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 non ha una funzione incorporata per la stampa di un numero intero in una base arbitraria. Dovrete scrivere il proprio, se si desidera.

Si potrebbe usare baseconv.py dal mio progetto: https://github.com/semente/python-baseconv

Utilizzo di esempio:

>>> 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'

Ci sono alcuni convertitori bultin come per esempio baseconv.base2, baseconv.base16 e baseconv.base64.

ricorsivo

Vorrei Semplifica più votato risposta a:

BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(n, b): 
    return "0" if not n else to_base(n//b, b).lstrip("0") + BS[n%b]

Con lo stesso consiglio per RuntimeError: maximum recursion depth exceeded in cmp su grandi numeri interi e numeri negativi. (Si potrebbe utilizzare sys.setrecursionlimit(new_limit) )

iterativo

evitare problemi di ricorsività :

BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(s, b):
    res = ""
    while s:
        res+=BS[s%b]
        s//= b
    return res[::-1] or "0"

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

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

Ecco un altro dallo stesso link

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

Ho fatto un pacchetto pip per questo.

Vi consiglio di usare la mia bases.py https://github.com/kamijoutouma/bases.py che è stato ispirato da 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 per quello che le basi sono utilizzabili

EDIT: pip collegamento 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
    if other_base == "":
        other_base = "0"
    return other_base

print base(31 ,16)

uscita:

  

"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'

Una soluzione ricorsiva per chi fosse interessato. Naturalmente, questo non funzionerà con i valori binari negativi. Si avrebbe bisogno di implementare complemento a due.

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()

spiegazione

In ogni base di ogni numero è uguale a a1+a2*base**2+a3*base**3... La "missione" è di trovare tutti un 's.

Per everyN=1,2,3... il codice sta isolando il aN*base**N da "mouduling" di b per b=base**(N+1) quale slice complesso un 's grande di N, ed affettare tutto il un' s che la loro seriale è più piccolo di N diminuendo un'ogni FUNC è chiamato dal aN*base**N corrente.

Base% (base-1) == 1 Perciò basamento ** p% (base-1) == 1 e siasi q * basamento ^ p% (base-1) == q con una sola eccezione quando q = base-1 che restituisce 0. Per risolvere che in caso restituisce 0 il func sta controllando è 0 dal beggining.


vantaggi

In questo esempio theres soltanto uno moltiplicazioni (invece di divisione) e alcuni moudulueses che prende relativamente piccole quantità di tempo.

num = input("number")
power = 0
num = int(num)
while num > 10:
    num = num / 10
    power += 1

print(str(round(num, 2)) + "^" + str(power))
def base_changer(number,base):
    buff=97+abs(base-10)
    dic={};buff2='';buff3=10
    for i in range(97,buff+1):
        dic[buff3]=chr(i)
        buff3+=1   
    while(number>=base):
        mod=int(number%base)
        number=int(number//base)
        if (mod) in dic.keys():
            buff2+=dic[mod]
            continue
        buff2+=str(mod)
    if (number) in dic.keys():
        buff2+=dic[number]
    else:
        buff2+=str(number)

    return buff2[::-1]   
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)

Un altro breve uno (e più facile da capire imo):

def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
    return (int_to_str(n/b, b, symbols) if n >= b else "") + symbols[n%b]

E con una corretta gestione delle eccezioni:

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")

Un'altra soluzione, funziona con base 2 a 10, richiede la modifica delle basi superiori:

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]))

Esempio:

n2b(10,2) => '10100'
int(n2b(10,2),2) => 10

Ecco una versione ricorsiva che gestisce interi e numeri personalizzati firmati.

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]

Le stringhe non sono l'unica scelta per rappresentare i numeri: è possibile utilizzare un elenco di numeri interi per rappresentare l'ordine di ogni cifra. Coloro che può essere facilmente convertito in una stringa.

Nessuna delle risposte rifiutano di base <2; e la maggior parte verrà eseguito molto lentamente o crash con una pila trabocca per molto grandi numeri (come ad esempio 56789 ** 43210). Per evitare tali errori, ridurre rapidamente in questo modo:

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 è comparabile con str per grandi numeri (circa 0.3s sulla mia macchina), ma se si confrontano contro hex si può essere sorpresi (circa 0.3ms sulla mia macchina, o 1000x più veloce). La ragione è che il grande numero intero viene memorizzato nella memoria della base 256 (byte). Ogni byte può semplicemente essere convertito in una stringa esadecimale di due caratteri. Questo allineamento si verifica solo per le basi che sono potenze di due, che è il motivo per cui ci sono casi particolari per 2,8, e 16 (e base64, ASCII, UTF16, UTF-32).

Si consideri l'ultima cifra di una stringa decimale. Come si collega alla sequenza di byte che forma il suo intero? Facciamo etichettare la s[i] byte con s[0] essere significativo (little endian) almeno. Poi l'ultima cifra è sum([s[i]*(256**i) % 10 for i in range(n)]). Ebbene, succede che 256 ** I termina con un 6 per i> 0 (6 * 6 = 36) in modo che ultima cifra è (s[0]*5 + sum(s)*6)%10. Da questo, si può vedere che l'ultima cifra dipende dalla somma di tutti i byte. Questa struttura non locale è ciò che rende la conversione in decimale più difficile.

def baseConverter(x, b):
    s = ""
    d = string.printable.upper()
    while x > 0:
        s += d[x%b]
        x = x / b
    return s[::-1]

Non ho visto alcun trasformatore di float qui. E ho perso il raggruppamento per sempre tre cifre.

TODO:

-Numeri in (n.nnnnnn*10**(exp) espressione scientifica - la '10' è self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-from_string funzione.

-base 1 -?> Numeri romani

-repr del complesso con Agles

Così qui è la mia soluzione:

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)

uscita:

3ewfdnca0n6ld1ggvfgg

per convertire in qualsiasi base, inversa è troppo facile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top