2012-06-15 29 views
8

El siguiente código define una secuencia de nombres que están asignados a números. Está diseñado para tomar un número y recuperar un nombre específico. La clase funciona asegurando que el nombre existe en su caché, y luego devuelve el nombre indizando en su caché. La pregunta en esto: ¿cómo se puede calcular el nombre basado en el número sin almacenar un caché?¿Existe alguna forma más rápida de convertir un número en un nombre?

El nombre puede ser pensado como un número de base 63, a excepción de la primera cifra que siempre está en la base 53.

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

Las siguientes sesiones interactivas muestran algunos de los valores que se espera que sean regresó en orden.

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

Lamentablemente, estos números deben correlacionarse con estos nombres exactos (para permitir una conversión inversa).


Tenga en cuenta: Un número variable de bits se reciben y se convierte de forma inequívoca en un número. Este número se debe convertir sin ambigüedades a un nombre en el espacio de nombres del identificador de Python. Finalmente, los nombres válidos de Python se convertirán en números, y estos números se convertirán a una cantidad variable de bits.


solución final:

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] 
+0

¿Por qué este requisito especial? ¿Podrías por favor elaborar el propósito de no caché? –

+0

El caché consume mucha memoria que realmente no debería ser necesaria. – recursive

+2

Se recibe una cantidad variable de bits y se convierte sin ambigüedad en un número. Este número se debe convertir sin ambigüedades a un nombre en el espacio de nombres del identificador de Python. Finalmente, los nombres válidos de Python se convertirán en números, y estos números se convertirán a una cantidad variable de bits. –

Respuesta

7

Este es un pequeño problema diversión llena de fuera por 1 errores.

Sin bucles:

import string 

first_digits = sorted(string.ascii_letters + '_') 
rest_digits = sorted(string.digits + string.ascii_letters + '_') 

def convert(number): 
    if number < len(first_digits): 
     return first_digits[number] 

    current_base = len(rest_digits) 
    remain = number - len(first_digits) 
    return convert(remain/current_base) + rest_digits[remain % current_base] 

Y las pruebas:

print convert(0) 
print convert(26) 
print convert(52) 
print convert(53) 
print convert(1692) 
print convert(23893) 

Salida:

A 
_ 
z 
A0 
_1 
FAQ 
+0

Gracias por su ayuda! Ver su variable' remain' ayudó mucho. –

+1

Alternativa para las últimas tres líneas: 'number, remain = divmod (number - len (first_digits), len (rest_digits)); return convert (número) + rest_digits [remain] '. –

+0

Usar recursion en lugar de looping no es necesariamente más rápido (no es lo que dijiste que era). Sin embargo, reduce el número de líneas de código. ¡Buena respuesta! – martineau

1

Usted puede utilizar el código en this respuesta a la pregunta "Base 62 conversión en Python" (o tal vez una de las otras respuestas).

Utilizando el código de referencia, creo que la respuesta a su verdadera cuestión que era "cómo se puede calcular el nombre basado en el número sin almacenar una memoria caché?" sería la de hacer que el nombre de la simple conversión de base 62 del número posiblemente con un guión bajo inicial si el primer carácter del nombre es un dígito (que simplemente se ignora al convertir el nombre de nuevo en un número).

Aquí está el código de ejemplo que ilustra lo que propongo:

from base62 import base62_encode, base62_decode 

def NumberToName(num): 
    ret = base62_encode(num) 
    return ('_' + ret) if ret[0] in '' 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) 

Salida:

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 

Si los números podrían ser negativo, es posible que tenga que modificar el código de la respuesta que se hace referencia (y no hay alguna discusión allí sobre cómo hacerlo).

2

probado por primera 10.000 nombres:

first_chars = sorted(string.ascii_letters + '_') 
later_chars = sorted(list(string.digits) + first_chars) 

def f(n): 
    # first, determine length by subtracting the number of items of length l 
    # also determines the index into the list of names of length l 
    ix = n 
    l = 1 
    while ix >= 53 * (63 ** (l-1)): 
     ix -= 53 * (63 ** (l-1)) 
     l += 1 

    # determine first character 
    first = first_chars[ix // (63 ** (l-1))] 

    # rest of string is just a base 63 number 
    s = '' 
    rem = ix % (63 ** (l-1)) 
    for i in range(l-1): 
     s = later_chars[rem % 63] + s 
     rem //= 63 

    return first+s 
3

Lo que tenemos es una forma corrupta de bijective numeration (el ejemplo habitual de ser nombres de columna de hoja de cálculo, que son la base biyectiva-26).

Una forma de generar la numeración biyectiva:

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

Todo lo que necesita hacer es suministrar un conjunto diferente de dígitos para el caso en 53 >= n > 0. También tendrá que incrementar n en 1, como correctamente el biyectiva 0 es la cadena vacía, no "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)) 
Cuestiones relacionadas