随机数生成器是我们的老朋友 RtlRandomEx,它使用 GetTickCount() 的当前值作为初始种子。
该函数采用单遍随机选择算法。我立刻能想到这一决定的两个好处。首先,与朴素的two-pass算法相比——即先统计所有条目数量,然后从 1 到 n 随机取一个数,再进行第二遍迭代找到该索引处的条目——它更高效,因为它减少了对文件系统的调用次数,而文件系统正是瓶颈所在。此外,单遍算法还避免了在代码运行期间目录中文件数量发生变化时可能出现的复杂情况。
单遍算法是蓄水池抽样(reservoir sampling)的一个特例,其中 k 为 1。这一特例允许使用一个量身定制的算法,该算法要简单得多。
selectRandomFromIterator(iterator) { var count = 0; var winner = null;
while (iterator.moveNext()) { ++count; if (uniform_random(min: 1, max: count) == count) { winner = iterator.current(); } }
return winner; }
该算法的工作原理基于这样一个观察:在包含 n 个条目的集合中,最后一个条目被随机选中的概率是 1/n。如果它没有被选中,那么你需要从前 n − 1 个条目中随机选择,这可以递归求解。
把这个递归向前推演:基本情况是,如果你有一个只有 1 个条目的列表,那么你唯一的选择就是选那个条目。否则,如果你有一个包含 n 个条目的列表,先从前 n − 1 个条目中随机选择一个,然后以 1/n 的概率切换到第 n 个条目。
作为最后一道安全检查,代码在采样 100 张图片后会停止。这避免了有人往 Default Pictures 目录里塞进一百万个文件时可能出现的病态行为。
关于作者:Raymond Chen 参与 Windows 的演进已超过 30 年。2003 年,他创建了一个名为 The Old New Thing 的网站,其受欢迎程度远远超出了他的想象,这一发展至今仍让他感到紧张不安。该网站还衍生出一本碰巧也名为 The Old New Thing 的书(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲述一些不传达任何有用信息的故事。