64位的各位对应值的哈希表,这是一个为了能够有效单独处理每一位所对应的一个数据。由于一共有64位,二进制各位模64,从第7位开始就全为0了,故首先从65看,而65不冲突的个数仅为12个,66不冲突的个数仅为11个。但计算到67时,会发现,刚好所有二进制单位上的值模67全部无冲突。即有64个不重复的数字。这些数字可以构建一个2的幂的哈希表,使得所有查询,修改,删除全为
O
(
1
)
O(1)
O(1)
因此采用
hash
(
n
)
=
n
mod
67
\text{hash}(n)=n\ \text{mod}\ 67
hash(n)=n mod 67 即可构建无冲突的二的幂的哈希表。对一些进制函数的处理非常有用,二进制在各种组合dfs中总会碰到的,可以考虑这样的hashmap。
或许你以为到这就完了,但实际上,稍微想想,能找到一些数论的关系出来。
原根定义,若
g
i
≡
1
(
mod
n
)
g^i\equiv1\left(\text{mod}\ n\right)
gi≡1(mod n)的最小正整数
i
=
φ
(
n
)
i=\varphi(n)
i=φ(n)称
g
g
g为
n
n
n的原根。
很显然,哈希表的函数要的是2作为哪个大于等于最大长度的数的原根。如这里是找到大于等于64的原根。设
m
m
m为定义域长度,即最高要达到的
a
i
a^i
ai的
i
i
i的最大值为
m
m
m。则
φ
(
n
)
≥
m
\varphi(n)\ge m
φ(n)≥m 且
n
n
n是
a
a
a的原根。
最好的结果是直接考虑 n n n为素数的情况, n n n是素数下。 φ ( n ) = n − 1 ≥ m \varphi(n)=n-1\ge m φ(n)=n−1≥m,需从 m + 1 m+1 m+1开始考虑。考虑大于 m m m的素数,检测其是否具备 2 2 2作为其原根的特性。不过光这个也都无法有固定公式,因为2是否是无穷多个素数的原根的问题本来就未被证明的,它来自于埃米尔阿廷猜想。当然不会有固定的公式。但是就一般的ACM题或者工程需要,不超过64的范围内都没有什么特别的需要处理的。
可以用Mathematica来查询:
SelectFirst[Select[Prime[Range[k]], # > m &], PrimitiveRoot[#] == 2 &]
表示大于m的在前k个素数中找到原根为2的n。这个数就是构建哈希表模的数。输入k=100和m=64可得67恰为答案。
对于m=128来说,k取100可得131为哈希表长。
m | k | 值 |
---|---|---|
32 | 1000 | 37 |
64 | 1000 | 67 |
128 | 1000 | 131 |
256 | 1000 | 269 |
512 | 1000 | 523 |
1024 | 1000 | 1061 |
还有一种方法查询,但是是大概率不会查到最小一个满足的:
FindInstance[Prime[n] > m && PrimitiveRoot[Prime[n]] == 2, n, Integers, k]
其中获得的是第n个素数的n,需要自行选取内部的n。当m特别大时采用。
这里讨论的东西可以完全扩展到其他底数的单幂值的哈希表,不只是2还可以有3,5等等。