所以,我碰巧在這方面做的參數搜索,發現以下14位散列函數:
int h(int x) {
x &= 0x3fff;
x ^= x >> 8;
x *= 0x68ab;
x &= 0x3fff;
x ^= x >> 8;
x *= 0x594b;
x &= 0x3fff;
x ^= x >> 8;
return x;
}
此散列遵循murmur3 mix功能建設。 &=
操作是保持算術在14位溢出域中運行,但除此之外每一步都是雙向注入,因此整體散列是雙向注入。
每次乘法都是奇數的(使它與模2 ** 14共素,從而保證所有結果都是唯一的),並且可以通過在一個位置解除操作一位來反轉移位異或操作時間。
但上面的功能不僅保證映射小於16384一個號碼到另一個號碼小於16384。我們需要把這個限制爲小於10000
如果您在下面的循環包裹它,它會受限於小於10000的值(不用擔心,平均迭代次數可證明爲1。6384所以它是相當安全):
int f(int x) {
do {
x = h(x);
} while (x >= 10000);
return x;
}
由於循環內的散列函數是雙射,只有一個輸入x
可以映射到任何給定的結果。如果結果超出範圍,但x
處於範圍內,則哈希的下一次迭代必須映射到新值。即使它指向一個新的超出範圍的價值鏈最終必須被迫回到範圍內。
一個完全超出範圍的週期可以存在,但它將無法從範圍內的值,因爲其含義是一個鏈接在該週期有兩個值映射到它(一來自外部,來自內部),這不能通過雙射函數來實現。
這意味着輸入到f()
必須已經在範圍內才能從無限循環中安全。
我們使之成爲每個用戶名不同,試試這個:
int f(int x, int username_hash) {
int m = (username_hash * 2 + 1) & 0x3fff;
int c = (username_hash >> 13) & 0x3fff;
do {
x = (x * m + c) & 0x3fff;
x = h(x);
} while (x >= 10000);
return x;
}
同樣,乘以奇數模二的冪是雙射,並且增加模二的冪也是雙射。根據username_hash
,在那裏拋出這兩個額外的操作有效地擴展了散列並擾亂了它的行爲。並且外部循環將結果保持在10000以下。
我不確定您的意思是將用戶總數限制爲10000,還是對每個爭用用戶名分別計數,但如果您傳遞給f()
該數字(保證小於10000)以及用戶名的一些整數哈希值以配置操作,然後您將得到一個數字,與您傳入的數字一樣唯一。
來源
2017-04-04 03:55:37
sh1
以一組可能的ID(0000-9999),刪除已經使用的ID,並隨機選擇一個? – Rogue
你希望通過一個函數而不是僅僅產生一個隨機數來獲得什麼? – biziclop
@biziclop最有可能的獨特性。廣義的[生日問題](https://en.wikipedia.org/wiki/Birthday_problem#The_generalized_birthday_problem)告訴我們,在10000個值的池大小下,我們有50/50的機會來生成重複已經產生了118個值。隨着我們產生更多價值,重複出現的比率迅速增加。 – pjs