What algorithm did Windows XP use to choose your initial user picture?

What algorithm did Windows XP use to choose your initial user picture?

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. Has anyone attempted to figure out the RNG for how Windows XP determines what profile picture is used on first account creation? — Xeno (@XenoPanther) December 11, 2025

我曾提到过,Windows XP 会从 %ALLUSERSPROFILE%\Application Data\Microsoft\User Account Pictures\Default Pictures 目录下的图片中随机选择一张作为你的初始头像。但似乎大家还想了解更多细节。有人尝试研究过 Windows XP 在创建首个账户时,究竟是通过什么样的随机数生成器(RNG)来决定头像的吗?—— Xeno (@XenoPanther) 2025年12月11日

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.

这里使用的随机数生成器是我们熟悉的老朋友 RtlRandomEx,它以 GetTickCount() 的当前值作为初始种子。该函数采用的是一种“单遍扫描”(one-pass)的随机选择算法。

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.

我立刻能想到这种方案的两个优点。首先,相比于那种“先统计所有项目总数,再从 1 到 n 中随机选一个数,最后再次遍历以找到对应索引项”的原始两遍扫描算法,单遍扫描算法效率更高,因为它减少了对文件系统的调用次数,而文件系统操作往往是性能瓶颈所在。此外,如果代码运行期间目录中的文件数量发生了变化,单遍扫描算法也能避免由此产生的复杂问题。

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.

这种单遍扫描算法其实是“蓄水池采样”(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;
}

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.

该算法的原理在于:在一个包含 n 个项目的集合中,最后一个项目被随机选中的概率是 1/n。如果它没被选中,那么你需要从前 n-1 个项目中随机选择,这个问题可以通过递归解决。从递归的正向逻辑来看:基础情况是如果列表中只有 1 个项目,那么你唯一的选择就是它。反之,如果你有 n 个项目,先从前 n-1 个项目中随机选一个,然后以 1/n 的概率切换到第 n 个项目。

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.

作为最后的安全检查,代码在采样 100 张图片后就会停止。这避免了如果有人在 Default Pictures 目录中放入一百万个文件时可能出现的异常行为。