如何在 SQLite 中实现 FNV-1(a)?
How to implement FNV-1(a) in SQLite?
(从 https://softwareengineering.stackexchange.com/questions/406813/how-to-implement-fnv-1a-in-sqlite 移动)
我正在尝试将 SQLite 查询(在 Android 中)修改为 return 其结果以伪随机顺序排列。与 this question 一样,在重复查询(例如由于分页、屏幕旋转等)中,顺序需要为 stable,所以我不能只使用 ORDER BY RANDOM()
。相反,我想使用一个哈希函数,它依赖于提供稳定性和足够唯一性的几个输入值。 (其中一个值是 table 的唯一 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)
。但是值的重复,加上循环的展开,开始变得笨拙。我可以只使用 +
(忽略溢出)而不是 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
,我将使用一个质数,该质数大于此查询 return 的预期行数。
那么这是在 SQLite 查询中近似 FNV-1a 的合理方法吗?有更好的现有实施吗? IE。尽管我破坏了真正的 FNV-1a 的操作,它实际上会产生一个对普通用户来说看起来非常随机的排序吗?
受到 rwong 和 GrandmasterB 在 the previous attempt at this question before I moved it 上的评论的启发,我决定可以预先计算 FNV-1a 循环的第一次迭代,即基于 table 的唯一 ID 列的散列。预计算列 fnv1a_step1
设置为
(fnvbasis ^ (ID & 0xFF)) * fnvprime
因为这个值是在 table 的每一行上单独预先计算的,所以它可以由应用程序提供,不需要在 SQLite 中表示;因此使用上面的 ^
(XOR)。此外,如果 ID 是一个字符串,我们也可以在 Java 或 Kotlin 中从中计算出一个 8 位哈希值。但我们甚至可以使用
(fnvbasis + (RANDOM() & 0xFF)) * fnvprime
(如果在 SQLite 中执行此操作,则返回使用 +
)因为该值仅计算一次,因此即使从 RANDOM() 计算时也是 stable。
FNV-1a 循环的第二次迭代可以在查询的 ORDER BY 子句中非常简单地计算,使用当前会话 ID,因此它产生一个不同的但-stable 排序每个会话:
ORDER BY (fnv1a_step1 + sessionId & 0xFF) * fnvprime % range;
我已经在我的应用程序中实现了它,它似乎可以满足我的要求。一个session内的顺序是stable,但是每个session的顺序都不一样
(从 https://softwareengineering.stackexchange.com/questions/406813/how-to-implement-fnv-1a-in-sqlite 移动)
我正在尝试将 SQLite 查询(在 Android 中)修改为 return 其结果以伪随机顺序排列。与 this question 一样,在重复查询(例如由于分页、屏幕旋转等)中,顺序需要为 stable,所以我不能只使用 ORDER BY RANDOM()
。相反,我想使用一个哈希函数,它依赖于提供稳定性和足够唯一性的几个输入值。 (其中一个值是 table 的唯一 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)
。但是值的重复,加上循环的展开,开始变得笨拙。我可以只使用 +
(忽略溢出)而不是 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
,我将使用一个质数,该质数大于此查询 return 的预期行数。
那么这是在 SQLite 查询中近似 FNV-1a 的合理方法吗?有更好的现有实施吗? IE。尽管我破坏了真正的 FNV-1a 的操作,它实际上会产生一个对普通用户来说看起来非常随机的排序吗?
受到 rwong 和 GrandmasterB 在 the previous attempt at this question before I moved it 上的评论的启发,我决定可以预先计算 FNV-1a 循环的第一次迭代,即基于 table 的唯一 ID 列的散列。预计算列 fnv1a_step1
设置为
(fnvbasis ^ (ID & 0xFF)) * fnvprime
因为这个值是在 table 的每一行上单独预先计算的,所以它可以由应用程序提供,不需要在 SQLite 中表示;因此使用上面的 ^
(XOR)。此外,如果 ID 是一个字符串,我们也可以在 Java 或 Kotlin 中从中计算出一个 8 位哈希值。但我们甚至可以使用
(fnvbasis + (RANDOM() & 0xFF)) * fnvprime
(如果在 SQLite 中执行此操作,则返回使用 +
)因为该值仅计算一次,因此即使从 RANDOM() 计算时也是 stable。
FNV-1a 循环的第二次迭代可以在查询的 ORDER BY 子句中非常简单地计算,使用当前会话 ID,因此它产生一个不同的但-stable 排序每个会话:
ORDER BY (fnv1a_step1 + sessionId & 0xFF) * fnvprime % range;
我已经在我的应用程序中实现了它,它似乎可以满足我的要求。一个session内的顺序是stable,但是每个session的顺序都不一样