結果として得られるアルゴリズムの組み合わせを多数計算しています。この組み合わせを並べ替えるには、二重の値で評価し、PriorityQueueに保存します。現在、そのキューには約200kのアイテムがあり、かなりメモリ集約的です。実際には、リスト内のすべてのアイテムの中で最高の1000または100と言うだけで十分です。そのため、Javaで固定サイズの優先度キューを作成する方法があるかどうかを考え始めました。私はこのように振る舞う必要があります:アイテムはすでに保存されているものよりも優れていますか?はいの場合は、適切な位置に挿入し、評価が最も低い要素を捨てます。
誰かがアイデアを持っていますか?本当にありがとうございました!
マルコ
que.add(d);
if (que.size() > YOUR_LIMIT)
que.poll();
または私はあなたの質問を誤解しましたか?
編集:これが機能するためには、サイクルごとに最も優先度の高い関数が破棄されるため、おそらくcomparTo関数を反転する必要があることを忘れてしまいました。 (aが「より良い」場合b比較(a、b)は正の数を返します。
最大数を維持する例では、次のようなものを使用します。
public int compare(Double first, Double second) {
// keep the biggest values
return first > second ? 1 : -1;
}
MinMaxPriorityQueue
、Googleグアバ実際、キューを維持するためのクラスがあり、コレクションの最大サイズを超えるアイテムを追加するときに、アイテムを比較して削除するアイテムを見つけ、それによって部屋を作成します: 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億を超える値の場合、2つのオブジェクト(Iterable
とIterator
)と1つのint
が必要です。
もちろん、コードをIterable
を使用しないように簡単に変更することもできます。これは、シーケンスを表現するエレガントな方法であるため使用しただけです(また、あまりにも多くのことを行ってきましたPythonおよびC#☺)。
アイテムを追加するたびに上位1000を保持するのは自然なことのようですが、PriorityQueue
はそれを優雅に達成するために何も提供していません。おそらく、PriorityQueue
を使用する代わりに、メソッドで次のようなことを行うことができます。
List<Double> list = new ArrayList<Double>();
...
list.add(newOutput);
Collections.sort(list);
list = list.subList(0, 1000);
より良いアプローチは、プログラムの実行時にキューに入れられるものをより厳密にモデレートし、削除して追加することです。キューに追加する前に一部のアイテムを除外する余地があるようです。いわば、ホイールを再発明するよりも簡単です。