arrays - 比较同一数组中的元素(图像文件)以查找附近重复项的算法
问题描述
我正在研究一种算法的实现,给定表示在文件夹中找到的文件的定义明确的对象数组,应该比较它们以找到类似重复的克隆以进行删除。这个算法完成后,应该考虑任何类型的文件,但为了让事情更容易,让我们只讨论这个问题的图像。
这个问题的 TL;DR 是:在比较一个非常大的数组中的文件时,我可以实施什么类型的算法/规则来最小化我的 big(O) 表示法的复杂性。我不是在谈论比较本身(我使用的是 Levenshtein / Hamming 距离和基于被比较的 2 个文件的 Dice 系数的组合),而是细分一百万个文件数组并应用某种逻辑的实际决定后面的“加工”部分比较。
当我说这些对象是“定义明确的”时,我的意思是我已经有了可以通过快速扫描获得的信息基础。像absolutePath
, createDate
,size
以字节为单位的东西,extension
还有一个 MD5 哈希,它把所有东西都放在我可以用作标识符的东西上,因为即使是两个相同的文件也至少有不同的日期,所以哈希就足够了。在图像的情况下,我也得到了pHash
比较。所有这些都出现在实际算法之前,因此还没有影响性能。
当我有一个包含所有这些对象的非常大的数组(想想arr.length > 1000000
)时,问题就开始了,突然之间,白痴证明 O(n log n) 两个 for 循环不再剪切了:
for (var i = 0; i < arr.length; i++) {
var fileBeingCompared = arr[i];
for (var j = 0; j < arr.length; j++) {
var fileToCompare = arr[j];
if (methodThatComparesFiles(fileBeingCompared, fileToCompare)) { // files are similar
// do stuff
}
}
}
在相当不错的 PC 上,在相对较小的小于 10000 张图像的测试文件夹上,这实际上花费了 4 多小时。显然,我开始进行一些改进,以尽量减少超过 10000^2 Levenshtein 比较的开销。
没有特别的顺序,这就是我所做的:
- 预先对数组进行排序,以便具有相似大小的文件更靠近在一起
- 将数组拼接成更小的块并序列化它们的执行(到目前为止使用相同的算法)
- 在调用任何昂贵的方法之前,优先考虑已知值
fileName
,并size
排除明显的重复项 - 我不是在彼此内部使用两个精确的 for 循环,而是通过从数组 X 中删除第一个元素 A,将其与数组 Y 上的每个其他元素 B 进行比较,然后将 A 添加到数组 Y,来严格比较尚未比较的。通过这种方式,我大大减少了诸如此类的冗余比较的
a = b
数量b = a
。也许这个例子可以更好地欺骗我的方法:
var arrayX = []; // contains 1000 or so files
var arrayY = []; // starts empty but is filled with the already-compared elements
while (arrayX.length) {
var fileBeingCompared = arrayX.pop();
for (var i = 0; i < arrayY.length; i++) {
var fileToCompare = arrayY[j];
if (methodThatComparesFiles(fileBeingCompared, fileToCompare)) { // files are similar
// do stuff
}
}
arrayY.push(fileBeingCompared);
}
但即便如此,这些改进虽然很明显,但还远远不够好。不仅如此,特定的实现本身也存在缺陷。假设我有 2 个视频,一个 480P,另一个 1080p:每个属性都会有所不同;大小、名称(可能)、日期等。由于我目前按大小排序,它们最终会出现在不同的线程中,而不是直接比较,留下重复。
如果有人可以提出某种可以帮助我获得性能的适用算法或建议,我正在使用带有 NodeJs 的 JS ES6,所以如果有任何推荐的库,请记住这一点。感谢读到最后的人。
解决方案
如果您的相似性计算没有什么结构,那么您将不得不测试每一对。如果它(或它的某种变换)至少服从三角不等式,您可以尝试构建某种结构来回答精确或近似的最近邻搜索,并为每个点找到它的最近邻。一个只需要计算距离的最近邻数据结构是https://en.wikipedia.org/wiki/Cover_tree,但除非您的点主要位于低维子空间附近,否则您可能会看到很少的加速。
推荐阅读
- php - 如何将来自多个循环的数据组合到多维数组中的同一个数字键中?
- python - 在 python anaconda 中使用不同版本的包
- python - 用值替换一些 html 内容
- reactjs - react-redux 调度未定义
- angular - 有没有办法以角度绑定到组件数组并使用 ngFor 渲染当前组件中的组件
- canvas - 在paperjs中将画布数据适合另一个画布
- node.js - node-pre-gyp install --fallback-to-build html-to-docx
- c - 如果有的话,哪一个是更好的方法?
- laravel - Laravel 7:将枢轴附加到具有多个值的表
- javafx - JavaFX 3D 有两个场景,两个摄像头查看相同的对象