需要bigint的plpgsql中的pseudo_encrypt()函数

时间:2012-10-06 16:04:06

标签: sql postgresql plpgsql

我正在开发一个生成随机ID的系统,就像回答#2 here一样。

我的问题是,提到的pseudo_encrypt()函数适用于int而不是bigint。我试图重写它,但它总是返回相同的结果:

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;

有人可以检查一下吗?

2 个答案:

答案 0 :(得分:17)

必须使用

4294967295作为位掩码来选择32位(而不是4294967296)。 这就是为什么你现在为不同的输入获得相同值的原因。

我还建议将bigint用于l2r2的类型,它们与[{1}}和r1不应该有所不同

并且,为了更好的随机性,在PRNG函数中使用更高的乘数来获得真正占用32位的中间块,如32767 * 32767而不是32767。

完整的修改版本:

l1

第一个结果:

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)

答案 1 :(得分:7)

旧但是仍然是一个有趣的问题。与Daniels回答相比,我使用的是稍微修改过的版本,将return语句更改为此(交换r1和l1),如文章Pseudo encrypt末尾所述:

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

这种变化的原因是底层Feistel algorithm不应该在最后一轮结束时左右交换。通过此更改,函数重新获得充当其自身反函数的能力:

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

以下是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;