كيفية تحويل عدد صحيح في أي قاعدة إلى سلسلة؟

StackOverflow https://stackoverflow.com/questions/2267362

  •  20-09-2019
  •  | 
  •  

سؤال

يسمح Python بسهولة إنشاء عدد صحيح من سلسلة من قاعدة معينة عبر

int(str, base). 

أريد أن أعكس: إنشاء سلسلة من عدد صحيح, ، أي أريد بعض الوظائف int2base(num, base), ، مثل ذلك:

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

أمر الوظيفة/ترتيب الوسيطة غير مهم.

لأي رقم x والقاعدة b الذي - التي int() سيقبل.

هذه وظيفة سهلة للكتابة: في الواقع ، إنها أسهل من وصفها في هذا السؤال. ومع ذلك ، أشعر أنني يجب أن أفتقد شيئًا.

أنا أعرف عن الوظائف bin, oct, hex, ، لكن لا يمكنني استخدامها لعدة أسباب:

  • هذه الوظائف غير متوفرة في الإصدارات القديمة من Python ، والتي أحتاج إليها مع (2.2)

  • أريد حلًا عامًا يمكن أن يسمى بنفس الطريقة لقواعد مختلفة

  • أريد أن أسمح قواعد أخرى بخلاف 2 ، 8 ، 16

متعلق ب

هل كانت مفيدة؟

المحلول

إذا كنت بحاجة إلى توافق مع الإصدارات القديمة من Python ، يمكنك إما استخدامها GMPY (والتي تشمل وظيفة تحويل سريعة ، عامة بشكل عام ، ويمكن بناؤها لمثل هذه الإصدارات القديمة-قد تحتاج إلى تجربة الإصدارات القديمة لأن الإصدارات الحديثة لم يتم اختبارها لإصدارات Python و GMP الموقرة ، فقط تلك الحديثة إلى حد ما) ، أو ، لأقل سرعة ولكن أكثر راحة ، استخدم رمز Python - على سبيل المثال ، ببساطة:

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)

نصائح أخرى

من المثير للدهشة أن الناس كانوا يقدمون حلولًا فقط تتحول إلى قواعد صغيرة (أصغر ثم طول الأبجدية الإنجليزية). لم تكن هناك محاولة لإعطاء حل يتحول إلى أي قاعدة تعسفية من 2 إلى اللانهاية.

إذن هنا حل بسيط للغاية:

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

لذلك إذا كنت بحاجة إلى تحويل بعض الأرقام الضخمة إلى القاعدة 577,

numberToBase(67854 ** 15 - 102, 577), ، سوف يعطيك حلا صحيحا:[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],

الذي يمكنك تحويله لاحقًا إلى أي قاعدة تريدها

def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])

المرجع:http://code.activestate.com/recipes/65212/

يرجى العلم أن هذا قد يؤدي إلى

RuntimeError: maximum recursion depth exceeded in cmp

لأعداد صحيحة كبيرة جدا.

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

إجابات رائعة! أعتقد أن الإجابة على سؤالي كانت "لا" لم أكن أفتقد بعض الحلول الواضحة. إليكم الوظيفة التي سأستخدمها والتي تكثف الأفكار الجيدة المعبر عنها في الإجابات.

  • السماح لرسم الخرائط المتقدمة من المتصل للأحرف (يسمح بتشفير BASE64)
  • يتحقق من السلبية والصفر
  • يخلع أرقام معقدة في tuples من السلاسل


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 على وظيفة مدمجة لطباعة عدد صحيح في قاعدة تعسفية. عليك أن تكتب بنفسك إذا كنت تريد ذلك.

يمكنك استخدام baseconv.py من مشروعي: https://github.com/semente/python-baseconv

استخدام العينة:

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

هناك بعض محولات Bultin كما على سبيل المثال baseconv.base2, baseconv.base16 و baseconv.base64.

العودية

أود تبسيط ال الجواب الأكثر تصويت ل:

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

بنفس النصيحة ل RuntimeError: maximum recursion depth exceeded in cmp على أعداد صحيحة كبيرة جدا والأرقام السلبية. (يمكنك استخدامsys.setrecursionlimit(new_limit))

ترابطي

ل تجنب مشاكل العودية:

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

إليك واحدة أخرى من نفس الرابط

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

لقد صنعت حزمة PIP لهذا الغرض.

أنصحك باستخدام قواعد بلدي https://github.com/kamijoutouma/bases.py الذي كان مستوحى من القواعد

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#monner-basesalphabetsلأي قواعد قابلة للاستخدام

تحرير: رابط 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)

انتاج:

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

حل عودية للمهتمين. بالطبع ، لن يعمل هذا مع القيم الثنائية السلبية. ستحتاج إلى تنفيذ تكملة اثنين.

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

تفسير

في أي قاعدة كل رقم يساوي a1+a2*base**2+a3*base**3... "المهمة" هي العثور على كل شيء.

لكلN=1,2,3... الرمز يعزل aN*base**N عن طريق "mouduling" من قبل ب ل b=base**(N+1) الذي يقطع كل شيء أكبر من n ، وتقطيع كل ما هو مسلسلهم أصغر من n عن طريق تقليل في كل مرة يطلق عليها Func من قبل التيار aN*base**N .

BASE ٪ (base-1) == 1 to base ** p ٪ (base-1) == 1 وهكاك Q*base^p ٪ (base-1) == Q مع استثناء واحد فقط عند Q = base-1 الذي يعود 0. لإصلاح أنه في حالة إرجاع 0 ، فإن FUNC تتحقق من أنه 0 من التجول.


مزايا

في هذه العينة ، هناك مضاعفات واحدة فقط (بدلاً من الانقسام) وبعض moudulueses التي تستغرق نسبيًا كميات صغيرة من الوقت.

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)

واحد قصير آخر (وأسهل لفهم IMO):

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

ومع معالجة الاستثناء الصحيح:

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

حل آخر ، يعمل مع قاعدة 2 إلى 10 ، يحتاج إلى تعديل للقواعد العليا:

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

مثال:

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

فيما يلي نسخة متكررة تتعامل مع الأعداد الصحيحة الموقعة والأرقام المخصصة.

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]

الأوتار ليست الخيار الوحيد لتمثيل الأرقام: يمكنك استخدام قائمة من الأعداد الصحيحة لتمثيل ترتيب كل رقم. يمكن تحويل هذه بسهولة إلى سلسلة.

لا شيء من الإجابات يرفض قاعدة <2 ؛ وسيعمل معظمهم ببطء شديد أو يصطدمون بفيضان المكدس كبير جدا الأرقام (مثل 56789 ** 43210). لتجنب مثل هذه الإخفاقات ، قلل بسرعة مثل هذا:

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 مماثلة مع str بالنسبة للأعداد الكبيرة (حوالي 0.3 ثانية على الجهاز الخاص بي) ، ولكن إذا قارنت ضد hex قد تتفاجأ (حوالي 0.3 مللي ثانية على الجهاز الخاص بي ، أو 1000x أسرع). والسبب هو أنه يتم تخزين عدد صحيح كبير في الذاكرة في القاعدة 256 (بايت). يمكن ببساطة تحويل كل بايت إلى سلسلة سداسية سداسيين. لا تحدث هذه المحاذاة فقط للقواعد التي تمثل قوى اثنين ، وهذا هو السبب في وجود حالات خاصة لـ 2،8 و 16 (و BASE64 ، ASCII ، UTF16 ، UTF32).

النظر في الرقم الأخير من سلسلة عشرية. كيف ترتبط بتسلسل البايتات التي تشكل عدد صحيح؟ دعنا نصف البايتات s[i] مع s[0] كونه الأقل أهمية (ليتل إنديان). ثم الرقم الأخير هو sum([s[i]*(256**i) % 10 for i in range(n)]). حسنًا ، يحدث أن 256 ** أنا انتهى بـ 6 لـ i> 0 (6*6 = 36) بحيث يكون الرقم الأخير هو (s[0]*5 + sum(s)*6)%10. من هذا ، يمكنك أن ترى أن الرقم الأخير يعتمد على مجموع جميع البايتات. هذه الخاصية غير المحلية هي ما يجعل التحويل إلى عشرية أكثر صعوبة.

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

لم أر أي محولات تعويم هنا. وفقدت التجميع لثلاثة أرقام دائمًا.

لكى يفعل:

-اللوم في التعبير العلمي (n.nnnnnn*10**(exp) -- ال '10' هو self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-Rom_String-Function.

-القاعدة 1 -> الأرقام الرومانية؟

-repr of complex with agles

إذن هذا هو الحل:

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)

انتاج:

3ewfdnca0n6ld1ggvfgg

للتحويل إلى أي قاعدة ، يكون العكسي سهلًا أيضًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top