我正在计算一种算法的大量可能的结果组合。为了对这些组合进行排序,我用双重值对它们进行评分,并将它们存储在PriorityQueue中。目前,该队列中有大约200k个项目,这会消耗很多内存。实际上,我只需要说最好的1000或100个项目中的一个。
所以,我开始想是否有一种方法可以在Java中使用固定大小的优先级队列。它应该像这样表现:
如果项目比已存储的项目更好,就将其插入到相应的位置并且丢弃评分最低的元素。
有人有想法吗?再次非常感谢!
马尔科
我正在计算一种算法的大量可能的结果组合。为了对这些组合进行排序,我用双重值对它们进行评分,并将它们存储在PriorityQueue中。目前,该队列中有大约200k个项目,这会消耗很多内存。实际上,我只需要说最好的1000或100个项目中的一个。
所以,我开始想是否有一种方法可以在Java中使用固定大小的优先级队列。它应该像这样表现:
如果项目比已存储的项目更好,就将其插入到相应的位置并且丢弃评分最低的元素。
有人有想法吗?再次非常感谢!
马尔科
que.add(d);
if (que.size() > YOUR_LIMIT)
que.poll();
或者我误解了你的问题?
编辑:忘记提到为了使其工作,您可能需要反转compareTo函数,因为它每个循环都会丢弃具有最高优先级的函数。(如果a比b“更好”,则比较(a,b)应返回正数。
要保留最大的数字,请使用以下代码示例:
public int compare(Double first, Double second) {
// keep the biggest values
return first > second ? 1 : -1;
}
MinMaxPriorityQueue
,Google Guava确实有一个维护队列的类,当添加一个超过集合最大大小的项时,它会比较这些项以找到要删除的项,从而创建空间:MinMaxPriorityQueue
,自版本8起在Google Guava中找到。
顺便说一下,如果你只想删除最旧的元素而不进行任何对象值的比较,则Google Guava 15获得了EvictingQueue
类。
Apache Lucene中有一个固定大小的优先队列:http://lucene.apache.org/java/2_4_1/api/org/apache/lucene/util/PriorityQueue.html
根据我的测试结果,它具有出色的性能。
使用SortedSet:
SortedSet<Item> items = new TreeSet<Item>(new Comparator<Item>(...));
...
void addItem(Item newItem) {
if (items.size() > 100) {
Item lowest = items.first();
if (newItem.greaterThan(lowest)) {
items.remove(lowest);
}
}
items.add(newItem);
}
如果队列中最小的元素小于(在您的情况下,评分较差),只需使用poll()
轮询队列即可。
static <V extends Comparable<? super V>>
PriorityQueue<V> nbest(int n, Iterable<V> valueGenerator) {
PriorityQueue<V> values = new PriorityQueue<V>();
for (V value : valueGenerator) {
if (values.size() == n && value.compareTo(values.peek()) > 0)
values.poll(); // remove least element, current is better
if (values.size() < n) // we removed one or haven't filled up, so add
values.add(value);
}
return values;
}
Comparable
接口,用于按照评分比较组合。
编辑:为了澄清,在我的示例中,Iterable
不需要预先填充。例如,这是一个Iterable<Integer>
,可以提供所有自然数的int
表示:Iterable<Integer> naturals = new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
int current = 0;
@Override
public boolean hasNext() {
return current >= 0;
}
@Override
public Integer next() {
return current++;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
如您所见,内存消耗非常小——对于超过20亿个值,您只需要两个对象(Iterable
和Iterator
)以及一个int
。
当然,您可以相对容易地调整我的代码,使其不使用Iterable
——我只是使用它作为表示序列的优雅方式(另外,我已经做了太多Python和C#的事情☺)。
valueGenerator
中的所有项目? - vahidgIterable
中累积太多的项。此外,如果排名越高,算法越好,那么peek
并不是你想要的。 - vahidgnext()
方法中即时生成值。 - gustafcpeek()
不能解决问题呢?它返回最小的元素,如果当前元素比最小元素更好,我会将最小元素舍弃并添加当前元素。我已经测试了代码,它是有效的。 - gustafcpoll
、remove
、peek
和element
访问队列头部的元素。”正如我在帖子中所说的:我假设用于表示组合的任何内容都以一种将低评级组合视为“小于”更高评级组合的方式实现了Comparable
。如果它没有或不能这样做,我将其留给读者来修改我的示例,以便使用自定义比较器。 - gustafc每次添加一个新项时,似乎自然而然的做法是只保留前1000个,但是PriorityQueue
并没有提供优雅地实现这一点的方式。也许你可以不使用PriorityQueue
,而在一个方法中实现类似以下操作:
List<Double> list = new ArrayList<Double>();
...
list.add(newOutput);
Collections.sort(list);
list = list.subList(0, 1000);
Map
不太好,因为它不允许具有相同评分的两个组合。 - gustafc