如何从文件夹树中快速选择随机文件?

How to quickly pick a random file from a folder-tree?

本文关键字:快速选择 随机文件 文件夹      更新时间:2023-10-16

我正在尝试从文件夹树中选择一个随机文件,从固定路径开始,并在所有子文件夹(或所选文件夹本身(中递归"搜索"。

我的想法是:制作文件列表,计算文件数量,在此范围内选择一个随机数,然后在该索引处选择文件。

这是我的代码:

// create list of all files
std::vector<std::string> paths;
for (const auto &entry : std::filesystem::recursive_directory_iterator(mPathDirectory)) {
if (!std::filesystem::is_directory(entry)) {
paths.push_back(entry.path().string());
}
}
// pick random file
size_t numberOfFiles = paths.size();
int indexRandomFile = (int)round(rescale(random::uniform(), 0.0, 1.0, 0, numberOfFiles - 1));
return paths[indexRandomFile];

同样使用O3,考虑到我有大量的文件列表并且我在"音频"应用程序中(应该更快(,它非常慢。

你有什么更聪明的想法吗?像O(1(这样的东西?:P

使用储层采样技术可以通过这种方式随机统一选择文件。对于每个文件,以 1/N 的几率选择它,其中 N 是到目前为止找到的文件数,包括刚刚找到的文件。然后,随机文件是以这种方式选择的最后一个文件。

另请参阅此问题,了解从文本文件中随机选择一行的类似任务;通常,只要事先不知道要选择的项目数量,就适用储层采样。


下面解释了储层取样的工作原理:

  1. 将 N 设置为 1。
  2. 将"选择文件"设置为空。
  3. 对于每个文件:
    • 如果random::uniform() < 1.0 / N,请将"选择文件"设置为文件名。
    • 将 1 加到 N。

现在,ChosenFile是随机选择的文件名。


根据您问题中的代码,以下是如何实施储层采样。请注意,列表中不再存储任何文件。另请注意,此代码未经测试。

// store randomly chosen file
std::string path;
size_t n = 1;
for (const auto &entry: std::filesystem::recursive_directory_iterator(mPathDirectory)) {
if (!std::filesystem::is_directory(entry)) {
if (random::uniform() < 1.0 / n) {
path = entry.path().string();
}
n++;
}
}
return path;

如果您对文件夹结构一无所知,则必须递归以找出有多少项。没有O(1(解决方案。

但是一个"应用程序"只需要感觉快速,也就是说,通常只有对响应能力的感知才是重要的。为此,在第一次启动时,您可以使用启发式方法,例如以一定的概率递归到某些子文件夹中,直到找到文件。它不会是均匀随机的,但从用户的角度来看,它会相对任意地选择。

同时,您可以真正递归到文件夹中并建立缓存,而最初选择的文件已经在播放。