13

我正在开发一个生成随机 id 的系统,如答案 #2这里

我的问题是,提到的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;

有人可以检查这个吗?

4

2 回答 2

18

4294967295必须用作位掩码以选择 32 位(而不是4294967296)。这就是为什么当前您为不同的输入获得相同值的原因。

我还建议使用andbigint的类型,它们不应该与and真正不同l2r2r1l1

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

完整修改版:

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;

第一个结果:

从 generate_series (1, 10) 中选择 x,pseudo_encrypt(x::bigint) 作为 x;
 x | 伪加密    
-+------------
  1 | 3898573529235304961
  2 | 2034171750778085465
  3 | 169769968641019729
  4 | 2925594765163772086
  5 | 1061193016228543981
  6 | 3808195743949274374
  7 | 1943793931158625313
  8 | 88214277952430814
  9 | 2835217030863818694
 10 | 970815170807835400
(10 行)
于 2012-10-06T16:53:47.727 回答
9

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

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

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

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;
于 2015-04-30T08:01:23.267 回答