2012-10-06 5 views
11

Pracuję nad systemem, który generuje losowe identyfikatory takie jak w odpowiedzi # 2 here.Funkcja pseudo_encrypt() w plpgsql, która zajmuje się bigintem

Mój problem polega na tym, że wspomniana funkcja pseudo_encrypt() działa z int not bigint. Próbowałem przepisać go ponownie, ale zawsze zwraca ten sam wynik:

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; 

Czy ktoś może to sprawdzić?

Odpowiedz

16

4294967295 musi być użyty jako maska ​​bitowa do wyboru 32 bitów (zamiast 4294967296). To jest powód, dla którego obecnie otrzymujesz tę samą wartość dla różnych wejść.

Chciałbym również zasugerować używając bigint dla typów l2 i r2, nie powinny one naprawdę różnią się od r1 i l1

I, dla lepszej losowości, należy znacznie wyższy mnożnik w funkcji PRNG dostać blok pośredni, które zajmują bardzo 32 bitów, jak 32767 do 32767 * zamiast 32767.

pełnej wersji zmodyfikowanej:

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; 

Pierwsze wyniki:

 
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 maskę bitową był głupi błąd ... Dziękuję bardzo dużo! Działa teraz idealnie! Btw: chciałem zasugerować bigint, nie int. –

+0

@ Daniel Vérité Co muszę zmodyfikować w fuction, jeśli potrzebuję biginta o maksymalnej długości 13 cyfr? – MattC

+0

@MattC: nie jest to banalne, możesz przesłać to jako nowe pytanie. Również przy tej technice górną granicą będzie "2^N", gdzie 'N' jest liczbą parzystą, a nie' 10^N'. –

5

Stare, ale wciąż interesujące pytanie. Porównując do Daniels odpowiedź Używam wersji nieco zmodyfikowanej, zmieniając oświadczenie wrócić do tego (wymieniane R1 i L1), a także wspomniany na końcu artykułu Pseudo encrypt:

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

Powodem tej zmiany jest to, że podstawowy Feistel algorithm nie powinien wymieniać lewej i prawej strony pod koniec ostatniej rundy. Dzięki tej zmianie funkcja odzyskuje zdolność do działania jako własnej funkcji odwrotnej:

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

Oto pełny kod w 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;