[Python] Gibt es eine schnellere Möglichkeit, eine Nummer in einen Namen umzuwandeln?


Answers

Was Sie haben, ist eine verfälschte Form der bijektiven Nummerierung (das übliche Beispiel ist Tabellenkalkulum Namen, die bijektive Basis-26 sind).

Eine Möglichkeit zur Generierung der bijektiven Nummerierung:

def bijective(n, digits=string.ascii_uppercase):
    result = []
    while n > 0:
        n, mod = divmod(n - 1, len(digits))
        result += digits[mod]
    return ''.join(reversed(result))

Sie müssen nur eine andere Zifferngruppe für den Fall 53 >= n > 0 in dem 53 >= n > 0 . Sie müssen auch n um 1 erhöhen, da die bijektive 0 die leere Zeichenfolge und nicht "A" :

def name(n, first=sorted(string.ascii_letters + '_'), digits=sorted(string.ascii_letters + '_' + string.digits)):
    result = []
    while n >= len(first):
        n, mod = divmod(n - len(first), len(digits))
        result += digits[mod]
    result += first[n]
    return ''.join(reversed(result))
Question

Der folgende Code definiert eine Sequenz von Namen, die Nummern zugeordnet sind. Es ist so konzipiert, dass es eine Nummer nimmt und einen bestimmten Namen abruft. Die Klasse stellt sicher, dass der Name in ihrem Cache vorhanden ist, und gibt den Namen dann zurück, indem sie in ihren Cache indexiert. Die Frage dabei: Wie kann der Name anhand der Nummer berechnet werden, ohne einen Cache zu speichern?

Der Name kann als Basis 63 Nummer gedacht werden, mit Ausnahme der ersten Ziffer, die immer in der Basis 53 ist.

class NumberToName:

    def __generate_name():
        def generate_tail(length):
            if length > 0:
                for char in NumberToName.CHARS:
                    for extension in generate_tail(length - 1):
                        yield char + extension
            else:
                yield ''
        for length in itertools.count():
            for char in NumberToName.FIRST:
                for extension in generate_tail(length):
                    yield char + extension

    FIRST = ''.join(sorted(string.ascii_letters + '_'))
    CHARS = ''.join(sorted(string.digits + FIRST))
    CACHE = []
    NAMES = __generate_name()

    @classmethod
    def convert(cls, number):
        for _ in range(number - len(cls.CACHE) + 1):
            cls.CACHE.append(next(cls.NAMES))
        return cls.CACHE[number]

    def __init__(self, *args, **kwargs):
        raise NotImplementedError()

Die folgenden interaktiven Sitzungen zeigen einige der Werte an, die erwartungsgemäß zurückgegeben werden.

>>> NumberToName.convert(0)
'A'
>>> NumberToName.convert(26)
'_'
>>> NumberToName.convert(52)
'z'
>>> NumberToName.convert(53)
'A0'
>>> NumberToName.convert(1692)
'_1'
>>> NumberToName.convert(23893)
'FAQ'

Leider müssen diese Nummern genau diesen Namen zugeordnet werden (um eine umgekehrte Konvertierung zu ermöglichen).

Bitte beachten Sie: Eine variable Anzahl von Bits wird empfangen und eindeutig in eine Zahl umgewandelt. Diese Nummer sollte eindeutig in einen Python-Bezeichner-Namespace konvertiert werden. Schließlich werden gültige Python-Namen in Zahlen umgewandelt, und diese Zahlen werden in eine variable Anzahl von Bits umgewandelt.

Endgültige Lösung:

import string

HEAD_CHAR = ''.join(sorted(string.ascii_letters + '_'))
TAIL_CHAR = ''.join(sorted(string.digits + HEAD_CHAR))
HEAD_BASE, TAIL_BASE = len(HEAD_CHAR), len(TAIL_CHAR)

def convert_number_to_name(number):
    if number < HEAD_BASE: return HEAD_CHAR[number]
    q, r = divmod(number - HEAD_BASE, TAIL_BASE)
    return convert_number_to_name(q) + TAIL_CHAR[r]



Sie können den Code in dieser Antwort auf die Frage "Base 62 Konvertierung in Python" (oder vielleicht eine der anderen Antworten) verwenden.

Mit dem referenzierten Code denke ich, dass die Antwort auf Ihre eigentliche Frage " Wie kann der Name anhand der Nummer berechnet werden, ohne einen Cache zu speichern? " Wäre, den Namen als einfache Basis-62-Umwandlung der Zahl mit einem führenden Unterstrich zu definieren wenn das erste Zeichen des Namens eine Ziffer ist (die einfach ignoriert wird, wenn der Name wieder in eine Zahl umgewandelt wird).

Hier ist ein Beispielcode, der veranschaulicht, was ich vorschlage:

from base62 import base62_encode, base62_decode

def NumberToName(num):
    ret = base62_encode(num)
    return ('_' + ret) if ret[0] in '0123456789' else ret

def NameToNumber(name):
    return base62_decode(name if name[0] is not '_' else name[1:])

if __name__ == '__main__':
    def test(num):
        name = NumberToName(num)
        num2 = NameToNumber(name)
        print 'NumberToName({0:5d}) -> {1!r:>6s}, NameToNumber({2!r:>6s}) -> {3:5d}' \
              .format(num, name, name, num2)

    test(26)
    test(52)
    test(53)
    test(1692)
    test(23893)

Ausgabe:

NumberToName(   26) ->    'q', NameToNumber(   'q') ->    26
NumberToName(   52) ->    'Q', NameToNumber(   'Q') ->    52
NumberToName(   53) ->    'R', NameToNumber(   'R') ->    53
NumberToName( 1692) ->   'ri', NameToNumber(  'ri') ->  1692
NumberToName(23893) -> '_6dn', NameToNumber('_6dn') -> 23893

Wenn die Zahlen negativ sein könnten, müssen Sie möglicherweise den Code aus der referenzierten Antwort ändern (und es gibt einige Diskussionen darüber, wie es gemacht wird).




Links