【问题标题】:pseudo_encrypt() function in plpgsql that takes bigintplpgsql 中采用 bigint 的 pseudo_encrypt() 函数
【发布时间】:2012-09-27 12:01:12
【问题描述】:

我正在开发一个生成随机 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;

有人可以检查一下吗?

【问题讨论】:

    标签: sql postgresql plpgsql


    【解决方案1】:

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

    我还建议将bigint 用于l2r2 的类型,它们不应该与r1l1 真正不同

    并且,为了获得更好的随机性,在 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 行)

    【讨论】:

    • 啊,位掩码是个愚蠢的错误……非常感谢!现在完美运行!顺便说一句:你想建议 bigint,而不是 int。
    • @Daniel Vérité 如果我需要最大长度为 13 位的 bigint,我必须在函数中修改什么?
    • @MattC:这很重要,您可以将其作为新问题提交。同样使用这种技术,上限将是2^N,其中N 是偶数,而不是10^N
    • @PatrickBoos:是的,但自逆属性不一定是可取的。例如,它排除了自行车步行方法的使用,如en.wikipedia.org/wiki/…中所述
    • @thnee:真正的原始as first postedreturn ((l1::bigint&lt;&lt;16) + r1)。交换 r1 和 l1 使函数成为自反函数,这可能是可取的,也可能不是可取的。请参阅上面的其他 cmets。
    【解决方案2】:

    老但仍然是一个有趣的问题。对比 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;
    

    【讨论】:

    • 非常感谢。这个答案产生积极的 bigints,与接受的答案相反,这对于输入值 >2^32-1 似乎是负数
    猜你喜欢
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多