Python элегантная обратная функция от int (string, base) - PullRequest
43 голосов
/ 14 января 2010

Python позволяет преобразовывать строки в целые числа, используя любую базу в диапазоне [2,36], используя:

int(string,base)

я ищу элегантную обратную функцию, которая принимает целое число и основание и возвращает строку

например

>>> str_base(224,15)
'ee'

У меня есть следующее решение:

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)

примечание: digit_to_char () работает для базисов <= 169, произвольно используя символы ascii после 'z' в качестве цифр для базисов выше 36 </p>

есть ли встроенная функция python, библиотечная функция или более элегантная обратная функция int (string, base)?

Ответы [ 9 ]

30 голосов
/ 28 февраля 2013

Возможно, это не должен быть ответ, но он может быть полезен для некоторых: встроенная функция format конвертирует числа в строку за несколько основ:

>>> format(255, 'b') # base 2
'11111111'
>>> format(255, 'd') # base 10
'255'
>>> format(255, 'o') # base 8
'377'
>>> format(255, 'x') # base 16
'ff'
19 голосов
/ 29 мая 2014

Если вы используете Numpy, есть numpy.base_repr.

Вы можете прочитать код под numpy/core/numeric.py. Короткий и элегантный

12 голосов
/ 14 января 2010

Этот поток имеет несколько примеров реализации.

На самом деле, я думаю, что ваше решение выглядит довольно красиво, оно даже рекурсивно, что здесь как-то приятно.

Я бы по-прежнему упростил его, чтобы убрать else, но это, вероятно, личный стиль. Я думаю, if foo: return очень ясно, и после него не нужно else, чтобы понять, что это отдельная ветвь.

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)

Я упростил случай 0-9 в digit_to_char(), думаю, str() более понятен, чем конструкция chr(ord()). Чтобы максимизировать симметрию с кейсом >= 10, можно было бы учесть ord(), но я не стал беспокоиться, так как это добавило бы линию, и краткость стала лучше. :)

6 голосов
/ 15 июля 2014

Приведенные выше ответы действительно хороши. Это помогло мне создать прототип алгоритма, который я должен был реализовать на C

Я хотел бы предложить небольшое изменение (которое я использовал) для преобразования десятичной дроби в основание пространства символов

Я также игнорировал отрицательные значения только для краткости и факта, что математически неверно -> другие правила для модульной арифметики -> другая математика, если вы используете бинарный, октальный или шестнадцатеричный -> diff в значениях без знака и со знаком

def str_base(number, base):
   (d,m) = divmod(number,len(base))
   if d > 0:
      return str_base(d,base)+base[m]
   return base[m]

, что приводит к следующему выводу

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

если вы хотите заполнить символом нулевого пропппера, вы можете использовать

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'
4 голосов
/ 12 января 2011

обзор.

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
2 голосов
/ 02 октября 2016

Когда-то я написал свою собственную функцию с той же целью, но теперь это невероятно сложно.

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

Я думаю, однако, что функция strbase должна поддерживать только базы> = 2 и <= 36, чтобы предотвратить конфликт с другими инструментами в python, такими как int. Кроме того, я думаю, что для предотвращения конфликта с другими функциями, такими как int, следует использовать только один регистр алфавитов, предпочтительно снова в верхнем регистре, поскольку он будет считать «a» и «A» равными 10. </p>

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)
2 голосов
/ 14 января 2010

digit_to_char может быть реализовано так:

def digit_to_char(digit):
    return (string.digits + string.lowercase)[digit]
0 голосов
/ 07 апреля 2018

Похоже, это моё время сиять. Хотите верьте, хотите нет, ниже приведен некоторый портированный и модифицированный код Scratch, который я написал почти три года назад, чтобы увидеть, насколько быстро я могу преобразовать из денари в шестнадцатеричное.

Проще говоря, он работает, сначала беря целое число, основание и необязательную сопровождающую строку цифр, а затем вычисляя каждую цифру преобразованного целого числа, начиная с наименее значимого.

def int2base(num, base, abc="0123456789abcdefghijklmnopqrstuvwxyz"):
  if num < 0:
    return '-' + int2base(-num, base, abc)

  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

На моем собственном ПК этот код смог выполнить 10 миллионов итераций, используя диапазон ввода 0-9999 и базовое значение 36, постоянно менее 5 секунд. Используя тот же тест, я обнаружил, что это как минимум на 4 секунды быстрее, чем любой другой ответ.

>>> timeit.timeit(lambda: [int2base(n, 36) for n in range(10000)], number=1000)
4.883068453882515
0 голосов
/ 26 апреля 2016

Вот мое решение:

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.

Base%(base-1)==1 поэтому base**p%(base-1)==1 и, следовательно, q*base^p%(base-1)==q только с одним исключением, когда q=base-1, который возвращает 0. Чтобы исправить это, в случае, если он возвращает 0, функция проверяет, является ли она 0 с самого начала.


Преимущества

В этом примере есть только одно умножение (вместо деления) и несколько экземпляров модуля, которые занимают относительно небольшое количество времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...