при профилировании приложения Java, которое вычисляет иерархическую кластеризацию тысяч элементов. Я понял, что ArrayList.get
занимает половину процессора, необходимый в части кластеризации исполнения.Java: ArrayList узкое место
Алгоритм ищет более двух аналогичных элементов (так что O (п * (п + 1)/2)), вот псевдо-код:
int currentMax = 0.0f
for (int i = 0 to n)
for (int j = i to n)
get content i-th and j-th
if their similarity > currentMax
update currentMax
merge the two clusters
Так эффективно Есть много ArrayList.get
участвует.
Есть ли более быстрый способ? Я, хотя с ArrayList
должен быть линейным массивом ссылок, он должен быть самым быстрым способом, и, может быть, я ничего не могу сделать, потому что есть слишком простое число get
.. но, возможно, я ошибаюсь. Я не думаю, что с помощью HashMap
может работать, так как мне нужно, чтобы получить их все на каждой итерации и map.values()
должны быть подкрепленная ArrayList
в любом случае ..
В противном случае я должен попробовать другие библиотеки коллекции, которые более оптимизированной? Как один Google, или апача один ..
EDIT:
вы несколько подтверждающие мои сомнения :(
ли я получить импульс в Perfomance пытается распараллелить вещи Может быть, используя пул? исполнители, которые вычисляют сходство более чем на одной паре, но я не знаю, закончится ли синхронизация и блокировка на структурах данных.
Сходство рассчитывается с использованием точечного произведения карт тегов двух содержимого. На картах указаны два HashMap<Tag, Float>
.. Кроме того, я уже кеширую сходства в TLongFloatHashMap
(от коллекции Trove), чтобы избежать повторного вычисления его в последующих итерациях, в которых ключ Long
вычисляется как хэш-код обоих содержимого (что уникально для пары, так что hash(c1, c2) == hash(c2, c1)
) так что все остальное уже настроено достаточно.
EDIT2:
я выложу немного коды просто, чтобы вы лучше понять .. Это используется для вычисления хэша, используемого для хранения сходства между двумя элементами:
private long computeKey(int h1, int h2) {
if (h1 < h2) {
int swap = h1;
h1 = h2;
h2 = swap;
}
return ((long)h1) << 32 | h2;
}
Это, как рассчитывается корреляция:
float correlation(Map<Tag, Float> map1, Map<Tag, Float>map2, HierarchNode n1, HierarchNode n2) {
long key = computeKey(n1.hashCode, n2.hashCode);
if (cache.contains(key)) {
++hitCounter;
return cache.get(key);
}
else {
float corr = 0.0f;
Set<Map.Entry<Tag, Float>> entries;
Map<Tag, Float> curMap;
if (map1.size() < map2.size()) {
entries = map1.entrySet();
curMap = map2;
}
else {
entries = map2.entrySet();
curMap = map1;
}
for (Map.Entry<Tag, Float> ee : entries) {
Float f2 = curMap.get(ee.getKey());
if (f2 != null)
corr += ee.getValue()*f2;
}
cache.put(key, corr);
return corr;
}
}
И это, как алгоритм сканирует содержание:
for (int j = 0; j < clusters.size(); ++j) {
skip = false;
for (int k = j+1; k < clusters.size(); ++k) {
float r = correlation(clusters.get(k).tags, clusters.get(j).tags, clusters.get(k), clusters.get(j));
if (r > max) {
max = r;
i1 = j;
i2 = k;
}
if (max == 1.0f) {
skip = true;
break;
}
}
if (skip)
break;
}
я бы использовал только матрицу, чтобы хранить все значения, но на каждой итерации наиболее похожие элементы будут удалены из списка и новый элемент добавляется (которые новая карта тегов в зависимости от двух выбранный)
Должно ли это быть 'for (int j = i + 1 to n)' ? –
@ Карл: Возможно, но не меняет O (n²) -ность алгоритма. :-( –
@ Крис: На самом деле, если он _should_ будет 'i', он может изменить это на' O (1) ', поскольку все элементы найдут свой самый похожий элемент самим собой: D –