我需要一些关于如何解决算法问题的建议(即本身不是编程)。以下是我的需求以及我如何努力满足它们。欢迎任何改进意见。
让我首先解释我的目标。我想玩一些扑克大约十亿次。也许我正在尝试创建下一个 PokerStars.net,也许我只是疯了。
我想创建一个程序,它可以生成更好的随机牌组,而不是调用 random() 的典型程序。这些需要是由高质量随机数创建的生产质量套牌。我听说商业级扑克服务器对每张牌使用 64 位向量,从而确保每天玩的所有数百万扑克游戏的随机性。
我想保持我写的简单。为此,该程序应该只需要一个输入来实现既定目标。我已经决定,每当程序开始时,它都会记录当前时间并将其用作起点。我意识到这种方法在商业环境中是不可行的,但只要它能够支持数十亿游戏,比更简单的替代方案更好,我会很高兴。
我开始编写伪代码来解决这个问题,但遇到了一个棘手的问题。这对我来说很清楚,但对你来说可能不是,所以请告诉我。
伪代码如下:
Start by noting the system time.
Hash the current time (with MD5) around ten times (I chose the ten arbitrarily).
Take the resulting hash, and use it as the seed to the language-dependent random() function.
Call random() 52 times and store the results.
Take the values produced by random() and hash them.
Any hash function that produces at least 64-bits of output will do for this.
Truncate (if the hash is too big) so the hashes will fit inside a 64-bit double.
Find a way to map the 52 doubles (which should be random now, according to my calculations) into 52 different cards, so we can play some poker.
我的问题是最后一步。我想不出一种将每个 64 位值正确映射到相应卡的方法,而不必担心两个数字相同(不太可能)或丢失任何随机性(可能)。
我的第一个想法是将 0x0000000000000000 - 0xFFFFFFFFFFFFFFFF 分成四个偶数部分(代表花色)。但是不能保证我们会在每个部分找到正好 13 张卡片,这会很糟糕。
既然您知道我被困在哪里,您将如何克服这一挑战?
-- 已编辑 --
从 /dev/random 读取字节实际上会很好。但这仍然让我迷失了如何进行转换?(假设我为 52 张卡读取了足够的字节)。
我真正的愿望是采用简单且可预测的东西,例如系统时间,并将其转换为随机的一副牌。使用系统时间播种 random() 是一种不好的方法。因此,时间的散列和随机()出来的值的散列。
见鬼,如果我愿意,我可以散列来自 /dev/random 的字节,只是为了发出嘘声和咯咯笑声。散列提高了事物的随机性,不是吗?这不是现代密码管理器存储经过数千次哈希处理的密码的原因吗?
-- 编辑 2 --
所以我已经阅读了你们的答案,我发现自己对你们中许多人暗示的结论感到困惑。我在第一次编辑中暗示了它,但它真的让我陷入了循环。我只想指出并继续前进。
彩虹表的存在可以进行时髦的数学运算和巧妙的魔法,本质上充当映射到特定密码的常见哈希的查找表。据我了解,更长、更好的密码不太可能出现在这些彩虹表中。但事实仍然是,尽管有多少用户密码很常见,但经过哈希处理的密码经过数千次哈希处理后仍然安全。
那么,在这种情况下,许多确定性操作增加了原始密码的随机性(或似乎?)我不是说我是对的,我只是说这就是我的感觉。
我想指出的第二件事是我正在倒退。
我的意思是,你们都在建议我使用一副有序的、可预测的、非随机的纸牌,并在上面使用 Fisher-Yates 洗牌。我确信 Fisher-Yates 是一个很好的算法,但可以说你不能出于任何原因使用它。
您能否获取一个随机的字节流,例如大约 416 个字节(52 张卡片,每张卡片 8 个字节),然后 BAM 会生成一个已经随机的卡片组?字节是随机的,所以这样做应该不会太难。
大多数人会从一副 52 张牌(随机或非随机)开始,然后将它们交换很多次(通过选择一个随机索引进行交换)。如果你能做到这一点,那么你可以取 52 个随机数,遍历它们一次,然后生成随机牌组。
正如我所描述的那样, 该算法接受随机字节流并查看每个 8 字节块。它将每个块映射到一张卡片。
前任。0x123 映射到黑桃 A。0x456 映射到钻石之王 Ex。0x789 映射到 Clubs 的 3 .... 等等。
只要我们为映射选择了一个好的模型,就可以了。无需改组。该程序将减少到两个步骤。
第 1 步:从一个好的源中获取足够数量的随机字节 第 2 步:将此字节流拆分为 52 个块,每张卡片一个块 第 2a 步:遍历这 52 个块,根据我们的方法将它们转换为卡片值地图。
这有意义吗?