Jak przekonwertować liczbę całkowitą na ciąg w dowolnej bazie?

202

Python pozwala na łatwe utworzenie liczby całkowitej z ciągu danej bazy za pośrednictwem

int(str, base). 

Chcę wykonać odwrotność: utworzenie ciągu z liczby całkowitej , tzn. Chcę jakąś funkcję int2base(num, base), taką jak:

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

Nazwa funkcji / kolejność argumentów jest nieistotna.

Dla dowolnej liczby xi bazy b, int()które zaakceptują.

Jest to prosta funkcja do napisania: w rzeczywistości jest łatwiejsza niż opisanie jej w tym pytaniu. Mam jednak wrażenie, że czegoś mi brakuje.

Wiem o funkcjach bin, oct, hex, ale nie można ich używać z kilku powodów:

  • Te funkcje nie są dostępne w starszych wersjach Pythona, z którymi potrzebuję zgodności z (2.2)

  • Chcę ogólnego rozwiązania, które można nazwać tak samo dla różnych baz

  • Chcę zezwolić na zasady inne niż 2, 8, 16

Związane z

Mark Borgerding
źródło
5
Zaskakująco nikt nie podał rozwiązania, które działa z dowolną dużą bazą (1023). Jeśli potrzebujesz, sprawdź moje rozwiązanie, które działa dla każdej bazy (od 2 do inf) stackoverflow.com/a/28666223/1090562
Salvador Dali

Odpowiedzi:

98

Jeśli potrzebujesz zgodności ze starymi wersjami Pythona, możesz albo użyć gmpy (który obejmuje szybką, całkowicie ogólną funkcję konwersji int-na-string i może być zbudowany dla takich starożytnych wersji - być może będziesz musiał wypróbować starsze wersje od ostatnie nie zostały przetestowane pod kątem czcigodnych wydań Python i GMP, tylko nieco nowsze), lub, dla mniejszej prędkości, ale większej wygody, użyj kodu Python - np. najprościej:

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)
Alex Martelli
źródło
8
Tylko w przypadku (gmpy2) wydaje się, że func, o którym mówi Alex gmpy2.digits(x, base).
mlvljr
2
Zwrócono mi uwagę, że niektóre przypadki wymagają bazy> 36, a więc wykopaliska powinny byćdigs = string.digits + string.lowercase + string.uppercase
Paul,
4
(lub string.digits + string.letters)
kojiro,
3
Masz pojęcie, dlaczego konwersja-base-N-na-string nie jest domyślnie zawarta w Pythonie? (Jest w Javascripcie.) Tak, wszyscy możemy napisać własną implementację, ale szukałem w tej witrynie i gdzie indziej, a wiele z nich ma błędy. Lepiej mieć jedną sprawdzoną, renomowaną wersję w dystrybucji podstawowej.
Jason S
4
@ lordscales91 Możesz także użyć tego, x //= basektóry zachowuje się jak /=w Pythonie 2 podczas upuszczania dziesiętnych. Ta odpowiedź powinna zawierać zastrzeżenie, że dotyczy Pythona 2.
Noumenon
100

Co zaskakujące, ludzie podawali tylko rozwiązania, które przekształcają się w małe bazy (mniejsze niż długość alfabetu angielskiego). Nie próbowano podać rozwiązania, które przekształciłoby się w dowolną bazę od 2 do nieskończoności.

Oto bardzo proste rozwiązanie:

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

więc jeśli chcesz przekonwertować jakąś super ogromną liczbę na bazę 577,

numberToBase(67854 ** 15 - 102, 577), Daje poprawne rozwiązanie: [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],

Które możesz później przekonwertować na dowolną bazę, którą chcesz

Salvador Dali
źródło
Na studiach wymyśliłem funkcję, która sformatowała zasady poniżej 20 do standardowej notacji, a zasady 20 i więcej do „dziesiętnego”. Na przykład int(4545,16)podał „11c1” i int(4545,60)„1:15:45”. Tak więc funkcja spełniała trzy funkcje: konwertuje do formatu dziesiętnego, komputerowego i znacznika czasu.
Peter Raynham
1
Jaka jest funkcja odwrotna dla tej metody?
Sohrab T
To nie odpowiada na pytanie zadane z 3 powodów, 1: pytanie zadane dla istniejącej funkcji biblioteki, a nie implementacja 2: pytanie zadane dla ciągu, tworzy listę 3: to nie jest odwrotność dla int (str, base) wbudowany.
płyn do płukania
@plugwash 1) w pewnym momencie zauważysz, że czasami nie ma wbudowanej funkcji biblioteki do robienia rzeczy, które chcesz, więc musisz napisać własną. Jeśli się nie zgadzasz, opublikuj własne rozwiązanie z wbudowaną funkcją, która może przekonwertować liczbę podstawową 10 na bazę 577. 2) Wynika to z niezrozumienia, co oznacza liczba w niektórych bazach. 3) Zachęcam do krótkiego zastanowienia się, dlaczego podstawa w twojej metodzie działa tylko dla n <= 36. Gdy skończysz, będzie oczywiste, dlaczego moja funkcja zwraca listę i ma podpis, który ma.
Salvador Dali
1
Nie działa to dla liczb ujemnych i nie jestem pewien, jak sprawić, by działało bez zasadniczej zmiany. Może dodając bit znaku 1 lub -1 u góry digits?
wjandrea
89
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/

Należy pamiętać, że może to prowadzić do

RuntimeError: maximum recursion depth exceeded in cmp

dla bardzo dużych liczb całkowitych.

jellyfishtree
źródło
5
Elegancki w swej zwięzłości. Wydaje się działać w Pythonie 2.2.3 dla liczb całkowitych nieujemnych. Liczba ujemna nieskończenie się powtarza.
Mark Borgerding,
+1 przydatne; naprawiono problem, gdy cyfry nie zaczynały się od „0”
patrz
4
Nie udaje się to cicho (a), gdy podstawą jest> len(numerals), a (b) num % bna szczęście < len(numerals). np. chociaż numeralsłańcuch ma tylko 36 znaków, baseN (60, 40) zwraca, '1k'podczas gdy baseN (79, 40) podnosi an IndexError. Oba powinny powodować jakiś błąd. Kod powinien zostać zmieniony, aby zgłosić błąd, jeśli not 2 <= base <= len(numerals).
Chris Johnson
3
@osa, chodzi mi o to, że kod, jak napisano, zawodzi w bardzo zły sposób (po cichu, dając mylącą odpowiedź) i można go łatwo naprawić. Jeśli mówisz, że nie byłoby błędu, gdybyś wiedział z góry, na bpewno nie przekroczyłoby to len(numerals), no cóż, powodzenia.
Chris Johnson
1
Zastosowanie zwarcia tutaj wydaje się niepotrzebnie mylące ... dlaczego nie użyć po prostu instrukcji if ... linia return numerals[0] if num == 0 else baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b]jest równie krótka.
Ian Hincks
83
"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144
Rost
źródło
46
Ale to tylko te trzy podstawy?
Thomas Ahle,
3
Tak, niestety nie można określić niestandardowej bazy int. Więcej informacji jest tutaj: docs.python.org/library/string.html#formatstrings
Rost
3
To 0jest niepotrzebne. Oto dokumentacja Python 2: docs.python.org/2/library/string.html#format-string-syntax
Evgeni Sergeev
7
Możesz osiągnąć te same wyniki za pomocą hex(100)[2:], oct(100)[2:]i bin(100)[2:].
Sassan,
2
@EvgeniSergeev: Nie jest konieczne tylko w wersji 2.7 / 3.1 +. W wersji 2.6 wymagana jest wyraźna pozycja (lub nazwa).
ShadowRanger
21

Świetne odpowiedzi! Myślę, że odpowiedź na moje pytanie brzmiała „nie”. Nie brakowało mi jakiegoś oczywistego rozwiązania. Oto funkcja, której użyję, która zagęszcza dobre pomysły wyrażone w odpowiedziach.

  • zezwól na mapowanie znaków dostarczone przez dzwoniącego (umożliwia kodowanie base64)
  • sprawdza ujemne i zerowe
  • odwzorowuje liczby zespolone na krotki ciągów


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

Mark Borgerding
źródło
4
Jak przekonwertować wyjście base64 naszej funkcji z powrotem na liczbę całkowitą?
detly
16

Rekurencyjne

Chciałbym uprościć ten najbardziej głosowało odpowiedź do:

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

Z tą samą radą dla RuntimeError: maximum recursion depth exceeded in cmpbardzo dużych liczb całkowitych i liczb ujemnych. (Możesz użyć sys.setrecursionlimit(new_limit))

Wielokrotny

Aby uniknąć problemów z rekurencją :

BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(s, b):
    res = ""
    while s:
        res+=BS[s%b]
        s//= b
    return res[::-1] or "0"
MM
źródło
2
Pięknie odnowione i bez biblioteki.
Giampaolo Ferradini
Czy nie powinien być return BS[0] if not nwtedy warunek zatrzymania ? Na wypadek, gdybyś chciał użyć fantazyjnych cyfr, tak jak ja :)
Arnaud P
@ArnaudP zgodził się. Ten działa dla mnie:return BS[n] if n < b else to_base(n // b) + BN[n % b]
Jens
15

Python nie ma wbudowanej funkcji do drukowania liczb całkowitych w dowolnej bazie. Będziesz musiał napisać własny, jeśli chcesz.

Mike Graham
źródło
13

Możesz skorzystać baseconv.pyz mojego projektu: https://github.com/semente/python-baseconv

Przykładowe użycie:

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

Istnieje kilka konwertery bultin jak na przykład baseconv.base2, baseconv.base16i baseconv.base64.

Semente
źródło
12

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

V. Ayrat
źródło
Niezłe rozwiązanie. W moim przypadku unikałem nieporozumień claczwiązanych z czasem ładowania. Wstępne ładowanie numpy więcej niż trzykrotnie zwiększa czas wykonywania prostej oceny wyrażeń w clac: np. clac 1+1 Z około 40 ms do 140 ms.
Mark Borgerding,
1
Zauważ, że numpy.base_repr()ma limit 36 ​​jako podstawę. W przeciwnym razie wyrzucaValueError
sbdchd
Który pasuje do ograniczenia wbudowanej funkcji „int”. Większe bazy wymagają podjęcia decyzji, co zrobić, gdy skończą się litery,
plugwash
4

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

Oto kolejny z tego samego linku

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
John La Rooy
źródło
base10toN nie uwzględnia przypadku num == 0.
Craeft
3

Zrobiłem dla tego pakiet pip.

Polecam korzystanie z moich bases.py https://github.com/kamijoutouma/bases.py, który został zainspirowany 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

patrz https://github.com/kamijoutouma/bases.py#known-basesalphabets, aby dowiedzieć się, jakie zasady są użyteczne

EDYCJA: pip link https://pypi.python.org/pypi/bases.py/0.2.2

Belldandu
źródło
Działa to jak urok dla określonych znanych baz .
Agi Hammerthief,
To zdecydowanie najlepsza odpowiedź! I dzięki za opakowanie pip!
ɹɐʎɯɐʞ
3
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)

wynik:

„1F”

Mukundan
źródło
other-basejest taki sam jak other - base, więc powinieneś użyćother_base
mbomb007
Również to nie działa poprawnie, jeśli decimalwynosi zero.
mbomb007,
1
>>> 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'
SilentGhost
źródło
1

Rekurencyjne rozwiązanie dla zainteresowanych. Oczywiście nie będzie to działać z ujemnymi wartościami binarnymi. Musisz wdrożyć 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
Pan Polywhirl
źródło
1
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()

wyjaśnienie

W dowolnej bazie każda liczba jest równa a1+a2*base**2+a3*base**3...„Misja” polega na znalezieniu wszystkich „a”.

Dla każdego N=1,2,3...kodu izoluje się aN*base**Nprzez „moudulowanie” przez b, dla b=base**(N+1) którego wycina się wszystkie a jest większe niż N, i przecina wszystkie a, że ​​ich numer seryjny jest mniejszy niż N, zmniejszając za każdym razem, gdy func jest wywoływany przez prąd aN*base**N.

Podstawa% (podstawa-1) == 1 jej podstawa ** p% (podstawa-1) == 1 i dlatego q * podstawa ^ p% (podstawa-1) == q z jednym wyjątkiem, gdy q = podstawa-1 która zwraca 0. Aby to naprawić w przypadku, gdy zwraca 0, func sprawdza, czy to 0 od początku.


Zalety

w tej próbce jest tylko jedna multiplikacja (zamiast dzielenia) i niektóre moudule, które względnie zajmują mało czasu.

Shu ba
źródło
1
num = input("number")
power = 0
num = int(num)
while num > 10:
    num = num / 10
    power += 1

print(str(round(num, 2)) + "^" + str(power))
Casey Howard
źródło
dodaj krótką informację, że to, co zrobiłeś specjalnym
inicjatorem
Chociaż może to odpowiedzieć na pytanie autora, brakuje w nim niektórych wyjaśnień i / lub linków do dokumentacji. Fragmenty surowego kodu nie są bardzo pomocne bez niektórych fraz wokół nich. Bardzo pomocne może się okazać, jak napisać dobrą odpowiedź . Edytuj swoją odpowiedź.
cholery
1
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]   
montaqami
źródło
W tej funkcji możesz łatwo przekonwertować dowolną liczbę dziesiętną na swoją ulubioną bazę.
montaqami,
Nie musisz komentować własnej odpowiedzi, możesz ją po prostu edytować, aby dodać wyjaśnienie.
Pochmurnik
1

Oto przykład, jak przekonwertować liczbę dowolnej bazy na inną bazę.

from collections import namedtuple

Test = namedtuple("Test", ["n", "from_base", "to_base", "expected"])


def convert(n: int, from_base: int, to_base: int) -> int:
    digits = []
    while n:
        (n, r) = divmod(n, to_base)
        digits.append(r)    
    return sum(from_base ** i * v for i, v in enumerate(digits))


if __name__ == "__main__":
    tests = [
        Test(32, 16, 10, 50),
        Test(32, 20, 10, 62),
        Test(1010, 2, 10, 10),
        Test(8, 10, 8, 10),
        Test(150, 100, 1000, 150),
        Test(1500, 100, 10, 1050000),
    ]

    for test in tests:
        result = convert(*test[:-1])
        assert result == test.expected, f"{test=}, {result=}"
    print("PASSED!!!")
Vlad Bezden
źródło
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)
nameisnotphil
źródło
0

Kolejny krótki (i łatwiejszy do zrozumienia imo):

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

I przy odpowiedniej obsłudze wyjątków:

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")
Ariel
źródło
0

Inne rozwiązanie, działające z bazą 2 do 10, wymaga modyfikacji dla wyższych baz:

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

Przykład:

n2b(10,2) => '10100'
int(n2b(10,2),2) => 10
Stanisław
źródło
0

Oto wersja rekurencyjna, która obsługuje podpisane liczby całkowite i cyfry niestandardowe.

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]
Antoine Pinsard
źródło
0

Ciągi nie są jedynym wyborem do reprezentowania liczb: możesz użyć listy liczb całkowitych do przedstawienia kolejności każdej cyfry. Można je łatwo przekonwertować na ciąg.

Żadna z odpowiedzi nie odrzuca podstawy <2; i większość będzie działać bardzo wolno lub ulegnie awarii z przepełnieniem stosu dla bardzo dużych liczb (takich jak 56789 ** 43210). Aby uniknąć takich awarii, zmniejsz szybko tak:

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_basejest porównywalny z strdużymi liczbami (około 0,3 s na moim komputerze), ale jeśli porównasz go, hexmożesz być zaskoczony (około 0,3 ms na moim komputerze lub 1000x szybciej). Powodem jest to, że duża liczba całkowita jest przechowywana w pamięci w bazie 256 (bajty). Każdy bajt można po prostu przekonwertować na dwuznakowy ciąg szesnastkowy. To wyrównanie ma miejsce tylko dla zasad, które są potęgami dwóch, dlatego istnieją specjalne przypadki dla 2,8 i 16 (i base64, ascii, utf16, utf32).

Rozważ ostatnią cyfrę ciągu dziesiętnego. Jak to się ma do sekwencji bajtów, która tworzy jego liczbę całkowitą? Załóżmy, etykieta bajty s[i]z s[0]bycia najmniej znaczący (little endian). Zatem ostatnia cyfra to sum([s[i]*(256**i) % 10 for i in range(n)]). Zdarza się, że 256 ** i kończy się na 6 dla i> 0 (6 * 6 = 36), więc ostatnia cyfra to (s[0]*5 + sum(s)*6)%10. Z tego widać, że ostatnia cyfra zależy od sumy wszystkich bajtów. Ta nielokalna właściwość utrudnia konwersję na dziesiętne.

colski
źródło
0
def baseConverter(x, b):
    s = ""
    d = string.printable.upper()
    while x > 0:
        s += d[x%b]
        x = x / b
    return s[::-1]
gjivanya
źródło
W przypadku python3 twój kod robi to: baseConverter (0, 26) -> '' baseConverter (1, 26) -> '0000000000000000000000000000000000000000000000000000000000000000001 '00' 00000000000000000000000000000000000000000000001 -> 1 baseConverter (3, 26) -> 3 baseConverter (5, 26) -> 5 baseConverter (26, 26) -> 10 baseConverter (32, 26) -> 16
Drachenfels
0

Cóż, osobiście korzystam z tej napisanej przeze mnie funkcji

import string

def to_base(value, base, digits=string.digits+string.ascii_letters):    # converts decimal to base n

    digits_slice = digits[0:base]

    temporary_var = value
    data = [temporary_var]

    while True:
        temporary_var = temporary_var // base
        data.append(temporary_var)
        if temporary_var < base:
            break

    result = ''
    for each_data in data:
        result += digits_slice[each_data % base]
    result = result[::-1]

    return result

Oto jak możesz go użyć

print(to_base(7, base=2))

Wynik: "111"

print(to_base(23, base=3))

Wynik: "212"

Proszę sugerować ulepszenia w moim kodzie.

Artaza Sameen
źródło
0
def base_conversion(num, base):
    digits = []
    while num > 0:
        num, remainder = divmod(num, base)
        digits.append(remainder)
    return digits[::-1]
hxuanhung
źródło
0

To stare pytanie, ale pomyślałem, że podzielę się tym, ponieważ uważam, że jest to nieco prostsze niż inne odpowiedzi (dobre dla baz od 2 do 36):

def intStr(n,base=10):
    if n < 0   : return "-" + intStr(-n,base)         # handle negatives
    if n < base: return chr([48,55][n>9] + n)         # 48 => "0"..., 65 => "A"...
    return intStr(n//base,base) + intStr(n%base,base) # recurse for multiple digits
Alain T.
źródło
-1

Nie widziałem tutaj żadnych konwerterów float. I brakowało mi grupowania na zawsze trzy cyfry.

DO ZROBIENIA:

- liczba w ekspresji naukowej (n.nnnnnn*10**(exp)- '10'jestself.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-from_string-function.

-baza 1 -> cyfry rzymskie?

-prof kompleks z aglami

Oto moje rozwiązanie:

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)
cmdLP
źródło
-2
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)

wynik:

3ewfdnca0n6ld1ggvfgg

na konwersję na dowolną bazę, odwrotność też jest łatwa.

Rogério Duarte
źródło
Mam NameError: global name 'n' is not defined. Czy divmod(x, n)ma być divmod(x, b)?
wjandrea