Быстрый способ подсчитать, как часто пары появляются в Clojure 1.4

Мне нужно посчитать, сколько раз определенные пары целых чисел встречаются в каком-либо процессе (эвристика, обнаруживающая похожие изображения с использованием хеширования, чувствительного к локальности - целые числа представляют изображения, а нижеприведенные «соседи» - это изображения, имеющие одинаковое значение хэша, поэтому количество указывает, сколько разных хэшей соединяют данную пару изображений).

Счет хранится в виде карты из (упорядоченной) пары для подсчета (matches ниже).

Вход (nbrs-list ниже) представляет собой список из целых чисел, которые считаются соседями, где каждая отдельная (упорядоченная) пара во внутреннем списке («соседи») должна быть подсчитана. Так, например, еслиnbrs-list является[[1,2,3],[10,8,9]] тогда пары[1,2],[1,3],[2,3],[8,9],[8,10],[9,10].

Рутинаcollect вызывается несколько раз;matches параметр накапливает результаты иnbrs-list новый на каждый звонок. Наименьшее количество соседей (размер внутреннего списка) равно 1, а наибольшее ~ 1000. Каждое целое число вnbrs-list происходит только один раз при любом вызовеcollect (this implies that, on each call, no pair occurs more than once) и целые числа охватывают диапазон от 0 до 1 000 000 (поэтому размерnbrs-list меньше 1 000 000 - поскольку каждое значение встречается только один раз, а иногда они встречаются в группах (но обычно больше 100 000 - поскольку большинство изображений не имеют соседей).

Я вытащил подпрограммы из большей части кода, поэтому они могут содержать небольшие ошибки редактирования, извините.

(defn- flatten-1
  [list]
  (apply concat list))

(defn- pairs
  ([nbrs]
    (let [nbrs (seq nbrs)]
      (if nbrs (pairs (rest nbrs) (first nbrs) (rest nbrs)))))
  ([nbrs a bs]
    (lazy-seq
      (let [bs (seq bs)]
        (if bs
          (let [b (first bs)]
            (cons (if (> a b) [[b a] 1] [[a b] 1]) (pairs nbrs a (rest bs))))
          (pairs nbrs))))))

(defn- pairs-map
  [nbrs]
  (println (count nbrs))
  (let [pairs-list (flatten-1 (pairs nbrs))]
    (apply hash-map pairs-list)))

(defn- collect
  [matches nbrs-list]
  (let [to-add (map pairs-map nbrs-list)]
    (merge-with + matches (apply (partial merge-with +) to-add))))

Таким образом, приведенный выше код расширяет каждый набор соседей до упорядоченных пар; создает карту из пар в1; затем объединяет карты, используя сложение значений.

Мне бы хотелось, чтобы это работало быстрее. Я не вижу, как избежать расширения пар O (n ^ 2), но я думаю, что могу, по крайней мере, уменьшить постоянные издержки, избегая так много промежуточных структур. В то же время я хотел бы, чтобы код был достаточно компактным и читабельным ...

О, и теперь я превышаю "предел накладных расходов GC". Поэтому сокращение использования памяти / оттока также является приоритетом: о)

[Может быть, это слишком конкретно? Я надеюсь, что уроки носят общий характер и, похоже, не так много сообщений об оптимизации "реальной" ситуации. закрытый код. Кроме того, я могу профилировать и т. Д., Но мой код кажется таким уродливым, что я надеюсь, что есть очевидный, более чистый подход - особенно для расширения пар.]

Ответы на вопрос(4)

Ваш ответ на вопрос