Question

Python permet la création facile d'un nombre entier d'une chaîne d'une base donnée via

int(str, base). 

Je veux effectuer l'inverse: création d'une chaîne à partir d'un nombre entier , dire que je veux une int2base(num, base) de fonction, de telle sorte que:

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

L'ordre nom fonction / argument est sans importance.

Pour tout nombre x et b de base qui int() accepteront.

Ceci est une fonction facile à écrire: en fait, il est plus facile que de décrire dans cette question. Cependant, je sens que je dois manquer quelque chose.

Je sais que sur les bin fonctions, oct, hex, mais je ne peux pas les utiliser pour quelques raisons:

  • Ces fonctions ne sont pas disponibles sur les anciennes versions de Python, avec lequel je besoin d'une compatibilité avec (2.2)

  • Je veux une solution générale qui peut être appelé de la même manière pour les différentes bases

  • Je veux permettre à des bases autres que 2, 8, 16

Related

Était-ce utile?

La solution

Si vous avez besoin d'une compatibilité avec les anciennes versions de Python, vous pouvez utiliser gmpy (qui inclut un rapide, complètement générale fonction de conversion int à chaîne, et peut être construit pour ces versions anciennes - vous devrez peut-être essayer les versions plus anciennes depuis les dernières ont pas été testés pour Python vénérable et GMP libère, seuls un peu plus récentes), ou, pour moins de vitesse mais plus de commodité, utilisez le code Python - par exemple, le plus simplement:

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)

Autres conseils

De façon surprenante, les gens donnent seulement des solutions qui se transforment en petites bases (plus petite que la longueur de l'alphabet anglais). Il n'y a eu aucune tentative pour donner une solution qui se transforme en toute base arbitraire de 2 à l'infini.

Voici donc une solution super simple:

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

donc si vous avez besoin de convertir un nombre énorme de super à la 577 de base,

numberToBase(67854 ** 15 - 102, 577), vous donnera une bonne solution: [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],

Que vous pouvez convertir plus tard à une base que vous voulez

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/

S'il vous plaît être conscient que cela peut conduire à

RuntimeError: maximum recursion depth exceeded in cmp

pour les entiers très grands.

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

Great réponses! Je suppose que la réponse à ma question était « non » Je ne manquait pas une solution évidente. Voici la fonction que je vais utiliser que les bonnes idées condensent exprimées dans les réponses.

  • permettre le mappage fourni par l'appelant de caractères (permet codage de base64)
  • vérifie négatif et zéro
  • Cartes nombres complexes en tuples de chaînes


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 ne possède pas de fonction intégrée pour l'impression d'un nombre entier dans une base arbitraire. Vous devez écrire votre propre si vous voulez.

Vous pouvez utiliser baseconv.py de mon projet: https://github.com/semente/python-baseconv

Utilisation de l'échantillon:

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

Il y a des convertisseurs bultin comme par exemple baseconv.base2, baseconv.base16 et baseconv.base64.

récursive

Je voudrais Simplify réponse la plus voté :

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

Avec les mêmes conseils pour RuntimeError: maximum recursion depth exceeded in cmp sur les très grands entiers et les nombres négatifs. (Vous pouvez utiliser sys.setrecursionlimit(new_limit) )

itératives

éviter les problèmes de récursion :

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

Voici un autre du même lien

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

J'ai fait un paquet pépin pour cela.

Je vous recommande d'utiliser mon bases.py https://github.com/kamijoutouma/bases.py qui a été inspiré par 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

reportez-vous à https://github.com/kamijoutouma/bases.py#known -basesalphabets pour quelles bases sont utilisables

EDIT: lien pip 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)

sortie:

  

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

Une solution récursive pour les personnes intéressées. Bien sûr, cela ne fonctionnera pas avec des valeurs négatives binaires. Vous auriez besoin de mettre en œuvre la Complement de deux.

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

explication

Dans toute base chaque nombre est égal à a1+a2*base**2+a3*base**3... La « mission » est de trouver tous un « s.

everyN=1,2,3... le code isole le aN*base**N par « mouduling » par b pour b=base**(N+1) qui Trancher tous un est plus grand que N, et le tranchage tous les a 's que leur série est plus petit que N en diminuant un chaque fois que le func est appelé par le aN*base**N actuel.

Base de% (base 1) == 1 à cet effet la base ** p% (base 1) == 1 et à cet effet la base q * ^ p% (base 1) == q avec une seule exception lorsque q = de base 1 qui renvoie 0. Pour résoudre ce problème dans le cas où il retourne 0 le fonc vérifie 0 est-il de la beggining.


avantages

dans cet échantillon Theres seulement un multiplications (au lieu de la division) et quelques moudulueses qui prend relativement petites quantités de temps.

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)

Une autre courte (et plus facile à comprendre imo):

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

Et avec une bonne gestion des exceptions:

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

Une autre solution, fonctionne avec la base 2 à 10, a besoin d'une modification de bases supérieures:

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

Exemple:

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

Voici une version récursive qui gère des entiers signés et chiffres personnalisés.

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]

Les chaînes ne sont pas le seul choix pour représenter les nombres: vous pouvez utiliser une liste d'entiers pour représenter l'ordre de chaque chiffre. Ceux-ci peuvent facilement être convertis en une chaîne.

Aucune des réponses de rejet de base <2; et la plupart courir très lentement ou accident avec pile déborde pour très grand nombre (tels que 56789 ** 43210). Pour éviter de tels échecs, réduire rapidement comme ceci:

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 est comparable à str pour un grand nombre (environ 0.3s sur ma machine), mais si vous comparez contre hex vous pourriez être surpris (environ 0,3 ms sur ma machine, ou 1000x plus rapide). La raison en est que le grand nombre entier est mis en mémoire dans la base 256 (octets). Chaque octet peut simplement être converti en une chaîne hexagonale à deux caractères. Cet alignement ne se produit que pour les bases qui sont des puissances de deux, ce qui explique pourquoi il y a des cas particuliers pour 2,8, et 16 (et base64, ascii, utf16, UTF32).

Prenons le dernier chiffre d'une chaîne décimale. Comment cela se rapporte à la séquence d'octets qui forme son entier? Faisons l'étiquette de la s[i] octets avec s[0] étant le moins important (peu endian). Ensuite, le dernier chiffre est sum([s[i]*(256**i) % 10 for i in range(n)]). Eh bien, il arrive que 256 ** i se termine par un 6 pour i> 0 (6 * 6 = 36) de telle sorte que le dernier chiffre est (s[0]*5 + sum(s)*6)%10. De là, vous pouvez voir que le dernier chiffre dépend de la somme de tous les octets. Cette propriété est nonlocal ce qui fait la conversion en décimal plus dur.

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

Je ne l'ai pas vu de convertisseurs de flotteur ici. Et j'ai raté le regroupement pour toujours trois chiffres.

TODO:

-Numéros dans l'expression scientifique (n.nnnnnn*10**(exp) - le '10' est self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-from_string-fonction.

-base 1 -> chiffres romains

-repr du complexe avec agles

Alors, voici ma solution:

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)

Sortie:

3ewfdnca0n6ld1ggvfgg

de se convertir à une base inverse est trop facile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top