基于日期的均匀分布(ish)随机函数

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

我编写了一个游戏,每天都会显示不同的谜题。我有 n 个谜题,每天都会选择其中一个。游戏不会保存任何信息(不应该),但它应该为当天打开网站的每个人显示相同的谜题。

因此,我目前使用日期本身作为种子,所以对所有人来说都是一样的。 但事实上,这是一个糟糕的想法,因为它使得元素甚至不接近均匀分布。代码就是这里的代码,我的“dayString”的格式为 yyyymmdd,例如今天 20231221。

import { useMemo } from "react";
import seedrandom from "seedrandom";

export function useCountry(dayString: string, numOfElements): number {
  const choosenIndex = useMemo(() => {
    return (
      Math.floor(seedrandom.alea(dayString)() * numOfElements)
    );
  }, [dayString]);

  return choosenIndex;
}

有没有人有更好的想法,可以得到一个好的、更均匀分布的随机函数,同时又不需要保存信息?

javascript typescript react-native random
1个回答
0
投票

从概念上讲,您正在寻找的称为“随机预言”,它用从输出范围中统一选择的真正随机答案来回答不同的问题,但总是对同一问题给出相同的答案。这意味着在你问它“'20231225'的谜题数字是多少”之前,你根本不知道答案在输出范围的哪个位置,但一旦它告诉你

908
,你就知道它总是会回答
 908
对于这个问题。真正的随机预言机在现实生活中可能存在,也可能不存在,但
伪随机
预言机确实存在,而您正在寻找其中之一。


伪随机数生成器

可用于制作伪随机预言机,但您不会通过将问题转换为生成器的种子并仅获取第一个输出值来实现这一点。如果您有一个伪随机数生成器,它采用种子 𝑠,并生成其序列的 𝑖th 成员值 𝑅(𝑠, 𝑖),则仅当您保持 𝑠 固定并递增 𝑖 时,𝑅 才意味着是随机的。当您为固定 𝑖 递增 𝑠 时,不能保证它是随机的。 解决此问题的一种方法是选择一些固定种子 𝑠,然后使用您的问题作为 𝑖,但这意味着您需要运行生成器一段可变的时间,而您可能不想这样做。

相反,您可能正在寻找
哈希函数

。哈希函数通常在其输入 𝑖 中产生均匀分布的输出 𝐻(𝑖)。当您只增加 𝑖 时,它们不一定是良好的伪随机生成器,但大多数为加密目的而设计的哈希函数应该是。 例如,一个容易使用的东西是MD5哈希函数。这不再被认为适合密码学,但你并没有在做密码学。不过,它仍然是一个很好的伪随机预言机。 这给出了类似的东西

function useCountry(dayString: string, numOfElements: number): number { return Number(BigInt("0x" + md5(dayString)) % BigInt(numOfElements)); }

假设 md5()

是您有权访问的函数,它接受一些字符串输入,并生成十六进制字符串输出。由于

md5()

产生 128 位输出,并且
numOfElements
将小于 2

128

,因此您必须缩小输出范围以匹配它。最简单的方法是使用 
余数运算符 (
%
)
。这有一些偏差(参见
为什么人们说使用随机数生成器时存在模偏差?),但它会很小,约为 numOfElements÷2
128
的一部分。只要 MD5 散列的尾随位与其余位一样伪随机,就可以了。
此外,您可能想要对
dayString
输入进行 salt

md5

,以防您想避免 MD5 哈希值中更多“经常出现”的区域。不过,这可能并不重要。 无论如何,让我们看一些输出: const n = 1000; // how many puzzles you have const d = new Date(); d.setDate(d.getDate() - 10); // start date const results: Record<string, number> = {} for (var i = 0; i < 20; i++) { const dayString = [d.getFullYear(), ('0' + (d.getMonth() + 1)).slice(-2), ('0' + d.getDate()).slice(-2)].join(''); const countryUsed = useCountry(dayString, n); results[dayString] = countryUsed d.setDate(d.getDate() + 1); } console.log(results); /* [LOG]: { "20231212": 229, "20231213": 344, "20231214": 112, "20231215": 187, "20231216": 485, "20231217": 499, "20231218": 334, "20231219": 703, "20231220": 578, "20231221": 995, "20231222": 123, "20231223": 589, "20231224": 608, "20231225": 908, "20231226": 501, "20231227": 928, "20231228": 395, "20231229": 600, "20231230": 951, "20231231": 276 } */

这合理吗?您必须进行更彻底的测试才能确定。

请注意,所有这些都假设您对可能的重复或聚集感到满意,因为数学随机性与人类所认为的随机性有很大不同。就我个人而言,如果我做这样的事情,我只会想出小于

numOfElements

的数字的伪随机
排列 
并存储它。

numOfElements

天后,你的谜题无论如何都会重复。但这在技术上超出了所提出问题的范围。

Playground 代码链接

    

© www.soinside.com 2019 - 2024. All rights reserved.