我想在StackOverflow上使用几次提到的pseudo_encrypt函数来使我的ID看起来更随机:https://wiki.postgresql.org/wiki/Pseudo_encrypt
如何自定义此选项以便为我输出唯一的“随机”数字。我在某处读到你可以改变1366.0常数,但我不想冒任何ID冒险,因为任何潜在的ID重复会导致重大问题。
我真的不知道每个常数实际上做了什么,所以除非我得到一些方向,否则我不想乱用它。有谁知道我可以安全地改变哪些常数?
这是:
CREATE OR REPLACE FUNCTION "pseudo_encrypt"("VALUE" int) RETURNS int IMMUTABLE STRICT AS $function_pseudo_encrypt$
DECLARE
l1 int;
l2 int;
r1 int;
r2 int;
i int:=0;
BEGIN
l1:= ("VALUE" >> 16) & 65535;
r1:= "VALUE" & 65535;
WHILE i < 3 LOOP
l2 := r1;
r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
r1 := l2;
l1 := r2;
i := i + 1;
END LOOP;
RETURN ((l1::int << 16) + r1);
END;
$function_pseudo_encrypt$ LANGUAGE plpgsql;
对于bigint的
CREATE OR REPLACE FUNCTION "pseudo_encrypt"("VALUE" bigint) RETURNS bigint IMMUTABLE STRICT AS $function_pseudo_encrypt$
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)::bigint;
r1 := l2;
l1 := r2;
i := i + 1;
END LOOP;
RETURN ((l1::bigint << 32) + r1);
END;
$function_pseudo_encrypt$ LANGUAGE plpgsql;
答案 0 :(得分:5)
替代解决方案:使用不同的密码
现在可以在postgres wiki上使用其他密码函数。它们会明显变慢,但除此之外,它们更适合生成定制的随机系列独特数字。
对于32位输出,Skip32 in plpgsql将使用10字节宽的密钥加密其输入,因此您只需选择自己的密钥即可拥有自己的特定排列(2 ^ 32的特定顺序)将出现独特的价值观。)
对于64位输出,XTEA in plpgsql的操作类似,但使用16字节宽的键。
否则,只需自定义pseudo_encrypt
,请参阅以下内容:
关于pseudo_encrypt
实施的说明:
此功能有3个属性
第一个和第二个属性来自Feistel网络,正如在@ CodesInChaos的答案中已经解释的那样,它们不依赖于这些常量的选择:1366
以及150889
和{{ 1}}。
确保在更改714025
时,它在数学意义上保持功能,即f(r1)
隐含x=y
,或者换句话说,相同的输入必须始终产生相同的输出。打破这一点将打破这种独特性。
这些常量和f(x)=f(y)
的这个公式的目的是产生一个相当好的伪随机效应。使用postgres内置f(r1)
或类似的方法是不可能的,因为它不是如上所述的数学函数。
为什么这些任意常数?在这部分功能中:
random()
公式和值r2 := l1 # ((((1366.0 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
,1366
和150889
来自数字食谱C (1992年,William H.Press,第2版。 ),第7章:随机数,特别是第284和285页。
这本书不能直接在网上索引,但可以通过这里的界面阅读:http://apps.nrbook.com/c/index.html。它也被引用作为实现PRNG的各种源代码的参考。
在本章讨论的算法中,上面使用的算法非常简单且相对有效。从前一个(714025
)获取新随机数的公式为:
jran
其中jran = (jran * ia + ic) % im;
ran = (float) jran / (float) im; /* normalize into the 0..1 range */
是当前的随机整数。
此生成器必须在一定数量的值(“句点”)之后循环自身,因此必须仔细选择常量jran
,ia
和ic
期间要尽可能大。这本书提供了一个表p.285,其中建议了不同长度的常数。
im
,ia=1366
和ic=150889
是其中一段时间的条目之一
2 29 位,这比需要的还多。
最后乘以im=714025
或2 15 -1不是PRNG的一部分,但意味着从0..1伪随机浮点值产生正半整数。除非扩大算法的块大小,否则不要更改该部分。
答案 1 :(得分:3)
此功能看起来像基于Feistel network的阻止程序 - 但它缺少密钥。
Feistel构造是双射的,即它保证没有碰撞。有趣的是:r2 := l1 # f(r1)
。只要f(r1)
仅取决于r1
,pseudo_encrypt
就会是双射的,无论函数是什么。
缺少密钥意味着任何知道源代码的人都可以恢复顺序ID。因此,您依赖安全性而非默默无闻。
另一种方法是使用一个带密钥的分组密码。对于32位块,选择相对较少,我知道Skip32和ipcrypt。对于64位块,有许多密码可供选择,包括3DES,Blowfish和XTEA。