Windows XP 使用什么算法来选择你的初始用户头像?
What algorithm did Windows XP use to choose your initial user picture?

原始链接: https://devblogs.microsoft.com/oldnewthing/20260909-00/?p=112683

Windows XP 使用单遍蓄水池采样算法来选择默认用户账户头像,该算法通过 `RtlRandomEx` 函数并由 `GetTickCount()` 提供种子。 与传统的两遍采样方法相比,这种方法具有两大优势。首先,它通过减少文件系统调用(这是性能瓶颈所在)提高了效率。其次,即使在执行过程中文件目录发生变化,它依然能保持稳定性。 该算法通过遍历文件实现,其中第 $n$ 个项目被选为“获胜者”的概率为 $1/n$。这确保了在单遍遍历中实现数学上合理的随机选择。为了防止包含大量文件的目录出现性能问题或“病态行为”,代码中设置了一个安全限制,将采样过程限制在最多 100 张图像。

这篇 Hacker News 讨论聚焦于微软开发者 Raymond Chen 发布的一篇文章,内容涉及 Windows XP 分配随机用户头像所使用的算法。 主要结论是:Windows XP 使用了**蓄水池抽样(reservoir sampling)**算法——这是一种允许程序在单次遍历中从未知大小的数据流中随机选择一项的技术。在当时,这被认为是“优雅的”,因为它避免了多次文件系统遍历带来的性能损耗,这在 21 世纪初缓慢的机械硬盘上尤为重要。 评论区围绕软件工程中的权衡展开了热烈讨论: * **性能与复杂性:** 许多用户认为这种复杂性并无必要,而另一些人则指出,在硬件资源受限的年代,微小而高效的决策至关重要。 * **“旧”与“新” Windows:** 讨论引发了怀旧情绪,用户将 Windows XP 的用户界面与其前代(2000, 95)及后继版本(Vista, 7)进行了对比,并经常争论 Windows 设计的“巅峰”时期。 * **代码质量:** 对话突显了技术权衡的演变,指出现代开发往往为了提升开发效率而牺牲性能,这与 Windows XP 时代那种受限且高度优化的环境形成了鲜明对比。
相关文章

原文

I noted some time ago that Windows XP chose your initial picture at random from among the pictures in the %ALLUSERSPROFILE%\Application Data\Microsoft\User Account Pictures\Default Pictures directory. But it seems people want to know more.

The random number generator is our friend RtlRandomEx, using the current value of GetTickCount() as the initial seed.

The function uses a one-pass random selection algorithm. I can immediately think of two benefits of this decision. First, compared to the naïve two-pass algorithm of counting up all the items, then randomly picking a number from 1 to n, and then iterating a second time to find the item at that index, it’s more efficient because it reduces the amount of calls into the file system, which is where the bottleneck is. Furthermore, the one-pass algorithm avoids complications if the number of files in the directory changes while the code is running.

The one-pass algorithm is a special case of reservoir sampling, where k is 1. This special case permits a tailored algorithm that is much simpler.

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;
}

The way this algorithm works is by observing that in a collection of n items, the last item has a 1/n chance of being randomly selected. If it isn’t selected, then you need to select randomly from the first n − 1 items, which you can solve recursively.

Playing the recursion forward, you start with the base case which is that if you have a list of 1 item, then your only choice is to chose that item. Otherwise, if you have a list of n items, first choose an item randomly from the first n − 1, and then switch to the nth item with a 1/n probability.

As a final safety check, the code stops after sampling 100 pictures. This avoids pathological behavior if somebody puts a million files in the Default Pictures directory.

联系我们 contact @ memedata.com