- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我遇到了以下问题:
简单的方法是为每个单独的 ID 计算哈希函数,然后将所有内容异或在一起。但是,如果您有一个用于 ID 的 32 位空间和一个用于散列函数的 64 位空间,那么这可能不是解决此问题的最佳方法(冲突等...)。
我一直在考虑使用 Murmur3 终结器,然后将结果一起进行异或运算,但我想出于同样的原因,这也行不通(老实说,我不确定)。同样,简单地将值相乘也应该可行(因为 ab = ba),但我不确定该哈希函数的“好”程度。
很明显,我想到了对 ID 进行排序,之后 Murmur3 会做得很好。不过,如果可以避免,我不想排序。
这样的哈希函数有什么好的算法?
更新
好吧,我想我可能有点困惑。
关于 Why is XOR the default way to combine hashes? 的第二个答案实际上解释了组合哈希函数。在那里介绍的案例中,XOR 被认为是一个糟糕的散列函数,因为“dab”产生与“abd”相同的代码。在我的例子中,我希望这些东西生成相同的散列值——但我也想尽量减少-say-“abc”也生成与-say-“abd”相同的散列值的机会.
大多数哈希函数的全部目的是,如果您向它们提供数据,它们很可能会使用完整的 key 空间。一般来说,这些散列函数利用数据是顺序这一事实,并乘以一个大数字来随机排列位。所以简单来说:
var hash = SomeInitialConstant;
foreach (var id in ids) {
hash = hash * SomeConstant + hashCode(id);
}
// ... optionally shuffle bits around as finalizer
return hash;
现在,如果 ID 的顺序始终相同,则此方法可以正常工作。但是,如果 ID 是无序的,这将不起作用,因为 x * constant + y
不可交换。
如果您对 ID 求平方,我认为您最终不会使用整个哈希空间。考虑如果你有大数字会发生什么,比如 100000、100001 等。这些正方形是 10000000000、10000200001 等。你不可能得到一个正方形来生成像 900000 这样的数字(仅仅是因为 sqrt(900000)是一个带分数的数字)。
更一般地说,10000000000 和 10000200001 之间的所有哈希空间很可能会丢失。但是,0 和 10 之间的空间会发生很多冲突,因为小数字的平方之间的可用散列空间也很小。
使用大 key 空间的全部目的显然是减少冲突。我想要一个相当大的散列空间(比如 256 位)以确保在现实生活场景中几乎不存在冲突。
最佳答案
我刚刚检查过:
#include <stdio.h>
#include <stdlib.h>
struct list {
struct list *next;
unsigned hash;
unsigned short cnt;
unsigned char *data;
};
struct list *hashtab[1<<16] = {NULL, };
#define COUNTOF(a) (sizeof a / sizeof a[0])
unsigned zobrist[256] = {0,};
/*************************/
unsigned hash_it(unsigned char *cp, unsigned cnt)
{
unsigned idx;
unsigned long long hash = 0;
for(idx=0; idx < cnt; idx++) {
#if 0 /* cube */
hash += (cp[idx] * cp[idx] * cp[idx]);
#else
unsigned val;
val = zobrist[cp[idx]];
hash += (val * val);
#endif
}
#if 0 /* as a tie-breaker: add the count (this avoids pythagorean triplets but *not* taxi-numbers) */
hash += cnt;
#endif
return hash;
}
/*************************/
struct list *list_new(unsigned cnt){
struct list *p;
unsigned idx;
p = malloc( sizeof *p + cnt);
p->data = (unsigned char*)(p+1);
p->cnt = cnt;
p->next = NULL;
for(idx=0; idx < cnt; idx++) {
p->data[idx] = 0xff & rand();
}
p->hash = hash_it(p->data, p->cnt);
return p;
}
/*************************/
void do_insert(struct list *this)
{
struct list **pp;
unsigned slot;
slot = this->hash % COUNTOF(hashtab);
for (pp = &hashtab[slot]; *pp; pp = &(*pp)->next) {;}
*pp = this;
}
/*************************/
void list_print(struct list *this)
{
unsigned idx;
if (!this) return;
printf("%lx data[%u] = ", (unsigned long) this->hash, this->cnt);
for (idx=0; idx < this->cnt; idx++) {
printf("%c%u"
, idx ? ',' : '{' , (unsigned int) this->data[idx] );
}
printf("}\n" );
}
/*************************/
unsigned list_cnt(struct list *this)
{
unsigned cnt;
for(cnt=0; this; this=this->next) { cnt++; }
return cnt;
}
/*************************/
unsigned list_cnt_collisions(struct list *this)
{
unsigned cnt;
for(cnt=0; this; this=this->next) {
struct list *that;
for(that=this->next; that; that=that->next) {
if (that->cnt != this->cnt) continue;
if (that->hash == this->hash) cnt++;
}
}
return cnt;
}
/*************************/
int main(void)
{
unsigned idx, val;
struct list *p;
unsigned hist[300] = {0,};
/* NOTE: you need a better_than_default random generator
** , the zobrist array should **not** contain any duplicates
*/
for (idx = 0; idx < COUNTOF(zobrist); idx++) {
do { val = random(); } while(!val);
zobrist[idx] = val;
}
/* a second pass will increase the randomness ... just a bit ... */
for (idx = 0; idx < COUNTOF(zobrist); idx++) {
do { val = random(); } while(!val);
zobrist[idx] ^= val;
}
/* load-factor = 100 % */
for (idx = 0; idx < COUNTOF(hashtab); idx++) {
do {
val = random();
val %= 0x40;
} while(val < 4); /* array size 4..63 */
p = list_new(val);
do_insert(p);
}
for (idx = 0; idx < COUNTOF(hashtab); idx++) {
val = list_cnt( hashtab[idx]);
hist[val] += 1;
val = list_cnt_collisions(hashtab[idx]);
if (!val) continue;
printf("[%u] : %u\n", idx, val);
for (val=0,p = hashtab[idx]; p; p= p->next) {
printf("[%u]: ", val++);
list_print(p);
}
}
for (idx = 0; idx < COUNTOF(hist); idx++) {
if (!hist[idx]) continue;
printf("[%u] = %u\n", idx, hist[idx]);
}
return 0;
}
/*************************/
输出直方图(链长,0 := 空槽):
$ ./a.out
[0] = 24192
[1] = 23972
[2] = 12043
[3] = 4107
[4] = 1001
[5] = 181
[6] = 34
[7] = 4
[8] = 2
最后说明:除了 Zobrist[] 的平方和之外,您还可以将它们异或在一起(假设条目是唯一的)
额外的最后说明:C 标准库 rand()
功能可能无法使用。 RAND_MAX 只能是 15 位:0x7fff (32767)。要填充 zobrist 表,您需要更多值。这可以通过异或一些额外的 (rand() << shift)
来完成进入更高位。
新结果,使用(来自)一个非常大的源域(32 个元素 * 8 位),将其散列为 32 位散列键,插入到 1<<20
的散列表中插槽。
Number of elements 1048576 number of slots 1048576
Element size = 8bits, Min setsize=0, max set size=32
(using Cubes, plus adding size) Histogram of chain lengths:
[0] = 386124 (0.36824)
[1] = 385263 (0.36742)
[2] = 192884 (0.18395)
[3] = 64340 (0.06136)
[4] = 16058 (0.01531)
[5] = 3245 (0.00309)
[6] = 575 (0.00055)
[7] = 78 (0.00007)
[8] = 9 (0.00001)
这非常接近最优;对于 100% 加载的哈希表,直方图中的前两个条目应该相等,在完美的情况下,都是 1/e。前两个条目是空槽和只有一个元素的槽。
关于algorithm - 用于无序 ID 集的良好哈希函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41187876/
我在为 MacOSX 构建的独立包中添加 DMG 背景的自定义图标时遇到问题。我在项目的根目录中添加了一个包。正在从中加载自定义图标,但没有加载 DMG 背景图标。我正在使用 Java fx 2.2.
Qt for Symbian 和 Qt for MeeGo 有什么区别?我知道 Qt 是一个交叉编译平台。这是否意味着如果我使用来自 Qt 的库,完全相同的库可以在所有支持 Qt 的设备(例如 Sym
我正在尝试使用 C# .NET 3.5/4.0 务实地运行 SQL Server 数据库的备份。我已经找到了如何完成此操作,但是我似乎找不到用于备份的命名空间库。 我正在寻找 Microsoft.Sq
我最近在疯狂学习 Java,但我通常是一名 .NET 开发人员。 (所以请原谅我的新手问题。) 在 .Net 中,我可以在不使用 IIS 的情况下开发 ASP.Net 页面,因为它有一个简化的 Web
这post仅当打印命令中有字符串时才有用。现在我有大量的源代码,其中包含一条声明,例如 print milk,butter 应该格式化为 print(milk,butter) 用\n 捕获行尾并不成功
所以我的问题是: https://gist.github.com/panSarin/4a221a0923927115584a 当我保存这个表格时,我收到了标题中的错误 NoMethodError (u
如何让 Html5 音频在点击时播放声音? (ogg 用于 Firefox 等浏览器,mp3 用于 chrome 等浏览器) 到目前为止,我可以通过 onclick 更改为单个文件类型,但我无法像在普
如果it1和it2有什么区别? std::set s; auto it1 = std::inserter(s, s.begin()); auto it2 = std::inserter(s, s.en
4.0.0 com.amkit myapp SpringMVCFirst
我目前使用 Eclipse 作为其他语言的 IDE,而且我习惯于不必离开 IDE 做任何事情 - 但是我真的很难为纯 ECMAScript-262 找到相同或类似的设置。 澄清一下,我不是在寻找 DO
我想将带有字符串数组的C# 结构发送到C++ 函数,该函数接受void * 作为c# 结构和char** 作为c# 结构字符串数组成员。 我能够将结构发送到 c++ 函数,但问题是,无法从 c++ 函
我正在使用动态创建的链接: 我想为f:param附加自定义转换器,以从#{name}等中删除空格。 但是f:param中没有转换器
是否可以利用Redis为.NET创建后写或直写式缓存?理想情况下,透明的高速缓存是由单个进程写入的,并且支持从数据库加载丢失的数据,并每隔一段时间持久保存脏块? 我已经搜查了好几个小时,也许是goog
我正在通过bash执行命令的ssh脚本。 FILENAMES=( "export_production_20200604.tgz" "export_production_log_2020060
我需要一个正则表达式来出现 0 到 7 个字母或 0 到 7 个数字。 例如:匹配:1234、asdbs 不匹配:123456789、absbsafsfsf、asf12 我尝试了([a-zA-Z]{0
我有一个用于会计期间的表格,该表格具有期间结束和开始的开始日期和结束日期。我使用此表来确定何时发生服务交易以及何时在查询中收集收入,例如... SELECT p.PeriodID, p.FiscalY
我很难为只接受字符或数字的 Laravel 构建正则表达式验证。它是这样的: 你好<-好的 123 <- 好的 你好123 <-不行 我现在的正则表达式是这样的:[A-Za-z]|[0-9]。 reg
您实际上会在 Repeater 上使用 OnItemDataBound 做什么? 最佳答案 “此事件为您提供在客户端显示数据项之前访问数据项的最后机会。引发此事件后,数据项将被清空,不再可用。” ~
我有一个 fragment 工作正常的项目,我正在使用 jeremyfeinstein 的 actionbarsherlock 和滑动菜单, 一切正常,但是当我想自定义左侧抽屉列表单元格时,出现异常
最近几天,我似乎平均分配时间在构建我的第一个应用程序和在这里发布问题!! 这是我的第一个应用程序,也是我们的设计师完成的第一个应用程序。我试图满足他所做的事情的外观和感觉,但我认为他没有做适当的事情。
我是一名优秀的程序员,十分优秀!