概率哈希 - 有这样的事吗?

时间:2009-06-17 16:06:33

标签: algorithm data-structures

假设您要实现一个点击跟踪器,您只需要在任何IP地址上点击一次链接,但链接和客户端的数量非常大,您不希望保留每个单击IP地址。假设您可能需要将此作为针对每次点击运行的内容的一部分,并且不希望针对每次点击对大表进行查找。

是否存在“概率散列”或“有损哈希”这样的事情,看看IP是否可能在一个集合中但你不关心是否存在某种错误率,因为你想节省资源?

6 个答案:

答案 0 :(得分:18)

你可能(ab?)使用bloom filter来做这样的事情。

答案 1 :(得分:6)

假设IPv4地址,则搜索空间为2 32 。每个IP地址不需要超过1位(0 ==无访问,1 ==访问)。在不考虑存储开销的情况下,这将需要512 MB(2 29 )来存储。因此,简单的实现将分配512 MB阵列(或具有2个 29 行的表,每个存储一个字节,或2个 27 行,每个行存储32位整数,或2 26 行,每行存储一个64位整数,或......)

您可以通过将稀疏种群变为树来优化稀疏种群。

定义“ x 位的”页面“大小。您将一次为一个页面分配存储空间。

按页面大小划分搜索空间(2 32 )。这是表示搜索空间中每个可能地址所需的总页数。

然后,要确定散列中是否存在匹配,您将首先确定该页面是否存在,如果存在,则是否设置了页面中的相应位。要缓存地址,首先要确定页面是否存在;如果没有,你会创建它。接下来,您将设置适当的位。

这很容易模拟到数据库表。您只需要两列:页面索引和二进制数组。分配页面时,只需在表中存储一行,其中包含正确的页面索引和一个空的二进制数组。

例如,对于1024位页面大小(产生2 22 最大页面),您可以像这样构建表:

CREATE TABLE VisitedIPs(
    PageIndex int         NOT NULL PRIMARY KEY,
    PageData  binary(128) NOT NULL
)

要检查IP是否已访问过,您将使用类似于(伪代码)的代码:

uint ip = address.To32Bit();

string sql =
    "SELECT PageData " +
    "FROM VisitedIPs " +
    "WHERE PageIndex = " + (ip >> 10);

byte[] page = (byte[])GetFromDB(sql);

byte b = page[(ip & 0x3FF) >> 3];

bool hasVisited = (b & (1 << (ip & 7)) != 0;

IP访问过的设置类似:

uint ip = address.To32Bit();

string sql =
    "SELECT PageData " +
    "FROM VisitedIPs " +
    "WHERE PageIndex = " + (ip >> 10);

byte[] page = (byte[])GetFromDB(sql);

page[(ip & 0x3FF) >> 3] |= (1 << (ip & 7));

sql =
    "UPDATE VisitedIPs " +
    "SET PageData = @pageData " +
    "WHERE PageIndex = " + (ip >> 10);

ExecSQL(sql, new SqlParam("@pageData", page));

答案 2 :(得分:4)

凭借pigeonhole principle,所有哈希都是有损的。不可避免地,你试图把N个东西塞进M个槽(其中N>&gt; M)。您需要做的就是不处理碰撞情况并选择足够大的哈希表。

答案 3 :(得分:2)

当然! N,决定你可以负担多少“箱子”(每个一位);将IP地址散列为一串B位;取B模数N.您可以计算意外碰撞的概率(使用某些近似值,例如,假设所有散列的IP地址形成同样可能的位串B)并相应地确定N,如果您对可接受的最大意外碰撞概率有约束应用

答案 4 :(得分:1)

开始截断位。

如果在2 ^ n中有2 ^(n / 2)个事物,则哈希冲突的概率变为50%。 IP地址为2 ^ 32,因此当容器中有2 ^ 16个项目时,有50%的机会发生冲突。

感觉舒服时减少。

答案 5 :(得分:0)

没有冲突处理的整数(ipv4)或字符串(ipv6)散列,使用散列值(模数散列表大小)作为位图数组的索引。