如何在SQLite中实现FNV-1(a)?

问题描述 投票:0回答:1

(从https://softwareengineering.stackexchange.com/questions/406813/how-to-implement-fnv-1a-in-sqlite移动]

我正在尝试修改SQLite查询(在Android中)以伪随机顺序返回其结果。与this question中一样,顺序必须在重复查询时保持稳定(例如,由于页面调度,屏幕旋转等),因此我不能只使用ORDER BY RANDOM()。相反,我想使用一个哈希函数,该函数依赖于提供稳定性和足够唯一性的几个输入值。 (这些值之一是表的唯一ID列,它是一组非常接近的整数;另一个值更像是会话ID,也是一个整数,在此查询中保持不变。)

根据this well-researched answer,FNV-1和FNV-1a是简单的哈希函数,冲突少且分布良好。但是,尽管非常简单,但FNV-1和FNV-1a都涉及XOR操作,以及循环输入字节。

在查询的每一行中循环非常尴尬。可以通过展开循环来伪造它,特别是如果只涉及几个字节的话。我可以处理两个字节,将来自两个输入值(val1 & 255val2 & 255)的LSB组合在一起。

XOR在SQLite中不直接支持。我了解A ^ B可以实现为(A | B) - (A & B)。但是值的重复,再加上循环的展开,开始变得笨拙。我可以只使用+(忽略溢出)而不是XOR吗?我不需要非常高质量的随机性。对于偶然的观察者,该顺序只需要在小整数范围内随机观察即可。

所以我想知道是否有人已经实现了这样的事情。考虑到widely used this hash function is的方式,似乎很可能已经有这种情况的实现。

这是我实施FNV-1a的尝试:

SELECT ..... ORDER BY (((fnvbasis + val1 & 255) * fnvprime) + val2 & 255) * fnvprime % range;

我忽略了这样一个事实,在FNV中,XOR操作(我已经用+代替)只应该影响哈希值的最低8位。我也忽略了任何溢出(我希望这只是意味着我不在乎的高位丢失了)。

对于fnvbasis,我将使用16777619,对于fnvprime,我将使用2166136261。这些是32位输入的指定值,因为我看不到16位输入的指定值。对于range,我将使用一个质数,该质数大于此查询返回的预期行数。

那么,这是在SQLite查询中近似FNV-1a的合理方法吗?是否有更好的现有实施方案?即尽管我破坏了真正的FNV-1a的操作,但它实际上是否会产生对休闲用户来说看起来非常随机的排序?

android sqlite hash bitwise-operators
1个回答
0
投票

[受到rwong和GrandmasterB对the previous attempt at this question before I moved it的评论的启发,我决定可以预先计算FNV-1a循环的第一个迭代,即基于表的唯一ID列的哈希。预先计算的列fnv1a_step1可以设置为

(fnvbasis + ID & 255) * fnvprime

甚至

(fnvbasis + RANDOM() & 255) * fnvprime

此值永远不会改变。可以使用当前会话ID在查询的ORDER BY子句中非常简单地计算FNV-1a循环的第二次迭代,因此它会为每个会话产生不同但稳定的排序:

ORDER BY (fnv1a_step11 + sessionId & 255) * fnvprime % range;
© www.soinside.com 2019 - 2024. All rights reserved.