我正在研究一种可以处理大量项目的排序/排名算法,我需要以有效的方式实现以下算法才能使其工作:
有两个数字列表。它们同样长,约100-500万件。从这里我需要找到这些列表之间的第n大产品,即。如果您创建一个矩阵,其中您有一个列表,那么您有另一个列表,每个单元格是上面的数字和侧面的数字的乘积。
示例:列表为A=[1, 3, 4]
和B=[2, 2, 5]
。然后产品是[2, 2, 5, 6, 6, 15, 8, 8, 20]
。如果我想要第三大,那就是8。
天真的解决方案是简单地生成这些数字,对它们进行排序,然后选择第n个最大数字。但那是O(m^2 * log m^2)
,其中m是小列表中元素的数量,而且速度不够快。
我认为我需要的是先对两个小清单进行排序。那是O(m * log m)
。然后我肯定知道最大的一个A [0] * B [0]。第二大的是A [0] * B [1]或A [1] * B [0],......
我觉得这可以在O(f(n))
步骤中完成,与矩阵的大小无关。但我无法找到一种有效的方法来完成这一部分。
编辑:有一个被删除的答案,建议记住两个有序集中的位置,然后看A [a] * B [b + 1]和A [a + 1] * B [b] ,返回较大的一个并递增a / b。我要在删除之前发表这条评论:
这不起作用。想象一下两个列表A = B = [3,2,1]。这会给你 像[9,6,3; 6,4,2; 3,2,1]。所以你从(0,0)= 9开始,转到 (0,1)= 6然后选择是(0,2)= 3或(1,1)= 4。但是,这会 错过(1,0)= 6,这比两者都大。所以你不能只看 两个邻居,但你必须回溯。
答案 0 :(得分:4)
我认为可以在O(n log n + n log m)
中完成。这是我的算法草图,我认为将起作用。这有点粗糙。
O(m log m)
)O(m log m)
)s
为min(m, n)
。 (需O(1)
)s
创建L[0]
延迟序列迭代器L[s-1]
。 L[i]
将遍历s
值A[i]*B[0]
,A[i]*B[1]
,...,A[i]*B[s-1]
。 (需O(s)
)q
中。迭代器将根据其当前值进行优先级排序。 (需要O(s)
,因为最初他们已经有序了)n
中提取q
个值。拉出的最后一个值将是所需的结果。拉动迭代器时,会使用其下一个值作为新优先级将其重新插入q
。如果迭代器已用完,请勿重新插入。 (需O(n log s)
)总之,此算法将采用O(m log m + (s + n)log s)
,但s
等于m
或n
。
答案 1 :(得分:0)
您不需要对500 000个元素进行排序以获得前3个。
只需取第3个,将它们放入SortedList,然后遍历列表,用新值替换3个元素中的最小值,如果更高,则求结果列表。
对两个列表执行此操作,您将以3 * 3矩阵结束,其中应该很容易取第3个值。
Here is an implementation in scala
如果我们假设n小于m,且A = [1,3,4]且B = [2,2,5],则n = 2:
你会采取(3,4)=>他们排序(4,3)
然后取(2,5)=>对它们进行排序(5,2)
您现在可以进行压缩搜索。当然,现在最大的产品是(5,4)。但下一个是(4 * 2)或(5 * 3)。对于更长的列表,您可以记住4 * 2的结果是什么,仅将其与下一个产品进行比较,采用另一种方式。这样你只会计算一个产品太多。
答案 2 :(得分:0)
我认为没有O(f(n))的算法,它与m无关。
但是有一个相对较快的O(n * logm)算法:
首先,我们对两个数组进行排序,得到A [0]> A [1]> ...> A [m-1]和B [0]> B [1]> ...> B [M-1]。 (当然,这是O(mlogm)。)
然后我们构建一个最大堆,其元素是A [0] * B [0],A [0] * B [1],... A [0] * B [m-1]。我们维持一个“指针数组”P [0],P [1],... P [m-1]。 P [i] = x表示B [i] * A [x]当前在堆中。所有P [i]最初都为零。
在每次迭代中,我们从堆中弹出max元素,这是下一个最大的产品。假设它来自B [i] * A [P [i]](我们可以记录堆中的元素来自哪个B [i]),然后我们将相应的指针向前移动:P [i] + = 1,并将新的B [i] * A [P [i]]推入堆中。 (如果P [i]移动到超出范围(> = m),我们只需将-inf推入堆中。)
在第n次迭代之后,我们获得了第n个最大的产品。
有n次迭代,每次都是O(logm)。
编辑:添加一些细节