8

次のコードは、数値にマップされる名前のシーケンスを定義します。番号を取得して特定の名前を取得するように設計されています。このクラスは、名前がキャッシュに存在することを確認して動作し、キャッシュにインデックスを付けて名前を返します。この質問:キャッシュを保存せずに、番号に基づいて名前を計算するにはどうすればよいですか?

名前は、常にベース53にある最初の桁を除いて、ベース63の番号と考えることができます。

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

4 に答える 4

7

これは、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
于 2012-06-15T15:32:15.303 に答える
3

あなたが持っているのは、全単射の数え上げの破損した形式です(通常の例は、全単射の基数であるスプレッドシートの列名です-26)。

全単射数を生成する1つの方法:

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))
于 2012-06-15T15:31:48.360 に答える
2

最初の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
于 2012-06-15T15:25:07.797 に答える
1

この回答のコードを、質問「Python での Base 62 変換」(またはおそらく他の回答の 1 つ) に使用できます。

参照されたコードを使用して、「キャッシュを保存せずに数値に基づいて名前を計算するにはどうすればよいですか? 」という本当の質問への答えは、おそらく先頭にアンダースコアを付けて、数値の単純な base 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

数値が負の可能性がある場合は、参照されている回答からコードを変更する必要がある場合があります (その方法についてはいくつかの議論があります)。

于 2012-06-15T15:16:14.570 に答える