减少伪加密函数输出的长度

时间:2015-04-07 13:40:05

标签: postgresql

我对the pseudo-encrypt function for postgres有疑问。 有什么方法可以将输出减少到6吗?我真的很喜欢这个功能,想要使用它,但只需要1到999999之间的输出。

此问题与my last question有关。我想用它来创建1到999999之间的unqiue数字。

谢谢。

2 个答案:

答案 0 :(得分:0)

对生成的值使用mod生成从start_valueend_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)