在不知道前一个数字但知道当前索引(无变量分配)的情况下生成随机数序列的简单函数?

Simple function to generate random number sequence without knowing previous number but know current index (no variable assignment)?

是否有任何(简单的)随机生成函数可以在没有变量赋值的情况下工作?我阅读的大多数函数看起来像这样 current = next(current)。但是目前我有一个限制(来自 SQLite)我根本不能使用任何变量。

有没有办法生成一个只有 n(序列中的当前数字索引)和 seed 的数字序列(例如,从 1 到 max)?

目前我正在使用这个:

cast(((1103515245 * Seed * ROWID + 12345) % 2147483648) / 2147483648.0 * Max as int) + 1

其中 max 为 47,ROWIDn。然而,对于某些种子,重复率太高(47 个中有 3 个是唯一的)。

在我的要求中,重复是可以的,只要不是太多(<50%)即可。有没有更好的功能满足我的需求?

问题有sqlite标签,但任何language/pseudo-code都可以。

P.s:我曾尝试将 Linear congruential generators 与一些 a/c/m 三胞胎一起使用,并将 Seed * ROWID 用作 Seed,但效果不佳,甚至更糟。

编辑:我目前使用这个,但我不知道它来自哪里。汇率看起来比我的好:

((((Seed * ROWID) % 79) * 53) % "Max") + 1

A linear congruential generator with appropriately-chosen parameters (a, c, 和模 m)将是一个 full-period 生成器,这样它在重复之前伪随机地循环遍历其周期中的每个整数。尽管您以前可能尝试过这个想法,但您是否考虑过m 等同于您的情况下的max?有关此类生成器的参数选择列表,请参阅 L'Ecuyer, P., "Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure", Mathematics of Computation 68(225), January 1999.

请注意,在 SQLite 中实现此操作存在一些实际问题,特别是如果您的 SQLite 版本仅支持 32 位整数和 64 位 floating-point 数字(精度为 52 位)。也就是说,可能存在风险——

  • 如果整数的中间乘法超过 32 位,则溢出,并且
  • 如果中间乘法产生 greater-than-52 位数字,则精度损失。

此外,请考虑您创建随机数序列的原因:

  • 这个顺序是不是故意设计成不可预测的?在这种情况下,仅线性同余生成器是不够的,您应该通过其他方式生成唯一标识符,例如 combining unique numbers with cryptographically random numbers.
  • 以这种方式生成的数字是否会以任何方式暴露给最终用户?如果不是,就没有必要用"shuffling"它们来混淆它们了。

此外,根据您使用的 SQLite API(针对您的编程语言),可能有一种方法可以编写自定义函数来将种子和 ROWID 转换为随机数唯一编号。然而,细节在很大程度上取决于特定的 SQLite API。 显示 Perl 示例。

如何使用良好的散列函数并将结果映射到 [1...max] 范围?

沿线(伪代码)。 sha1 已添加到 SQLite 3.17。

sha1(ROWID) % Max + 1

或使用任何外部 C 代码进行哈希(murmur、chacha、...),如图所示 here

我不确定您是否仍然遇到同样的问题,但我可能会为您提供解决方案。 你可以做的是使用基于移位寄存器的伪随机 M 序列生成器。您只需要对原始多项式进行足够高的阶数,而实际上不需要存储任何变量。 有关详细信息,您可以查看 wiki page

你需要编写的只是原始多项式移动方程,我已经在在线编辑器中检查过它应该很容易做到。我认为对您来说最简单的方法是使用 Binary base 并使用 PRBS 序列,并根据您将拥有的元素数量选择序列长度。例如,这是 2^15 = 32768 (PRBS15) 长度的实现,我从 wiki 页面获取的原始多项式(在那里你可以找到一直到 PRBS31 的原始多项式 2^31=2.1475e+09) 基本上你需要做的是:

SELECT (((ROWID << 1) | (((ROWID >> 14) <> (ROWID >> 13)) & 1)) & 0x7fff)

这种方法的美妙之处在于,如果您采用周期长于 ROWID 最大值的 PRBS 序列,您将拥有唯一的随机索引。很简单的。 :)

如果您在搜索原始多项式方面需要帮助,您可以查看我的 github repo,它专门处理查找原始多项式和唯一 m 序列的问题。它目前是用 Matlab 编写的,但我计划在接下来的几天内用 python 编写它。

干杯!