整数を62進数に変換するにはどうすればよいですか(16進数のようですが、これらの数字は '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')。
私はそれに適したPythonライブラリを見つけようとしていますが、それらはすべて文字列の変換に専念しているようです。Python base64モジュールは文字列のみを受け入れ、1桁を4文字に変換します。私はURL短縮サービスが使用するものに似たものを探していました。
これには標準モジュールはありませんが、それを実現するために独自の関数を作成しました。
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(num, alphabet):
"""Encode a positive number into Base X and return the string.
Arguments:
- `num`: The number to encode
- `alphabet`: The alphabet to use for encoding
"""
if num == 0:
return alphabet[0]
arr = []
arr_append = arr.append # Extract bound-method for faster access.
_divmod = divmod # Access to locals is faster.
base = len(alphabet)
while num:
num, rem = _divmod(num, base)
arr_append(alphabet[rem])
arr.reverse()
return ''.join(arr)
def decode(string, alphabet=BASE62):
"""Decode a Base X encoded string into the number
Arguments:
- `string`: The encoded string
- `alphabet`: The alphabet to use for decoding
"""
base = len(alphabet)
strlen = len(string)
num = 0
idx = 0
for char in string:
power = (strlen - (idx + 1))
num += alphabet.index(char) * (base ** power)
idx += 1
return num
エンコードとデコードに使用する任意のアルファベットを指定できることに注意してください。引数を省略した場合alphabet
、コードの最初の行で定義された 62 文字のアルファベットが取得されるため、62 ベースへの/からのエンコード/デコードが行われます。
お役に立てれば。
PS - URL 短縮機能については、0Ol1oI などのいくつかの紛らわしい文字を除外した方がよいことがわかりました。したがって、URL 短縮のニーズにこのアルファベットを使用しています -"23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
楽しむ。
私もこれを行うスクリプトを書いたことがありますが、とてもエレガントだと思います:)
import string
# Remove the `_@` below for base62, now it has 64 characters
BASE_LIST = string.digits + string.letters + '_@'
BASE_DICT = dict((c, i) for i, c in enumerate(BASE_LIST))
def base_decode(string, reverse_base=BASE_DICT):
length = len(reverse_base)
ret = 0
for i, c in enumerate(string[::-1]):
ret += (length ** i) * reverse_base[c]
return ret
def base_encode(integer, base=BASE_LIST):
if integer == 0:
return base[0]
length = len(base)
ret = ''
while integer != 0:
ret = base[integer % length] + ret
integer /= length
return ret
使用例:
for i in range(100):
print i, base_decode(base_encode(i)), base_encode(i)
次のデコーダー メーカーは、妥当なベースで動作し、より整然としたループを持ち、無効な文字に遭遇すると明示的なエラー メッセージを表示します。
def base_n_decoder(alphabet):
"""Return a decoder for a base-n encoded string
Argument:
- `alphabet`: The alphabet used for encoding
"""
base = len(alphabet)
char_value = dict(((c, v) for v, c in enumerate(alphabet)))
def f(string):
num = 0
try:
for char in string:
num = num * base + char_value[char]
except KeyError:
raise ValueError('Unexpected character %r' % char)
return num
return f
if __name__ == "__main__":
func = base_n_decoder('0123456789abcdef')
for test in ('0', 'f', '2020', 'ffff', 'abqdef'):
print test
print func(test)
最高の効率 (django など) を探している場合は、次のようなものが必要になります。このコードは、Baishampayan Ghose と WoLpH および John Machin の効率的なメソッドを組み合わせたものです。
# Edit this list of characters as desired.
BASE_ALPH = tuple("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
BASE_DICT = dict((c, v) for v, c in enumerate(BASE_ALPH))
BASE_LEN = len(BASE_ALPH)
def base_decode(string):
num = 0
for char in string:
num = num * BASE_LEN + BASE_DICT[char]
return num
def base_encode(num):
if not num:
return BASE_ALPH[0]
encoding = ""
while num:
num, rem = divmod(num, BASE_LEN)
encoding = BASE_ALPH[rem] + encoding
return encoding
事前に辞書を計算することもできます。(注: 非常に長い数値であっても、文字列でのエンコードはリストよりも効率的です。)
>>> timeit.timeit("for i in xrange(1000000): base.base_decode(base.base_encode(i))", setup="import base", number=1)
2.3302059173583984
2.5 秒未満で 100 万の数字をエンコードおよびデコードしました。(2.2Ghz i7-2670QM)
何かをエンコード/デコードするのではなく、短いIDを生成するだけでよい場合(URL短縮サービスについて言及しているため)、このモジュールが役立つ場合があります。
おそらくbase62ではなくbase64が必要です。それの URL 互換バージョンがあちこちに出回っているので、余分な 2 つのフィラー文字は問題にならないはずです。
プロセスは非常に簡単です。base64 は 6 ビットを表し、通常のバイトは 8 を表すと考えてください。選択した 64 文字のそれぞれに 000000 から 111111 までの値を割り当て、4 つの値を組み合わせて 3 つの base256 バイトのセットに一致させます。3 バイトのセットごとに繰り返し、選択したパディング文字 (通常は 0 が便利です) で最後にパディングします。
私はここで他の人の投稿から大きな恩恵を受けました。もともとDjangoプロジェクト用のPythonコードが必要でしたが、それ以来node.jsに目を向けたので、Baishampayan Ghoseが提供したコードのjavascriptバージョン(エンコード部分)を次に示します。
var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
function base62_encode(n, alpha) {
var num = n || 0;
var alphabet = alpha || ALPHABET;
if (num == 0) return alphabet[0];
var arr = [];
var base = alphabet.length;
while(num) {
rem = num % base;
num = (num - rem)/base;
arr.push(alphabet.substring(rem,rem+1));
}
return arr.reverse().join('');
}
console.log(base62_encode(2390687438976, "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"));
これが私の解決策です:
def base62(a):
baseit = (lambda a=a, b=62: (not a) and '0' or
baseit(a-a%b, b*62) + '0123456789abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[a%b%61 or -1*bool(a%b)])
return baseit()
どの基数でも、すべての数は等しいです。 a1+a2*base**2+a3*base**3...
したがって、目標はすべてのa
s を見つけることです。
すべてN=1,2,3...
のコードは、関数が現在の によって再帰的に呼び出されるたびに減少することによって、すべての s が よりも大きいスライスをaN*base**N
「モジュロ化」しb
、b=base**(N+1)
すべての s をスライスして、それらのシリアルが減少することによって を分離します。a
N
a
N
a
aN*base**N
Base%(base-1)==1
したがってbase**p%(base-1)==1
、したがってq*base^p%(base-1)==q
、例外が 1 つだけq==base-1
あります0
。そのケースを修正するには、 を返します0
。関数は最初からチェックします0
。
このサンプルでは、1 つの乗算 (除算ではなく) といくつかのモジュラス演算のみがあり、これらはすべて比較的高速です。
次のスニペットが役立つことを願っています。
def num2sym(num, sym, join_symbol=''):
if num == 0:
return sym[0]
if num < 0 or type(num) not in (int, long):
raise ValueError('num must be positive integer')
l = len(sym) # target number base
r = []
div = num
while div != 0: # base conversion
div, mod = divmod(div, l)
r.append(sym[mod])
return join_symbol.join([x for x in reversed(r)])
あなたの場合の使用法:
number = 367891
alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
print num2sym(number, alphabet) # will print '1xHJ'
明らかに、より少ないまたはより多い数の記号で構成される別のアルファベットを指定できます。その後、数値をより少ないまたはより多い基数に変換します。たとえば、アルファベットとして '01' を指定すると、入力数値を表す文字列がバイナリとして出力されます。
最初にアルファベットをシャッフルして、数字を独自に表現することができます。URL 短縮サービスを作成している場合に役立ちます。
個人的には、Baishampayan のソリューションが気に入っています。主に、紛らわしい文字が取り除かれているためです。
完全性とパフォーマンスの向上を目的として、この投稿では Python base64 モジュールを使用する方法を示します。
これを行うための再帰的かつ反復的な方法を次に示します。反復的なものは、実行回数に応じて少し速くなります。
def base62_encode_r(dec):
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
return s[dec] if dec < 62 else base62_encode_r(dec / 62) + s[dec % 62]
print base62_encode_r(2347878234)
def base62_encode_i(dec):
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ret = ''
while dec > 0:
ret = s[dec % 62] + ret
dec /= 62
return ret
print base62_encode_i(2347878234)
def base62_decode_r(b62):
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
if len(b62) == 1:
return s.index(b62)
x = base62_decode_r(b62[:-1]) * 62 + s.index(b62[-1:]) % 62
return x
print base62_decode_r("2yTsnM")
def base62_decode_i(b62):
s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ret = 0
for i in xrange(len(b62)-1,-1,-1):
ret = ret + s.index(b62[i]) * (62**(len(b62)-i-1))
return ret
print base62_decode_i("2yTsnM")
if __name__ == '__main__':
import timeit
print(timeit.timeit(stmt="base62_encode_r(2347878234)", setup="from __main__ import base62_encode_r", number=100000))
print(timeit.timeit(stmt="base62_encode_i(2347878234)", setup="from __main__ import base62_encode_i", number=100000))
print(timeit.timeit(stmt="base62_decode_r('2yTsnM')", setup="from __main__ import base62_decode_r", number=100000))
print(timeit.timeit(stmt="base62_decode_i('2yTsnM')", setup="from __main__ import base62_decode_i", number=100000))
0.270266867033
0.260915645986
0.344734796766
0.311662500262
私はこれをしばらく前に書きましたが、かなりうまくいきました(ネガとすべてが含まれています)
def code(number,base):
try:
int(number),int(base)
except ValueError:
raise ValueError('code(number,base): number and base must be in base10')
else:
number,base = int(number),int(base)
if base < 2:
base = 2
if base > 62:
base = 62
numbers = [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j",
"k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
"z","A","B","C","D","E","F","G","H","I","J","K","L","M","N",
"O","P","Q","R","S","T","U","V","W","X","Y","Z"]
final = ""
loc = 0
if number < 0:
final = "-"
number = abs(number)
while base**loc <= number:
loc = loc + 1
for x in range(loc-1,-1,-1):
for y in range(base-1,-1,-1):
if y*(base**x) <= number:
final = "{}{}".format(final,numbers[y])
number = number - y*(base**x)
break
return final
def decode(number,base):
try:
int(base)
except ValueError:
raise ValueError('decode(value,base): base must be in base10')
else:
base = int(base)
number = str(number)
if base < 2:
base = 2
if base > 62:
base = 62
numbers = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
"g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v",
"w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L",
"M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
final = 0
if number.startswith("-"):
neg = True
number = list(number)
del(number[0])
temp = number
number = ""
for x in temp:
number = "{}{}".format(number,x)
else:
neg = False
loc = len(number)-1
number = str(number)
for x in number:
if numbers.index(x) > base:
raise ValueError('{} is out of base{} range'.format(x,str(base)))
final = final+(numbers.index(x)*(base**loc))
loc = loc - 1
if neg:
return -final
else:
return final
全体的に長くてすみません
BASE_LIST = tuple("23456789ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz")
BASE_DICT = dict((c, v) for v, c in enumerate(BASE_LIST))
BASE_LEN = len(BASE_LIST)
def nice_decode(str):
num = 0
for char in str[::-1]:
num = num * BASE_LEN + BASE_DICT[char]
return num
def nice_encode(num):
if not num:
return BASE_LIST[0]
encoding = ""
while num:
num, rem = divmod(num, BASE_LEN)
encoding += BASE_LIST[rem]
return encoding
申し訳ありませんが、ここのライブラリについてはお手伝いできません。base64 を使用して、選択した文字を追加することをお勧めします-可能であれば!
次に、base64 モジュールを使用できます。
これが本当に、本当に不可能な場合:
この方法で自分で行うことができます (これは疑似コードです)。
base62vals = []
myBase = 62
while num > 0:
reminder = num % myBase
num = num / myBase
base62vals.insert(0, reminder)