2015-04-07 42 views

回答

0

所產生的價值使用mod在範圍內產生數start_valueend_value

select start_value + mod(pseudo_encrypt(number), end_value - start_value + 1); 

對於你的情況,這將是這樣的:

select 1 + mod(pseudo_encrypt(23452), 999999); 
+0

不錯的答案,但是當我使用這個選擇我再次有獨特的問題。它很難用999999條目測試它,但是當我嘗試使用例如99.我再次遇到unqiue驗證問題。 – Pauli

0

這不是很簡單的設置一個999999的上限,因爲該算法對比特塊進行操作,所以很難擺脫兩者的冪。

您可以解決此通過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)