Go語言實現時間滑動視窗演算法 動態計算增加量、最大值、最小值

java从精通到入门發表於2024-03-20
// 時間視窗
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
}

相關文章