2012-10-06 21 views
11

Estoy trabajando en un sistema que genera identificadores aleatorios como en la respuesta # 2 here.pseudo_encrypt() función en plpgsql que toma bigint

Mi problema es que la función pseudo_encrypt() mencionada funciona con int not bigint. Traté de reescribirlo pero siempre devuelve el mismo resultado:

CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE bigint) returns bigint AS $$ 
DECLARE 
l1 bigint; 
l2 int; 
r1 bigint; 
r2 int; 
i int:=0; 
BEGIN 
    l1:= (VALUE >> 32) & 4294967296::bigint; 
    r1:= VALUE & 4294967296; 
    WHILE i < 3 LOOP 
     l2 := r1; 
     r2 := l1 # ((((1366.0 * r1 + 150889) % 714025)/714025.0) * 32767)::int; 
     l1 := l2; 
     r1 := r2; 
     i := i + 1; 
    END LOOP; 
RETURN ((l1::bigint << 32) + r1); 
END; 
$$ LANGUAGE plpgsql strict immutable; 

¿Alguien puede verificar esto?

Respuesta

16

4294967295 se debe utilizar como la máscara de bits para seleccionar 32 bits (en lugar de 4294967296). Esa es la razón por la que actualmente obtienes el mismo valor para diferentes entradas.

También me gustaría sugerir el uso de bigint para los tipos de l2 y r2, que en realidad no debería diferir de r1 y l1

Y, para una mejor aleatoriedad, utilice un multiplicador mucho mayor en la función PRNG para obtener secuencia intermedia, que realmente ocupan 32 bits, al igual que 32.767 * 32.767 en lugar de 32.767

La versión modificada completa:

CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE bigint) returns bigint AS $$ 
DECLARE 
l1 bigint; 
l2 bigint; 
r1 bigint; 
r2 bigint; 
i int:=0; 
BEGIN 
    l1:= (VALUE >> 32) & 4294967295::bigint; 
    r1:= VALUE & 4294967295; 
    WHILE i < 3 LOOP 
     l2 := r1; 
     r2 := l1 # ((((1366.0 * r1 + 150889) % 714025)/714025.0) * 32767*32767)::int; 
     l1 := l2; 
     r1 := r2; 
     i := i + 1; 
    END LOOP; 
RETURN ((l1::bigint << 32) + r1); 
END; 
$$ LANGUAGE plpgsql strict immutable; 

Primeros resultados:

 
select x,pseudo_encrypt(x::bigint) from generate_series (1, 10) as x; 
x | pseudo_encrypt  
----+--------------------- 
    1 | 3898573529235304961 
    2 | 2034171750778085465 
    3 | 169769968641019729 
    4 | 2925594765163772086 
    5 | 1061193016228543981 
    6 | 3808195743949274374 
    7 | 1943793931158625313 
    8 | 88214277952430814 
    9 | 2835217030863818694 
10 | 970815170807835400 
(10 rows) 
+0

Ah la máscara de bits era un error estúpido ... Muchas gracias ¡mucho! Funciona perfectamente ahora! Por cierto, querías sugerir bigint, no int. –

+0

@Daniel Vérité ¿Qué tengo que modificar en la producción si necesito un bigint con una longitud máxima de 13 dígitos? – MattC

+0

@MattC: no es trivial, puede enviar esto como una nueva pregunta. También con esta técnica el límite superior va a ser '2^N' donde' N' es un número par, no '10^N'. –

5

Antigua pero una pregunta todavía interesante. En comparación con la respuesta Daniels Estoy utilizando una versión ligeramente modificada, cambiando la instrucción de retorno a esta (intercambiados R1 y L1), como también se menciona al final del artículo Pseudo encrypt:

RETURN ((r1::bigint << 32) + l1); 

La razón de este cambio es que el Feistel algorithm subyacente no debe cambiar a la izquierda y a la derecha al final de la última ronda. Con este cambio la función recupera la capacidad de actuar como su propia función inversa:

pseudo_encrypt(pseudo_encrypt(x) == x // always returns true 

Aquí está el código completo en pgsql:

CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE bigint) returns bigint AS $$ 
DECLARE 
l1 bigint; 
l2 bigint; 
r1 bigint; 
r2 bigint; 
i int:=0; 
BEGIN 
    l1:= (VALUE >> 32) & 4294967295::bigint; 
    r1:= VALUE & 4294967295; 
    WHILE i < 3 LOOP 
     l2 := r1; 
     r2 := l1 # ((((1366.0 * r1 + 150889) % 714025)/714025.0) * 32767*32767)::int; 
     l1 := l2; 
     r1 := r2; 
    i := i + 1; 
    END LOOP; 
RETURN ((r1::bigint << 32) + l1); 
END; 
$$ LANGUAGE plpgsql strict immutable; 
Cuestiones relacionadas