Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm to turn numeric IDs in to short, different alphanumeric codes

I have IDs from a database, and I want them to be short and easily differentiatable by eye (i.e., two close numbers look different).

Like this:

13892359163211 -> ALO2WE7
13992351216421 -> 52NBEK3

or similar, algorithmically. So kind of like a hash, except it needs to be reversible? An encryption algorithm like AES is almost ideal, except that its outputs are way too long. (and overkill).

I'm using Python (3), although I don't think that should really matter

like image 845
retnikt Avatar asked Dec 22 '25 04:12

retnikt


2 Answers

New answer with 'close' numbers looking different

You could use RSA to encrypt (and later decrypt) your numbers. This is definitely overkill - but ... here is the example: Install https://github.com/sybrenstuvel/python-rsa (pip install rsa)

import rsa
import rsa.core
# (pubkey, privkey) = rsa.newkeys(64) # Generate key pair
pubkey = rsa.PublicKey(n=9645943279888986023, e=65537)
privkey = rsa.PrivateKey(n=9645943279888986023, e=65537, d=7507666207464026273, p=9255782423, q=1042153201)

print("1st", rsa.core.encrypt_int(13892359163211, pubkey.e, pubkey.n))
print("2nd", rsa.core.encrypt_int(13992351216421, pubkey.e, pubkey.n))
print("1st", hex(rsa.core.encrypt_int(13892359163211, pubkey.e, pubkey.n))[2:])
print("2nd", hex(rsa.core.encrypt_int(13992351216421, pubkey.e, pubkey.n))[2:])

# If you want to compare a couple of numbers that are similar
for i in range (13892359163211, 13892359163251):
  encrypted = rsa.core.encrypt_int(i, pubkey.e, pubkey.n)
  # decrypted = rsa.core.decrypt_int(encrypted, privkey.d, privkey.n)
  print (i, hex(encrypted)[2:], encrypted)

Please not that you cannot encrypt numbers bigger than pubkey.n. This is a RSA related limitation. By generating a different keypair with a higher n you can circumvent this issue. If you would like all generated numbers to have the same length, prefix them with leading zeroes. You could also consider making them uppercase for better readability. To make the displayed strings shorter consider using the base62 encoding mentioned in my old answer below.

output

1st 5427392181794576250
2nd 7543432434424555966
1st 4b51f86f0c99177a
2nd 68afa7d5110929be

input          hex(encrypted)   encrypted
13892359163211 4b51f86f0c99177a 5427392181794576250
13892359163212 2039f9a3f5cf5d46 2322161565485194566
13892359163213 173997b57918a6c3 1673535542221383363
13892359163214 36644663653bbb4  244958435527080884
13892359163215 c2eeec0c054e633  877901489011746355
...

Old answer related to displaying the numbers a bit shorter, not being aware that they should look substantially different

You want to change the base of your number from 10 to something bigger to use less characters. See https://stackoverflow.com/a/1119769 for an example with base 62 (a-zA-Z0-9).

Or quick and dirty for base 16, (0-9A-F, hexadecimal).

hex(13892359163211)[2:] # -> 'ca291220d4b'
like image 134
nitzel Avatar answered Dec 23 '25 17:12

nitzel


How about finding crc32 for the input and showing the result in hex?

>>> n = 13892359163211
>>> 
>>> import binascii
>>> hex(binascii.crc32(str(n).encode()))[2:]
'240a831a'
like image 37
Prem Anand Avatar answered Dec 23 '25 18:12

Prem Anand



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!