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

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

时间:2024-03-20 10:46:05浏览次数:29  
标签:acc 增加量 float64 am Value 最小值 key time 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
}

 

标签:acc,增加量,float64,am,Value,最小值,key,time,Go
From: https://www.cnblogs.com/axibug/p/18084721

相关文章

  • 2024-03-20:用go语言,自 01背包问世之后,小 A 对此深感兴趣。 一天,小 A 去远游,却发现他的
    2024-03-20:用go语言,自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组。每组中的物品只能选择1件,现在他想知道最大的利用价值是多少?答案2024-03-20:来自左程云。灵捷3.5大体步骤如下:1.定义常量MAXN和MAXM,分别表......
  • fyne - 谁说用Go不能开发应用界面
    fyne项目介绍fyne是一个纯Golang的跨平台GUI库,跨平台库说实话,是有很多选择的,Flutter、Electron、QT等。fyne绝对不是一个很大众的选择。但是在我,一名后端程序员尝试使用Electron实现一个简单的番茄时钟,痛苦地在使用js如何在渲染进程和主进程之间传递信息,如何在客户端退......
  • Go Redis专题精讲
    GoRedis专题精讲一、介绍1.1、客户端列表go-redis提供各种类型的客户端:Redis单节点客户端Redis集群客户端Redis哨兵客户端Redis分片客户端Redis通用客户端go-redis也可以用于kvrocks,kvrocks是分布式键值NoSQL数据库,使用RocksDB作为存储引擎......
  • Django一对多、多对多的增删改查
    一对多的增删改查多对多的增删改查正反向概念书和出版社,外键字段建立在书表里那由书查出版社就是正向,而出版社查书就是反向正向:外键字段在我手里,从我查你反向:外键字段不在我手里,我查你多表查询子查询(基于对象的跨表查询)select*frompublishwhere......
  • 01 | Swoole与Go系列教程之HTTP服务的应用
    首发原文链接:Swoole与Go系列教程之HTTP服务的应用大家好,我是码农先森。写在前面PHP曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole曾经尝试填补空白,但局限性也比较的明显。Go语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转......
  • Go 文件操作-读写文件
    Go文件操作-读写文件Go读取文件整个文件读取进内存(适合读小文件)1.直接指定文件名读取os.ReadFile()ioutil.ReadFile()(在Go1.16开始,ioutil.ReadFile()就等价于os.ReadFile())packagemainimport( "fmt" "os")funcmain(){ bytes,err:=os.ReadFile(".......
  • Go语言的隐式契约:探索接口无声的实现
    在讨论Go语言的特性时,我们常常会提到一个独特之处:Go语言中并没有显式的implement关键字用于表明一个类型实现了某个接口。这一点与许多其他面向对象编程语言形成了鲜明对比,比如Java或C#中,实现接口需要明确声明。然而,Go语言采取了一种更加隐式的方式来处理接口和类型之间的关......
  • Go: 泛型及其应用详解
    在软件开发的世界里,泛型是一个强大的工具,它允许我们编写灵活且可重用的代码。对于我们这些追求成为软件架构师和系统架构师的开发者来说,深入理解并有效应用泛型是提升我们代码设计能力的关键一步。Go语言自1.18版本起正式引入了泛型功能,这一变化无疑给Go语言带来了更广阔的......
  • Django项目记录
    在线教育平台Django项目#manage.pyos.environ.setdefault("DJANGO_SETTINGS_MODULE","mxonline.settings")是什么意思?这行代码是用于设置Django项目的配置模块。在Django项目中,有一个名为settings.py的文件,其中定义了项目的各种配置选项。DJANGO_SETTINGS_MODULE是一个环境变......
  • Google Earth Engine——如何实现裁剪后研究区影像的批量下载(以NDVI为例)
    简介GEE云平台(GoogleEarthEngine)是一个强大的云平台,提供了丰富的地理数据和计算资源,用于进行地理数据分析和处理。在GEE平台上,可以实现对研究区影像的单景影像(以NDVI为例)的批量下载。下面是具体的过程:1.登录GEE云平台并初始化首先,需要登录GEE云平台(https://earthengine.g......