Java: Collection 个最高的 N 个元素
Java: Collection of N highest elements
我需要在一大 collection 数据中找到 N 个最大的元素。
我有:
- 外部数据库(Cassandra)中的 collection 数亿项
一个遍历这些项目并找到具有最大值的项目的作业
Item largest = null;
// Page through big data
List<Item> items = getNextPage(pageSize);
while (items.size() > 0) {
// Update largest item based on values from current page
for (Item current : items) {
if (largest == null || largest.getValue() < current.getValue()) {
largest = current;
}
}
// Move to next page
items = getNextPage(pageSize);
}
我需要:
- 扩展此作业以容纳 N(比如说 100)个具有最高值的元素
我的做法:
我在考虑像优先级 queue 和固定大小
这样的东西
class PQsort implements Comparator<Item> {
public int compare(Item one, Item two) {
return two.getValue() - one.getValue();
}
}
PriorityQueue<Item> pq = new PriorityQueue<Item>(101, new PQsort());
...while...for...
pq.offer(current);
if (pq.size() == 101) {
// Remove the tail somehow
}
...
去掉尾巴:Removing tail element of priority queue
这个任务的最佳解决方案是什么?
对此有一些想法。
此任务非常适合使用多个处理器。您可以将页面拆分到线程池中,然后在完成时合并结果。
不需要插入每个值,让集合排序然后删除最小值。更快的方法是检查每个项目是否大于集合中最小的(即最后一个)项目。
下面是一个简单的示例,它在包含 10,000 个随机整数的数组中查找 100 个最大的整数。
Queue<Integer> largest = new PriorityQueue<>(100);
for (int item : new Random().ints(10000, 0, 100).toArray()) {
if (largest.size() < 100 || largest.peek() < item) {
if (largest.size() == 100)
largest.remove();
largest.add(item);
}
}
System.out.println(largest);
我会用 List<Item>
替换你的 largest
。在你的循环中你可以做类似的事情:
largest.add(current);
bubbleSort(largest);
if ( largest.size() > 100 ) {
largest.remove(0);
}
通过使用冒泡排序,您可以保持 O(n)
复杂性,因为冒泡排序的一个特点是,如果只有一个条目不合适,它会在 O(n)
时间内执行。
我把它留给学生去实现 bubbleSort
。
可以使用 SortedSet 实现来完成这项工作:
class PQsort implements Comparator<Item> {
public int compare(Item one, Item two) {
return two.getValue() - one.getValue();
}
}
...
Comparator<Item> itemComparator = new PQSort();
SortedSet<Item> top100 = new TreeSet<Item>(100, itemComparator);
Item smallestOfTheTop100 = null;
// Page through big data
List<Item> items = getNextPage(pageSize);
while (items.size() > 0) {
for (Item current : items) {
if (smallestOfTheLargest == null || itemComparator.compare(smallestOfTheTop100, current) > 0) {
top100.add(item); // the current item is larger than the end of our top 100 list, so add it to the set.
top100.remove(top100.first()); // remove the 101th element of the set - it is now extra.
smallestOfTheTop100 = top100.first();
}
}
// Move to next page
items = getNextPage(pageSize);
}
正如 sprinter 在他的回答中所说,它也可以在并行实现中重新设计 - 例如使用流。
我会坚持使用 PriorityQueue,只在它大于必要时删除项目。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 10, 2, 9, 3, 7, 4, 6, 5, 7, 7, 7);
findNHighest(list, 3);
findNHighest(list, 1);
findNHighest(list, 4);
}
private static void findNHighest(List<Integer> list, int n) {
Queue<Integer> nthHighest = new PriorityQueue<>();
for (Integer each : list) {
nthHighest.add(each);
if (nthHighest.size() > n) {
nthHighest.poll();
}
}
System.out.println(nthHighest);
}
输出
[7, 9, 10]
[10]
[7, 7, 9, 10]
构建优先级队列需要 MlogM,其中 M 是项目总数,然后弹出 N 个项目需要额外的 NlogM。
这比使用 MlogM 排序数组然后 select 在 N.
中的最后 N 项要贵一些
如果 N 很小,只需将数组迭代 N 次,每次取下一个最佳最大值。
标准解决方案是 Quick Select,具有 average 线性时间,here 是 Profesor 的实现罗伯特·塞奇威克。如果你需要 100 个最大的项目,select 最大的第 100 个元素,项目右边的所有元素都会更大。
教授就该主题进行了精彩的 video 讲座。
相关部分:
/***************************************************************************
* Rearranges the elements in a so that a[k] is the kth smallest element,
* and a[0] through a[k-1] are less than or equal to a[k], and
* a[k+1] through a[n-1] are greater than or equal to a[k].
***************************************************************************/
public static <Key extends Comparable<Key>> Key select(Key[] a, int k) {
if (k < 0 || k >= a.length) {
throw new IndexOutOfBoundsException("Selected element out of bounds");
}
StdRandom.shuffle(a);
int lo = 0, hi = a.length - 1;
while (hi > lo) {
int i = partition(a, lo, hi);
if (i > k) hi = i - 1;
else if (i < k) lo = i + 1;
else return a[i];
}
return a[lo];
}
我需要在一大 collection 数据中找到 N 个最大的元素。
我有:
- 外部数据库(Cassandra)中的 collection 数亿项
一个遍历这些项目并找到具有最大值的项目的作业
Item largest = null; // Page through big data List<Item> items = getNextPage(pageSize); while (items.size() > 0) { // Update largest item based on values from current page for (Item current : items) { if (largest == null || largest.getValue() < current.getValue()) { largest = current; } } // Move to next page items = getNextPage(pageSize); }
我需要:
- 扩展此作业以容纳 N(比如说 100)个具有最高值的元素
我的做法:
我在考虑像优先级 queue 和固定大小
这样的东西class PQsort implements Comparator<Item> { public int compare(Item one, Item two) { return two.getValue() - one.getValue(); } } PriorityQueue<Item> pq = new PriorityQueue<Item>(101, new PQsort()); ...while...for... pq.offer(current); if (pq.size() == 101) { // Remove the tail somehow } ...
去掉尾巴:Removing tail element of priority queue
这个任务的最佳解决方案是什么?
对此有一些想法。
此任务非常适合使用多个处理器。您可以将页面拆分到线程池中,然后在完成时合并结果。
不需要插入每个值,让集合排序然后删除最小值。更快的方法是检查每个项目是否大于集合中最小的(即最后一个)项目。
下面是一个简单的示例,它在包含 10,000 个随机整数的数组中查找 100 个最大的整数。
Queue<Integer> largest = new PriorityQueue<>(100);
for (int item : new Random().ints(10000, 0, 100).toArray()) {
if (largest.size() < 100 || largest.peek() < item) {
if (largest.size() == 100)
largest.remove();
largest.add(item);
}
}
System.out.println(largest);
我会用 List<Item>
替换你的 largest
。在你的循环中你可以做类似的事情:
largest.add(current);
bubbleSort(largest);
if ( largest.size() > 100 ) {
largest.remove(0);
}
通过使用冒泡排序,您可以保持 O(n)
复杂性,因为冒泡排序的一个特点是,如果只有一个条目不合适,它会在 O(n)
时间内执行。
我把它留给学生去实现 bubbleSort
。
可以使用 SortedSet 实现来完成这项工作:
class PQsort implements Comparator<Item> {
public int compare(Item one, Item two) {
return two.getValue() - one.getValue();
}
}
...
Comparator<Item> itemComparator = new PQSort();
SortedSet<Item> top100 = new TreeSet<Item>(100, itemComparator);
Item smallestOfTheTop100 = null;
// Page through big data
List<Item> items = getNextPage(pageSize);
while (items.size() > 0) {
for (Item current : items) {
if (smallestOfTheLargest == null || itemComparator.compare(smallestOfTheTop100, current) > 0) {
top100.add(item); // the current item is larger than the end of our top 100 list, so add it to the set.
top100.remove(top100.first()); // remove the 101th element of the set - it is now extra.
smallestOfTheTop100 = top100.first();
}
}
// Move to next page
items = getNextPage(pageSize);
}
正如 sprinter 在他的回答中所说,它也可以在并行实现中重新设计 - 例如使用流。
我会坚持使用 PriorityQueue,只在它大于必要时删除项目。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 10, 2, 9, 3, 7, 4, 6, 5, 7, 7, 7);
findNHighest(list, 3);
findNHighest(list, 1);
findNHighest(list, 4);
}
private static void findNHighest(List<Integer> list, int n) {
Queue<Integer> nthHighest = new PriorityQueue<>();
for (Integer each : list) {
nthHighest.add(each);
if (nthHighest.size() > n) {
nthHighest.poll();
}
}
System.out.println(nthHighest);
}
输出
[7, 9, 10]
[10]
[7, 7, 9, 10]
构建优先级队列需要 MlogM,其中 M 是项目总数,然后弹出 N 个项目需要额外的 NlogM。 这比使用 MlogM 排序数组然后 select 在 N.
中的最后 N 项要贵一些如果 N 很小,只需将数组迭代 N 次,每次取下一个最佳最大值。
标准解决方案是 Quick Select,具有 average 线性时间,here 是 Profesor 的实现罗伯特·塞奇威克。如果你需要 100 个最大的项目,select 最大的第 100 个元素,项目右边的所有元素都会更大。 教授就该主题进行了精彩的 video 讲座。
相关部分:
/***************************************************************************
* Rearranges the elements in a so that a[k] is the kth smallest element,
* and a[0] through a[k-1] are less than or equal to a[k], and
* a[k+1] through a[n-1] are greater than or equal to a[k].
***************************************************************************/
public static <Key extends Comparable<Key>> Key select(Key[] a, int k) {
if (k < 0 || k >= a.length) {
throw new IndexOutOfBoundsException("Selected element out of bounds");
}
StdRandom.shuffle(a);
int lo = 0, hi = a.length - 1;
while (hi > lo) {
int i = partition(a, lo, hi);
if (i > k) hi = i - 1;
else if (i < k) lo = i + 1;
else return a[i];
}
return a[lo];
}