Antes de pular direto para bibliotecas prontas, talvez seja interessante estudar e entender um pouco mais sobre os fundamentos.
A Abordagem Clássica: Feistel Cipher
O Feistel Cipher é uma técnica fundamental usada em muitas funções de hash populares, como AES e Blowfish. Ele é um método que permite criar uma função pseudoaleatória que é invertível, o que significa que cada entrada única produzirá uma saída única e vice-versa. Embora a implementação completa de algoritmos como DES seja complexa, uma versão simplificada do Feistel Cipher pode ser usada para gerar identificadores únicos de forma eficiente.
É importante entender que os bits que compõem um número podem ser "convertidos" ou "casted" para qualquer base ou formato. Para obter um identificador alfanumérico de 6 caracteres, podemos simplesmente realizar uma mudança de base. Por exemplo, podemos converter o número gerado pelo Feistel Cipher para Base62, que utiliza os caracteres 0-9, a-z, e A-Z. Em teoria, a Base62 é ideal para esse tipo de aplicação porque é uma representação de qualquer número como uma string alfanúmerica.
Aqui está uma implementação simples:
import math
MULTIPLIER = int(math.sqrt(2) * 1000)
INCREMENT = int(math.pi * 100000)
MODULUS = 2**20 + 3
SCALING_FACTOR = 2**24 - 1
BASE62_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def feistel_cipher(val, rounds=3):
l1 = (val >> 16) & 0xffff
r1 = val & 0xffff
for i in range(rounds):
l2 = r1
r2 = l1 ^ int(math.floor(((MULTIPLIER * r1 + INCREMENT) % MODULUS) / MODULUS * SCALING_FACTOR))
l1 = l2
r1 = r2
return (r1 << 16) + l1
def to_base62(num):
if num == 0:
return BASE62_CHARS[0]
base62 = []
while num > 0:
remainder = num % 62
base62.append(BASE62_CHARS[remainder])
num = num // 62
return ''.join(reversed(base62))
def generate_id(index):
feistel_value = feistel_cipher(index)
base62_id = to_base62(feistel_value)
return base62_id.zfill(6)
print("Index | Feistel Value | Base62 ID")
print("------+-----------------+----------")
for i in range(-10, 11):
feistel_value = feistel_cipher(i)
final_id = generate_id(i)
print(f"{i:>5} | {feistel_value:>15} | {final_id}")
Aqui está a saída gerada:
Index | Feistel Value | Base62 ID
------+-----------------+----------
-10 | -2028606875 | 02Awtc
-9 | -377150308 | 01tvDI
-8 | -1826568944 | 02Apzz
-7 | -39235198 | 00d7x0
-6 | -2325185930 | 031qFc
-5 | -800687972 | 01F0e4
-4 | -2247551437 | 02WGBa
-3 | -822742845 | 01E1Vc
-2 | -243871210 | 00xciQ
-1 | -1707877051 | 02DcXw
0 | 2746720850 | 3jvTYx
1 | 867012857 | 1G8o2V
2 | 674985276 | 1AP7da
3 | 889674871 | 1H2OpP
4 | 745826968 | 1CytVA
5 | 2939497522 | 3o7SRK
6 | 1498284863 | 2cihU5
7 | 2970999551 | 3oyt9r
8 | 1134483750 | 2C5bNe
9 | 2670139713 | 3jBcnT
10 | 1222978124 | 2Ee2aY
A Realidade: Desempenho e Sugestões
Embora isso seja o que desejamos em teoria, essa implementação pode ser muito lenta para uso em produção. A finalidade dessa ilustração é mostrar como o processo funciona, mas em um ambiente real, você provavelmente não gostaria de fazer isso.
Sugestão Real:
Enquanto a solução que discutimos anteriormente serve como uma excelente ilustração de como conceitos como criptografia e conversão de base funcionam, para aplicações práticas, o uso de uma função de hash padrão e truncar o resultado é, na maioria dos casos, a melhor abordagem. Não há necessidade de reinventar a roda — aproveite as ferramentas robustas e otimizadas que já estão à sua disposição.
Além disso, evite adicionar bibliotecas desnecessárias ao seu projeto quando a solução já está disponível no seu ambiente. Mantenha seu código o mais simples possivel e você economizará tempo e recursos no longo prazo.
Realizar a geração de identificadores diretamente no banco de dados garante consistência, performance e reduz a complexidade na aplicação. No Postgres, por exemplo, você pode implementar essa lógica de forma simples usando um campo gerado, semelhante ao que o Git faz ao truncar hashes para gerar identificadores únicos e compactos.
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
unique_id VARCHAR(6) GENERATED ALWAYS AS (
substring(encode(digest(CAST(id AS TEXT), 'sha256'), 'hex') FOR 6)
) STORED
);
Essa solução é simples e direta e mantém seu código fácil sem introduzir nenhuma dependência desnecessária. Ao entender os fundamentos, você pode fazer escolhas mais informadas e eficazes para suas necessidades práticas, utilizando de forma inteligente as ferramentas disponíveis.