Go语言实现时间滑动窗口算法 动态计算增加量、最大值、最小值

// 时间窗口
var fiveMinAccMap = NewAccumulatorsMap(5 * time.Minute)
var tenMinAccMap = NewAccumulatorsMap(10 * time.Minute)
var fifteenMinAccMap = NewAccumulatorsMap(15 * time.Minute)
var thirtyMinAccMap = NewAccumulatorsMap(30 * time.Minute)
var oneHourAccMap = NewAccumulatorsMap(time.Hour)


// Accumulator 结构用于存储单个键的相关信息
type Accumulator struct {
    sync.Mutex               // 用于同步访问累加器
    window     *list.List    // 双向链表,用于存储时间窗口内的值
    total      float64       // 窗口内的总增加量
    max        float64       // 窗口内的最大值
    min        float64       // 窗口内的最小值
    windowSize time.Duration // 窗口大小
}

// NewAccumulator 创建一个新的增加量累加器实例
func NewAccumulator(windowSize time.Duration) *Accumulator {
    return &Accumulator{
        window:     list.New(),
        windowSize: windowSize,
        max:        math.Inf(-1),
        min:        math.Inf(1),
    }
}

// AddAcc 增加一个新的值到累加器
func (acc *Accumulator) AddAcc(value float64, timestamp time.Time) {
    acc.Lock()
    defer acc.Unlock()

    // 创建一个新的节点存储增加值和时间戳
    _ = acc.window.PushFront(struct {
        Value     float64
        Timestamp time.Time
    }{Value: value, Timestamp: timestamp})

    acc.total += value
    // 移除过期项(超过时间窗口的项)
    now := time.Now()
    for e := acc.window.Back(); e != nil; {
        entry := e.Value.(struct {
            Value     float64
            Timestamp time.Time
        })
        if now.Sub(entry.Timestamp) > acc.windowSize {
            acc.total -= entry.Value
            next := e.Prev()
            acc.window.Remove(e)
            e = next
        } else {
            break
        }
    }
}

// MaxAcc 增加一个新的值到累加器
func (acc *Accumulator) MaxAcc(value float64, timestamp time.Time) {
    acc.Lock()
    defer acc.Unlock()

    // 创建一个新的节点存储增加值和时间戳
    _ = acc.window.PushFront(struct {
        Value     float64
        Timestamp time.Time
    }{Value: value, Timestamp: timestamp})

    if value > acc.max {
        acc.max = value
    }

    // 移除过期项(超过时间窗口的项)
    now := time.Now()
    for e := acc.window.Back(); e != nil; {
        oldEntry := e.Value.(struct {
            Value     float64
            Timestamp time.Time
        })
        if now.Sub(oldEntry.Timestamp) > acc.windowSize {
            acc.total -= oldEntry.Value
            // 检查移除的项是否是当前最大值,如果是则需要重新计算最大值
            if oldEntry.Value == acc.max {
                acc.recalculateMaxValue()
            }
            next := e.Prev()
            acc.window.Remove(e)
            e = next
        } else {
            break
        }
    }
}

// recalculateMaxValue 重新计算当前时间窗口内的最大值
func (acc *Accumulator) recalculateMaxValue() {
    // 负无穷大
    var maxValue = math.Inf(-1)
    for e := acc.window.Front(); e != nil; e = e.Next() {
        entry := e.Value.(struct {
            Value     float64
            Timestamp time.Time
        })
        if entry.Value > maxValue {
            maxValue = entry.Value
        }
    }
    acc.max = maxValue // 更新累加器中的最大值
}

// MinAcc 增加一个新的值到累加器
func (acc *Accumulator) MinAcc(value float64, timestamp time.Time) {
    acc.Lock()
    defer acc.Unlock()

    // 创建一个新的节点存储增加值和时间戳
    _ = acc.window.PushFront(struct {
        Value     float64
        Timestamp time.Time
    }{Value: value, Timestamp: timestamp})

    if value < acc.min {
        acc.min = value
    }

    // 移除过期项(超过时间窗口的项)
    now := time.Now()
    for e := acc.window.Back(); e != nil; {
        oldEntry := e.Value.(struct {
            Value     float64
            Timestamp time.Time
        })
        if now.Sub(oldEntry.Timestamp) > acc.windowSize {
            acc.total -= oldEntry.Value
            // 检查移除的项是否是当前最大值,如果是则需要重新计算最小值
            if oldEntry.Value == acc.min {
                acc.recalculateMinValue()
            }
            next := e.Prev()
            acc.window.Remove(e)
            e = next
        } else {
            break
        }
    }
}

// recalculateMaxValue 重新计算当前时间窗口内的最小值
func (acc *Accumulator) recalculateMinValue() {
    // 正无穷大
    var minValue = math.Inf(1)
    for e := acc.window.Front(); e != nil; e = e.Next() {
        entry := e.Value.(struct {
            Value     float64
            Timestamp time.Time
        })
        if entry.Value < minValue {
            minValue = entry.Value
        }
    }
    acc.min = minValue // 更新累加器中的最小值
}

// GetMaxValue 获取当前时间窗口内的最大值
func (acc *Accumulator) GetMaxValue() float64 {
    acc.Lock()
    defer acc.Unlock()
    return acc.max
}

// GetMinValue 获取当前时间窗口内的最小值
func (acc *Accumulator) GetMinValue() float64 {
    acc.Lock()
    defer acc.Unlock()
    return acc.min
}

// GetTotalIncrease 获取当前时间窗口内的总增加量
func (acc *Accumulator) GetTotalIncrease() float64 {
    acc.Lock()
    defer acc.Unlock()
    return acc.total
}

// AccumulatorsMap 存储不同键的累加器Map
type AccumulatorsMap struct {
    sync.RWMutex
    accumulators map[string]*Accumulator
    windowSize   time.Duration
}

// NewAccumulatorsMap 创建一个新的时间窗口实例
func NewAccumulatorsMap(windowSize time.Duration) *AccumulatorsMap {
    return &AccumulatorsMap{
        accumulators: make(map[string]*Accumulator),
        windowSize:   windowSize,
    }
}

// AddAccMap 增加一个新的值到累加器
func (am *AccumulatorsMap) AddAccMap(key string, value float64) {
    am.Lock()
    acc, exists := am.accumulators[key]
    if !exists {
        acc = NewAccumulator(am.windowSize)
        am.accumulators[key] = acc
    }
    am.Unlock()

    acc.AddAcc(value, time.Now())
}

// MaxAccMap 增加一个新的值到累加器
func (am *AccumulatorsMap) MaxAccMap(key string, value float64) {
    am.Lock()
    acc, exists := am.accumulators[key]
    if !exists {
        acc = NewAccumulator(am.windowSize)
        am.accumulators[key] = acc
    }
    am.Unlock()
    acc.MaxAcc(value, time.Now())
}

// MinAccMap 增加一个新的值到累加器
func (am *AccumulatorsMap) MinAccMap(key string, value float64) {
    am.Lock()
    acc, exists := am.accumulators[key]
    if !exists {
        acc = NewAccumulator(am.windowSize)
        am.accumulators[key] = acc
    }
    am.Unlock()
    acc.MinAcc(value, time.Now())
}

// GetTotalIncrease 获取key的当前时间窗口内的总增加量
func (am *AccumulatorsMap) GetTotalIncrease(key string) float64 {
    am.RLock()
    acc, exists := am.accumulators[key]
    am.RUnlock()

    if exists {
        return acc.GetTotalIncrease()
    }
    return 0
}

// GetMax 获取key的当前时间窗口内的最大值
func (am *AccumulatorsMap) GetMax(key string) float64 {
    am.RLock()
    acc, exists := am.accumulators[key]
    am.RUnlock()

    if exists {
        return acc.GetMaxValue()
    }
    return 0
}

// GetMin 获取key的当前时间窗口内的最小值
func (am *AccumulatorsMap) GetMin(key string) float64 {
    am.RLock()
    acc, exists := am.accumulators[key]
    am.RUnlock()

    if exists {
        return acc.GetMinValue()
    }
    return 0
}

 

未经允许 禁止转载
posted @ 2024-03-20 10:37  java从精通到入门  阅读(51)  评论(0编辑  收藏  举报