【发布时间】:2020-03-23 15:02:04
【问题描述】:
(从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 & 255 和 val2 & 255)的 LSB。
SQLite 不直接支持 XOR。我了解A ^ B 可以实现为(A | B) - (A & B)。但是值的重复,加上循环的展开,开始变得笨拙。我可以只使用+(忽略溢出)而不是异或吗?我不需要非常高质量的随机性。该顺序只需要在小整数范围内让不经意的观察者看起来是随机的。
所以我想知道是否有人已经实施了这样的事情。鉴于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 的合理方法吗?有更好的现有实现吗? IE。尽管我破坏了真正的 FNV-1a 的操作,它实际上会产生一个对于普通用户来说看起来非常随机的顺序吗?
【问题讨论】:
-
Android sqlite 绑定是否支持添加用户定义的函数?
-
显然不是 (stackoverflow.com/a/8283265/423105)。 “为了让它工作,您需要使用 NDK编译 SQLite C 库。”对于我要解决的问题,这在开发过程的复杂性上迈出了一大步。
标签: android sqlite hash bitwise-operators