[英]Is there a faster way of converting a number to a name?
以下代碼定義了映射到數字的名稱序列。 它旨在獲取一個數字並檢索特定名稱。 該類通過確保名稱存在於其緩存中來運行,然后通過索引將其返回到其緩存中來返回名稱。 問題在於: 如何在不存儲緩存的情況下根據數量計算名稱?
該名稱可以被認為是基數63,除了始終在基數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()
以下交互式會話顯示了一些預期按順序返回的值。
>>> NumberToName.convert(0)
'A'
>>> NumberToName.convert(26)
'_'
>>> NumberToName.convert(52)
'z'
>>> NumberToName.convert(53)
'A0'
>>> NumberToName.convert(1692)
'_1'
>>> NumberToName.convert(23893)
'FAQ'
不幸的是,這些數字需要映射到這些確切的名稱(以允許反向轉換)。
請注意:接收可變數量的位並將其明確轉換為數字。 應將此數字明確轉換為Python標識名稱空間中的名稱。 最終,有效的Python名稱將轉換為數字,這些數字將轉換為可變數量的位。
最終解決方案
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]
這是一個充滿1個錯誤的有趣小問題。
沒有循環:
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]
測試:
print convert(0)
print convert(26)
print convert(52)
print convert(53)
print convert(1692)
print convert(23893)
輸出:
A
_
z
A0
_1
FAQ
你得到的是一種損壞的雙射數字形式(通常的例子是電子表格列名,它是雙射基數 - 26)。
生成雙射計數的一種方法:
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))
您需要做的就是為53 >= n > 0
的情況提供一組不同的數字。 您還需要將n遞增1,因為正確的雙射0
是空字符串,而不是"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))
測試前10,000個名稱:
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
您可以使用此答案中的代碼“Python中的Base 62轉換”(或者可能是其他答案之一)。
使用引用的代碼,我認為你的真實問題的答案是“ 如何根據數字計算名稱而不存儲緩存? ”將使名稱成為簡單的基數62轉換數字可能帶有前導下划線如果名稱的第一個字符是一個數字(在將名稱轉換回數字時會被忽略)。
這是示例代碼,說明了我的建議:
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)
輸出:
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
如果數字可能是負數,您可能必須修改引用答案中的代碼(並且有關於如何執行此操作的討論)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.