这是一个在 plpgsql 中已经实现的没有冲突的好方法。
第一步:考虑 PG wiki 中的pseudo_encrypt函数。这个函数接受一个 32 位整数作为参数,并返回一个 32 位整数,在人眼看来它是随机的,但唯一地对应于它的参数(所以这是加密,而不是散列)。在函数内部,您可以更改公式:使用只有您知道(((1366.0 * r1 + 150889) % 714025) / 714025.0)
的另一个函数,它会产生 [0..1] 范围内的结果(只需调整常量可能就足够了,请参阅下面我的尝试) . 有关更多理论解释,请参阅有关Feistel 密码的维基百科文章。
第二步:在您选择的字母表中编码输出数字。这是一个以所有字母数字字符为基数的 62 位函数。
CREATE OR REPLACE FUNCTION stringify_bigint(n bigint) RETURNS text
LANGUAGE plpgsql IMMUTABLE STRICT AS $$
DECLARE
alphabet text:='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
base int:=length(alphabet);
_n bigint:=abs(n);
output text:='';
BEGIN
LOOP
output := output || substr(alphabet, 1+(_n%base)::int, 1);
_n := _n / base;
EXIT WHEN _n=0;
END LOOP;
RETURN output;
END $$
现在,我们将得到与单调序列相对应的前 10 个 URL:
select stringify_bigint(pseudo_encrypt(i)) from generate_series(1,10) as i;
stringify_bigint
------------------
tWJbwb
eDUHNb
0k3W4b
w9dtmc
woCi
2hVQz
PyOoR
cjzW8
比戈克
A5tDHb
结果看起来是随机的,并且保证在整个输出空间中是唯一的(如果您也将整个输入空间与负整数一起使用,则结果是 2^32 或大约 40 亿个值)。如果 40 亿个值不够宽,您可以小心地组合两个 32 位结果以达到 64 位,同时不会失去输出的唯一性。棘手的部分是正确处理符号位并避免溢出。
关于修改函数以生成自己独特的结果:让我们将函数体中的常量从 1366.0 更改为 1367.0,然后重试上面的测试。看看结果是如何完全不同的:
NprBxb
SY38Ob
urrF6b
OjKVnc
vdS7j
uEfEB
3zuaT
0fjsab
j7OYrb
PYiwJb
更新:对于那些可以编译 C 扩展的人来说,一个很好的替代pseudo_encrypt()
是range_encrypt_element()
from permuteseq extension
,它具有以下优点: