在我们日常的应用中时间衰减函数无处不在,比如避免出现犹太反应(强者愈强,弱者愈弱)各种排行榜;通常我们希望某些指标如文章热度、电影评分随着时间的漂移越来越低或者随着时间的衰减出现一个系数能拟合这一过程,比较出名的就是牛顿冷却定律。
一、刚开始接触时间衰减,是在使用elasticsearch的过程中对function_score函数的使用
function_score支持的衰减函数有三种,分别是 linear、exp 和 gauss
linear、exp、gauss三种衰减函数的差别只在于衰减曲线的形状,在DSL的语法上的用法完全一样
linear : 线性函数是条直线,一旦直线与横轴0相交,所有其他值的评分都是0
exp : 指数函数是先剧烈衰减然后变缓
guass(最常用) : 高斯函数则是钟形的,他的衰减速率是先缓慢,然后变快,最后又放缓
衰减函数们 (linear、exp、gauss) 支持的参数
origin : 中心点,或是字段可能的最佳值,落在原点(origin)上的文档评分_score为满分1.0,支持数值、时间 以及 “经纬度地理座标点”(最常用) 的字段
offset : 从 origin 为中心,为他设置一个偏移量offset覆盖一个范围,在此范围内所有的评分_score也都是和origin一样满分1.0
scale : 衰减率,即是一个文档从origin下落时,_score改变的速度
decay : 从 origin 衰减到 scale 所得的评分_score,默认为0.5 (一般不需要改变,这个参数使用默认的就好了)
二、热度衰减
文章热度是时间的衰减与温度与时间的衰减规律很类似。我们可以定义一个文章有一个热度分,
当前热度分=上一期得分 x exp(-(冷却系数) x 间隔的小时数)
三、仿照Elasticsearch中的function-score函数实现
import java.text.SimpleDateFormat
import java.util.Date
object TimeDecayUtil {
/**
* exp 衰减
*
* @param date
* @return
*/
def decayExp(date: Date): Double = {
val decay = 0.5
val origin = DateUtil.getToday
val offset = 7
val scale = 173
val lamda = math.log(decay) / scale.toDouble
math.exp(lamda * math.max(0, math.abs(DateUtil.getDistanceDay(date, origin)) - offset))
}
def decayExp(dateStr: String): Double = {
val simpleDateFormat = new SimpleDateFormat("yyyyMMdd")
val date = simpleDateFormat.parse(dateStr)
val decay = 0.5
val origin = DateUtil.getToday
val offset = 7
val scale = 173
val lamda = math.log(decay) / scale.toDouble
math.exp(lamda * math.max(0, math.abs(DateUtil.getDistanceDay(date, origin)) - offset))
}
}