我对the pseudo-encrypt function for postgres有疑问。 有什么方法可以将输出减少到6吗?我真的很喜欢这个功能,想要使用它,但只需要1到999999之间的输出。
此问题与my last question有关。我想用它来创建1到999999之间的unqiue数字。
谢谢。
答案 0 :(得分:0)
对生成的值使用mod
生成从start_value
到end_value
范围内的数字:
select start_value + mod(pseudo_encrypt(number), end_value - start_value + 1);
对于您的情况,这将是:
select 1 + mod(pseudo_encrypt(23452), 999999);
答案 1 :(得分:0)
设置999999的上限并不是很简单,因为算法在位块上运行,因此很难摆脱2的幂。
您可以cycle walking解决此问题 - 只需尝试encrypt(n)
,encrypt(encrypt(n))
,encrypt(encrypt(encrypt(n)))
...直到您最终得到范围[1, 999999]。为了将迭代次数保持在最小值,您需要调整块大小以使您尽可能接近此范围。
此版本将允许您指定输入/输出的范围:
CREATE OR REPLACE FUNCTION pseudo_encrypt(
value INT8,
min INT8 DEFAULT 0,
max INT8 DEFAULT (2^62::NUMERIC)-1
) RETURNS INT8 AS
$$
DECLARE
rounds CONSTANT INT = 3;
L INT8[];
R INT8[];
i INT;
blocksize INT;
blockmask INT8;
result INT8;
BEGIN
max = max - min;
value = value - min;
IF NOT ((value BETWEEN 0 AND max) AND (max BETWEEN 0 AND 2^62::NUMERIC-1)) THEN
RAISE 'Input out of range';
END IF;
blocksize = ceil(char_length(ltrim(max::BIT(64)::TEXT,'0'))/2.0);
blockmask = (2^blocksize::NUMERIC-1)::INT8;
result = value;
LOOP
L[1] = (result >> blocksize) & blockmask;
R[1] = result & blockmask;
FOR i IN 1..rounds LOOP
L[i+1] = R[i];
R[i+1] = L[i] # ((941083981*R[i] + 768614336404564651) & blockmask);
END LOOP;
result = (L[rounds]::INT8 << blocksize) | R[rounds];
IF result <= max THEN
RETURN result + min;
END IF;
END LOOP;
END;
$$
LANGUAGE plpgsql STRICT IMMUTABLE;
我无法保证其正确性,但您可以轻松地将其[1,999999]映射回[1,999999]:
SELECT i FROM generate_series(1,999999) s(i)
EXCEPT
SELECT pseudo_encrypt(i,1,999999) FROM generate_series(1,999999) s(i)