Java:最简单的整数散列
我需要一个用于整数的快速哈希函数:Java:最简单的整数散列,java,hash,Java,Hash,我需要一个用于整数的快速哈希函数: int hash(int n) { return ...; } Java中已经存在一些东西了吗 我需要的最小属性是: hash(n)&1与一组n的连续值一起使用时,不会出现周期性 散列(n)&1大约同样可能为0或1 HashMap,以及Guava的基于散列的,在hashCode()结果上使用以下方法来改善位分布并抵御较弱的散列函数: /* * This method was written by Doug Lea with assistance
int hash(int n) { return ...; }
Java中已经存在一些东西了吗
我需要的最小属性是:
hash(n)&1与一组n的连续值一起使用时,不会出现周期性
大约同样可能为0或1散列(n)&1
HashMap
,以及Guava的基于散列的,在hashCode()
结果上使用以下方法来改善位分布并抵御较弱的散列函数:
/*
* This method was written by Doug Lea with assistance from members of JCP
* JSR-166 Expert Group and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
*
* As of 2010/06/11, this method is identical to the (package private) hash
* method in OpenJDK 7's java.util.HashMap class.
*/
static int smear(int hashCode) {
hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4);
}
[所以决定将我的“琐碎的回答”转换为评论。尝试在其中添加一些文字,看看它是否可以被愚弄]
除非你需要更宽的散列函数范围
NumberOfSetBits函数的变化似乎比hashCode大得多,因此似乎更适合您的需要。事实证明已经有了一个相当有效的算法
看。所以,我读了这个问题,觉得这是一个非常数学化的问题,我可能无法胜任。然后,我花了太多时间思考它,以至于我相信我已经得到了答案:没有函数能够满足f(n)&1对于n的连续值是非周期的 希望有人会告诉我我的推理是多么荒谬,但在那之前我相信它是正确的 这样:任何二进制整数n都可以表示为1…0或1…1,并且只有该位图的最低有效位会影响n&1的结果。此外,下一个连续整数n+1将始终包含相反的最低有效位。因此,很明显,当传递给函数n&1时,任何连续整数序列都将显示一个2的周期。那么,有没有函数f(n)能够充分地分配连续整数序列,从而消除周期性 任何函数f(n)=n+c都会失败,因为c必须以0或1结尾,因此LSB将根据所选常数翻转或保持不变 上面还消除了所有琐碎情况下的减法运算,但我还没有花时间分析进位行为,因此这里可能有一个漏洞 任何函数f(n)=c*n都会失败,因为如果c以0结尾,则LSB始终为0,如果c以1结尾,则LSB始终等于n的LSB 通过类似的推理,任何函数f(n)=n^c都会失败。幂函数的LSB总是与n相同 出于同样的原因,任何函数f(n)=c^n都会失败 除法和模对我来说没有那么直观,但基本上,两种方法的LSB都是通过减法来确定的(已经排除了)。显然,模数的周期也等于除数 不幸的是,我没有必要证明这一点,但我相信上述操作的任何组合最终也会失败。这让我相信我们可以排除任何超越函数,因为它们是用多项式实现的(泰勒级数?不是术语) 最后,在回家的火车上,我希望数数零碎的东西会有用;然而,这实际上也是一个周期函数。我的想法是,想象一下,取任意十进制数的数字之和。这个总和显然会从0到9,然后下降到1,从1到10,然后下降到2。。。它有一个周期,我们数得越多,范围就越大。实际上,我们可以对二进制数字的和做同样的事情,在这种情况下,我们得到如下结果:0,1,1,2,2,…5,5,6,6,7,7,8,8 我遗漏了什么吗
TL;博士,我认为你的问题没有答案。我做了一些实验(见下面的测试程序);伽罗瓦场中2^n的计算和floor(A*sin(n))都很好地产生了“随机”位序列。我尝试了乘法同余随机数生成器和一些代数和CRC(类似于伽罗瓦域中的k*n),但没有一个做得很好 地板(A*sin(n))方法是最简单和最快速的;GF32中的2^n计算大约需要64次乘法和1024次异或,但在线性反馈移位寄存器的上下文中,输出位的周期性是非常清楚的
package com.example.math;
public class QuickHash {
interface Hasher
{
public int hash(int n);
}
static class MultiplicativeHasher1 implements Hasher
{
/* multiplicative random number generator
* from L'Ecuyer is x[n+1] = 1223106847 x[n] mod (2^32-5)
* http://dimsboiv.uqac.ca/Cours/C2012/8INF802_Hiv12/ref/paper/RNG/TableLecuyer.pdf
*/
final static long a = 1223106847L;
final static long m = (1L << 32)-5;
/*
* iterative step towards computing mod m
* (j*(2^32)+k) mod (2^32-5)
* = (j*(2^32-5)+j*5+k) mod (2^32-5)
* = (j*5+k) mod (2^32-5)
* repeat twice to get a number between 0 and 2^31+24
*/
private long quickmod(long x)
{
long j = x >>> 32;
long k = x & 0xffffffffL;
return j*5+k;
}
// treat n as unsigned before computation
@Override public int hash(int n) {
long h = a*(n&0xffffffffL);
long h2 = quickmod(quickmod(h));
return (int) (h2 >= m ? (h2-m) : h2);
}
@Override public String toString() { return getClass().getSimpleName(); }
}
/**
* computes (2^n) mod P where P is the polynomial in GF2
* with coefficients 2^(k+1) represented by the bits k=31:0 in "poly";
* coefficient 2^0 is always 1
*/
static class GF32Hasher implements Hasher
{
static final public GF32Hasher CRC32 = new GF32Hasher(0x82608EDB, 32);
final private int poly;
final private int ofs;
public GF32Hasher(int poly, int ofs) {
this.ofs = ofs;
this.poly = poly;
}
static private long uint(int x) { return x&0xffffffffL; }
// modulo GF2 via repeated subtraction
int mod(long n) {
long rem = n;
long q = uint(this.poly);
q = (q << 32) | (1L << 31);
long bitmask = 1L << 63;
for (int i = 0; i < 32; ++i, bitmask >>>= 1, q >>>= 1)
{
if ((rem & bitmask) != 0)
rem ^= q;
}
return (int) rem;
}
int mul(int x, int y)
{
return mod(uint(x)*uint(y));
}
int pow2(int n) {
// compute 2^n mod P using repeated squaring
int y = 1;
int x = 2;
while (n > 0)
{
if ((n&1) != 0)
y = mul(y,x);
x = mul(x,x);
n = n >>> 1;
}
return y;
}
@Override public int hash(int n) {
return pow2(n+this.ofs);
}
@Override public String toString() {
return String.format("GF32[%08x, ofs=%d]", this.poly, this.ofs);
}
}
static class QuickHasher implements Hasher
{
@Override public int hash(int n) {
return (int) ((131111L*n)^n^(1973*n)%7919);
}
@Override public String toString() { return getClass().getSimpleName(); }
}
// adapted from http://www.w3.org/TR/PNG-CRCAppendix.html
static class CRC32TableHasher implements Hasher
{
final private int table[];
static final private int polyval = 0xedb88320;
public CRC32TableHasher()
{
this.table = make_table();
}
/* Make the table for a fast CRC. */
static public int[] make_table()
{
int[] table = new int[256];
int c;
int n, k;
for (n = 0; n < 256; n++) {
c = n;
for (k = 0; k < 8; k++) {
if ((c & 1) != 0)
c = polyval ^ (c >>> 1);
else
c = c >>> 1;
}
table[n] = (int) c;
}
return table;
}
public int iterate(int state, int i)
{
return this.table[(state ^ i) & 0xff] ^ (state >>> 8);
}
@Override public int hash(int n) {
int h = -1;
h = iterate(h, n >>> 24);
h = iterate(h, n >>> 16);
h = iterate(h, n >>> 8);
h = iterate(h, n);
return h ^ -1;
}
@Override public String toString() { return getClass().getSimpleName(); }
}
static class TrigHasher implements Hasher
{
@Override public String toString() { return getClass().getSimpleName(); }
@Override public int hash(int n) {
double s = Math.sin(n);
return (int) Math.floor((1<<31)*s);
}
}
private static void test(Hasher hasher) {
System.out.println(hasher+":");
for (int i = 0; i < 64; ++i)
{
int h = hasher.hash(i);
System.out.println(String.format("%08x -> %08x %%2 = %d",
i,h,(h&1)));
}
for (int i = 0; i < 256; ++i)
{
System.out.print(hasher.hash(i) & 1);
}
System.out.println();
analyzeBits(hasher);
}
private static void analyzeBits(Hasher hasher) {
final int N = 65536;
final int maxrunlength=32;
int[][] runs = {new int[maxrunlength], new int[maxrunlength]};
int[] count = new int[2];
int prev = -1;
System.out.println("Run length test of "+N+" bits");
for (int i = 0; i < maxrunlength; ++i)
{
runs[0][i] = 0;
runs[1][i] = 0;
}
int runlength_minus1 = 0;
for (int i = 0; i < N; ++i)
{
int b = hasher.hash(i) & 0x1;
count[b]++;
if (b == prev)
++runlength_minus1;
else if (i > 0)
{
++runs[prev][runlength_minus1];
runlength_minus1 = 0;
}
prev = b;
}
++runs[prev][runlength_minus1];
System.out.println(String.format("%d zeros, %d ones", count[0], count[1]));
for (int i = 0; i < maxrunlength; ++i)
{
System.out.println(String.format("%d runs of %d zeros, %d runs of %d ones", runs[0][i], i+1, runs[1][i], i+1));
}
}
public static void main(String[] args) {
Hasher[] hashers = {
new MultiplicativeHasher1(),
GF32Hasher.CRC32,
new QuickHasher(),
new CRC32TableHasher(),
new TrigHasher()
};
for (Hasher hasher : hashers)
{
test(hasher);
}
}
}
package com.example.math;
公共类快速哈希{
接口哈希器
{
公共整数散列(intn);
}
静态类乘法hasher1实现哈希器
{
/*乘法随机数发生器
*从L'Ecuyer是x[n+1]=1223106847 x[n]模(2^32-5)
* http://dimsboiv.uqac.ca/Cours/C2012/8INF802_Hiv12/ref/paper/RNG/TableLecuyer.pdf
*/
最终静长a=1223106847L;
最终静态长m=(1L>>32;
长k=x&0xffffffffffl;
返回j*5+k;
}
//在计算前将n视为无符号
@重写公共整数散列(整数n){
长h=a*(n&0xFFFFFFFFFFL);
长h2=quickmod(quickmod(h));
返回(int)(h2>=m?(h2-m):h2);
}
@重写公共字符串toString(){return getClass().getSimpleName();}
}
/**
*计算(2^n)mod P,其中P是GF2中的多项式
*系数为2^(k+1),由“poly”中的位k=31:0表示;
*系数2^0始终为1
*/
静态类GF32Hasher实现Hasher
{
静态最终公共GF32Hasher CRC32=新GF32Hasher(0x82608EDB,32);
最终私有int poly;
最终的私有内部ofs;
公共GF32Hasher(整数多边形,整数ofs){
这。ofs=ofs;
this.poly=poly;
}
静态私有长uint(int x){return x&0xffffffffffl;}
//通过重复减法模GF2
整数模(长n){
长rem=n;
长q=uint(this.poly);
q=(q=1,q>>>=1)
{