2011-10-04 8 views
14

Sto costruendo un sistema in Clojure che consuma eventi in tempo reale e agisce su di loro in base a quanti messaggi simili sono stati ricevuti di recente. Vorrei implementarlo utilizzando un punteggio di recency basato sul raffreddamento newtoniano.Mappa di recency in clojure usando il raffreddamento newtoniano

In altre parole, quando arriva un evento, voglio essere in grado di assegnargli un punteggio compreso tra 1.0 (mai accaduto prima, o "temperatura ambiente" nell'equazione di Newton) e 10.0 (caldo caldo, si è verificato più volte nel minuto passato).

Ho una vaga idea di come sia questa struttura dati - ogni "tipo di evento" è una chiave di mappa, e ogni valore di mappa dovrebbe contenere un certo numero di timestamp per eventi precedenti e forse una media corrente dell'attuale " calore "per quel tipo di evento, ma non riesco a capire come iniziare ad implementare oltre questo. In particolare, ho difficoltà a capire come passare dall'equazione attuale di Newton, che è molto generica, e applicarla a questo specifico scenario.

Qualcuno ha qualche indicazione? Qualcuno potrebbe suggerire un "algoritmo del punteggio di recency" più semplice per iniziare, che potrebbe essere sostituito da Newtonian che si raffredda lungo la strada?

MODIFICA: Ecco alcuni codice del clojure! Si riferisce agli eventi come lettere ma potrebbe ovviamente essere riproposto per prendere qualsiasi altro tipo di oggetto.

(ns heater.core 
    (:require [clojure.contrib.generic.math-functions :as math])) 

(def letter-recency-map (ref {})) 

(def MIN-TEMP 1.0) 
(def MAX-TEMP 10.0) 
;; Cooling time is 15 seconds 
(def COOLING-TIME 15000) 
;; Events required to reach max heat 
(def EVENTS-TO-HEAT 5.0) 

(defn temp-since [t since now] 
    (+ 
     MIN-TEMP 
     (* 
      (math/exp (/ 
       (- (- now since)) 
       COOLING-TIME)) 
      (- t MIN-TEMP)))) 

(defn temp-post-event [temp-pre-event] 
    (+ temp-pre-event 
     (/ 
      (- MAX-TEMP temp-pre-event) 
      EVENTS-TO-HEAT))) 

(defn get-letter-heat [letter] 
     (dosync 
      (let [heat-record (get (ensure letter-recency-map) letter)] 
      (if (= heat-record nil) 
       (do 
       (alter letter-recency-map conj {letter {:time (System/currentTimeMillis) :heat 1.0}}) 
       MIN-TEMP) 
       (let [now (System/currentTimeMillis) 
        new-temp-cooled (temp-since (:heat heat-record) (:time heat-record) now) 
        new-temp-event (temp-post-event new-temp-cooled)] 
        (alter letter-recency-map conj {letter {:time now :heat new-temp-event}}) 
        new-temp-event))))) 
+0

+1 per una grande domanda. Sarò interessato a vedere le risposte che ottieni. –

+0

+1. E ha appena aggiunto il tag 'algorithm'. – 4e6

risposta

5

In assenza di eventi, la soluzione all'equazione di raffreddamento è decadimento esponenziale. Dire T_0 è la temperatura all'inizio di un periodo di raffreddamento, e dt è il passo temporale (calcolata di volta del sistema o qualsiasi altra cosa) dal momento che ha valutato la temperatura da T_0:

T_no_events(dt) = T_min + (T_0 - T_min)*exp(- dt/t_cooling) 

Poiché gli eventi sono impulsi discreti, e avete una temperatura massima, si vuole un dato rapporto per evento:

T_post_event = T_pre_event + (T_max - T_pre_event)/num_events_to_heat 

Alcune note:

  • t_cooling è il momento di raffreddarsi per un fattore pari a 1/e = 1/(2.718...).

  • num_events_to_heat è il numero di eventi necessari per avere un effetto paragonabile a T_max. Probabilmente dovrebbe essere un valore positivo moderatamente ampio (ad esempio 5.0 o più?). Si noti che se num_events_to_heat==1.0, ogni evento ripristinerà la temperatura a T_max, che non è molto interessante, quindi il valore dovrebbe essere per lo meno maggiore di uno.

  • In teoria, sia il riscaldamento che il raffreddamento non dovrebbero mai raggiungere le temperature massime e minime rispettivamente (presupponendo che i parametri siano impostati come sopra e che si stia avviando da qualche parte nel mezzo). In pratica, però, la natura esponenziale del processo dovrebbe avvicinarsi abbastanza da non fare differenza ...

  • Per implementare questo, è necessario solo memorizzare il timestamp e la temperatura dell'ultimo aggiornamento. Quando si riceve un evento, eseguire una fase di raffreddamento, quindi un evento di riscaldamento e aggiornare con la nuova temperatura e il timestamp.

  • Si noti che le query di sola lettura non richiedono un aggiornamento: è sufficiente calcolare il raffreddamento dall'ultimo aggiornamento.

+0

t_cooling è una "costante di tempo": il tempo di raffreddamento del 36,8% del delta originale. Questa è un'ottima risposta. Mostra una buona conoscenza della fisica del raffreddamento di una massa puntiforme e la applica in un modo nuovo. – duffymo

Problemi correlati