最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

GO?CountMinSketch計數(shù)器(布隆過濾器思想的近似計數(shù)器)

 更新時間:2022年09月14日 08:34:00   作者:jiaxwu??????  
這篇文章主要介紹了GO?CountMinSketch計數(shù)器(布隆過濾器思想的近似計數(shù)器),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下

簡介

CountMinSketch是一種計數(shù)器,用來統(tǒng)計一個元素的計數(shù),它能夠以一個非常小的空間統(tǒng)計大量元素的計數(shù),同時保證高的性能及準確性。

與布隆過濾器類似,由于它是基于概率的,因此它所統(tǒng)計的計數(shù)是有一定概率存在誤差的,也就是可能會比真實的計數(shù)大。比如一個元素實際的計數(shù)是10,但是計算器的計算結(jié)果可能比10大。因此適合能夠容忍計數(shù)存在一定誤差的場景,比如社交網(wǎng)絡(luò)中推文的訪問次數(shù)。

它一秒能夠進行上百萬次操作(主要取決于哈希函數(shù)的速度),并且如果我們每天有一個長度為100億的數(shù)據(jù)流需要進行計數(shù),計數(shù)值允許的誤差范圍是100,允許的錯誤率是0.1%,計數(shù)器大小是32位,只需要7.2GB內(nèi)存,這完全可以單機進行計數(shù)。

原理

數(shù)據(jù)結(jié)構(gòu)

CountMinSketch計數(shù)器的數(shù)據(jù)結(jié)構(gòu)是一個二維數(shù)組,每一個元素都是一個計數(shù)器,計數(shù)器可以使用一個數(shù)值類型進行表示,比如無符號int

增加計數(shù)

每個元素會通過不同的哈希函數(shù)映射到每一行的某個位置,并增加對應(yīng)位置上的計數(shù):

估算計數(shù)

估算計數(shù)也是如上圖流程,根據(jù)哈希映射到每一行的對應(yīng)位置,然后讀取所有行的計數(shù),返回其中最小的一個。

返回最小的一個是因為其他其他元素也可能會映射到自身所映射位置上面,導(dǎo)致計數(shù)比真實計數(shù)大,因此最小的一個計數(shù)最可能是真實計數(shù):

比如上圖元素123映射到了元素abc第一行的相同位置,因此這個位置的計數(shù)累加了元素abc和元素123的計數(shù)和。但是只要我們?nèi)∪欣锩孀钚〉囊粋€計數(shù),那么就能容忍這種情況。

當然,如果一個元素的每一行的對應(yīng)位置都被其他元素所映射,那么這個估算的計數(shù)就會比真實計數(shù)大。

哈希函數(shù)

CountMinSketch計數(shù)器里面的哈希函數(shù)需要是彼此獨立且均勻分布(類似于哈希表的哈希函數(shù)),而且需要盡可能的快,比如murmur3就是一個很好的選擇。

CountMinSketch計數(shù)器的性能嚴重依賴于哈希函數(shù)的性能,而一般哈希函數(shù)的性能則依賴于輸入串(一般為字節(jié)數(shù)組)的長度,因此為了提高CountMinSketch計數(shù)器的性能建議減少輸入串的長度。

下面是一個簡單的性能測試,單位是字節(jié),可以看到時間的消耗隨著元素的增大基本是線性增長的:

pkg: github.com/jiaxwu/gommon/counter/cm
cpu: Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
BenchmarkAddAndEstimate/1-8              2289142               505.9 ns/op         1.98 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/2-8              2357380               513.7 ns/op         3.89 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/4-8              2342382               496.9 ns/op         8.05 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/8-8              2039792               499.7 ns/op        16.01 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/16-8             2350281               526.8 ns/op        30.37 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/32-8             2558060               444.3 ns/op        72.03 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/64-8             2540272               459.5 ns/op       139.29 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/128-8            1919720               538.6 ns/op       237.67 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/256-8            1601738               720.6 ns/op       355.28 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/512-8             950584              1599 ns/op         320.18 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/1024-8            363592              3169 ns/op         323.17 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/2048-8            187500              5888 ns/op         347.81 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/4096-8            130425              8825 ns/op         464.15 MB/s           0 B/op          0 allocs/op
BenchmarkAddAndEstimate/8192-8             67198             17460 ns/op         469.18 MB/s           0 B/op          0 allocs/op

數(shù)組大小、哈希函數(shù)數(shù)量、錯誤范圍、錯誤率

數(shù)組大小、哈希函數(shù)數(shù)量、錯誤范圍和錯誤率之間是互相影響的,如果我們想減少錯誤率和錯誤范圍,則需要更大的數(shù)組和更多的哈希函數(shù)。但是我們很難直觀的計算出這些參數(shù),還好有兩個公式可以幫助我們計算出準確的數(shù)值:

在我們可以確定我們的數(shù)據(jù)流大小和能夠容忍的錯誤范圍錯誤率的情況下,我們可以根據(jù)下面公式計算數(shù)組大小哈希函數(shù)數(shù)量

n = 數(shù)據(jù)流大小 
m = 數(shù)組大小
k = 哈希函數(shù)數(shù)量 
eRange = 錯誤范圍(ErrorRange)
eRate = 錯誤率(ErrorRate)
ceil() = 向上取整操作
E = 2.718281828459045(自然常數(shù))

m = ceil(E/(eRange/n))
k = ceil(ln(1/eRate))

應(yīng)用

TopK(海量數(shù)據(jù)計數(shù)器)

對于海量數(shù)據(jù)流中頻率最高的K個數(shù),如果使用常規(guī)的map<key, uint>,由于內(nèi)存大小限制,一般情況下單機無法完成計算,需要把數(shù)據(jù)路由到多臺機器上進行計數(shù)。

而如果我們使用CountMinSketch則能夠在單機情況下處理大量的數(shù)據(jù),比如開頭所提到對于一個長度為100億的數(shù)據(jù)流進行計數(shù),只需要7.2GB內(nèi)存。這個計數(shù)結(jié)果可能存在一定誤差,不過我們可以在這個基礎(chǔ)上再進行過濾。

TinyLFU

TinyLFU是一個緩存淘汰策略,它里面有LFU策略的思想,LFU是一個基于訪問頻率的淘汰策略,因此需要統(tǒng)計每個元素被訪問的次數(shù)。如果對每個元素使用一個獨立的計數(shù)器,那么這個成本會很大,而且對于一個緩存淘汰策略來說,我們并不需要這個計數(shù)器非常大且非常準確。

因此TinyLFU使用一個計數(shù)器長度為4位的CountMinSketch計數(shù)器統(tǒng)計每個元素的頻率,減少計數(shù)所消耗的內(nèi)存空間,同時還引入了計數(shù)衰減機制避免某些之前熱門但是當前已經(jīng)很少被訪問的元素很難被淘汰。

實現(xiàn)

這里給出一個Golang的泛型實現(xiàn),這個實現(xiàn)支持uint8、uint16、uint32、uint64等基本類型計數(shù)器,實際上還可以實現(xiàn)比如長度為2bit、4bit、6bit的計數(shù)器,但是代碼會稍微復(fù)雜一點(特別是非2的次方的計數(shù)器)。

package cm
import (
	"math"

	"github.com/jiaxwu/gommon/hash"
	mmath "github.com/jiaxwu/gommon/math"
	"github.com/jiaxwu/gommon/mem"
	"golang.org/x/exp/constraints"
)

// Count-Min Sketch 計數(shù)器,原理類似于布隆過濾器,根據(jù)哈希映射到多個位置,然后在對應(yīng)位置進行計數(shù)
// 讀取時拿對應(yīng)位置最小的
// 適合需要一個比較小的計數(shù),而且不需要這個計數(shù)一定準確的情況
// 可以減少空間消耗
// https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.591.8351&rep=rep1&type=pdf
type Counter[T constraints.Unsigned] struct {
	counters    [][]T
	countersLen uint64       // 計數(shù)器長度
	hashs       []*hash.Hash // 哈希函數(shù)列表
	maxCount    T            // 最大計數(shù)值
}

// 創(chuàng)建一個計數(shù)器
// size:數(shù)據(jù)流大小
// errorRange:計數(shù)值誤差范圍(會超過真實計數(shù)值)
// errorRate:錯誤率
func New[T constraints.Unsigned](size uint64, errorRange T, errorRate float64) *Counter[T] {
	// 計數(shù)器長度
	countersLen := uint64(math.Ceil(math.E / (float64(errorRange) / float64(size))))
	// 哈希個數(shù)
	hashsCnt := int(math.Ceil(math.Log(1.0 / errorRate)))
	hashs := make([]*hash.Hash, hashsCnt)
	counters := make([][]T, hashsCnt)
	for i := 0; i < hashsCnt; i++ {
		hashs[i] = hash.New()
		counters[i] = make([]T, countersLen)
	}
	return &Counter[T]{
		counters:    counters,
		countersLen: countersLen,
		hashs:       hashs,
		maxCount:    T(0) - 1,
	}
}

// 增加元素的計數(shù)
func (c *Counter[T]) Add(b []byte, val T) {
	for i, h := range c.hashs {
		index := h.Sum64(b) % c.countersLen
		if c.counters[i][index]+val <= c.counters[i][index] {
			c.counters[i][index] = c.maxCount
		} else {
			c.counters[i][index] += val
		}
	}
}

// 增加元素的計數(shù)
// 等同于Add(b, 1)
func (c *Counter[T]) Inc(b []byte) {
	c.Add(b, 1)
}

// 增加元素的計數(shù)
// 字符串類型
func (c *Counter[T]) AddString(s string, val T) {
	c.Add([]byte(s), val)
}

// 增加元素的計數(shù)
// 等同于Add(b, 1)
// 字符串類型
func (c *Counter[T]) IncString(s string) {
	c.Add([]byte(s), 1)
}

// 估算元素的計數(shù)
func (c *Counter[T]) Estimate(b []byte) T {
	minCount := c.maxCount
	for i, h := range c.hashs {
		index := h.Sum64(b) % c.countersLen
		count := c.counters[i][index]
		if count == 0 {
			return 0
		}
		minCount = mmath.Min(minCount, count)
	}
	return minCount
}

// 估算元素的計數(shù)
// 字符串類型
func (c *Counter[T]) EstimateString(s string) T {
	return c.Estimate([]byte(s))
}

// 計數(shù)衰減
// 如果factor為0則直接清空
func (c *Counter[T]) Attenuation(factor T) {
	for _, counter := range c.counters {
		if factor == 0 {
			mem.Memset(counter, 0)
		} else {
			for j := uint64(0); j < c.countersLen; j++ {
				counter[j] /= factor
			}
		}
	}
}

數(shù)據(jù)結(jié)構(gòu)

這里的數(shù)據(jù)結(jié)構(gòu)核心是一個k*m的二維數(shù)組counters,k是哈希函數(shù)數(shù)量,m是數(shù)組每一行的長度;countersLen其實就是m;hashs是哈希函數(shù)列表;maxCount是當前類型的最大值,比如uint8就是255,下面的計算需要用到它。

type Counter[T constraints.Unsigned] struct {
	counters    [][]T
	countersLen uint64       // 計數(shù)器長度
	hashs       []*hash.Hash // 哈希函數(shù)列表
	maxCount    T            // 最大計數(shù)值
}

初始化

我們首先使用上面提到的兩個公式計算數(shù)組每一行長度和哈希函數(shù)的數(shù)量,然后初始化哈希函數(shù)列表和二維數(shù)組。

// 創(chuàng)建一個計數(shù)器
// size:數(shù)據(jù)流大小
// errorRange:計數(shù)值誤差范圍(會超過真實計數(shù)值)
// errorRate:錯誤率
func New[T constraints.Unsigned](size uint64, errorRange T, errorRate float64) *Counter[T] {
	// 計數(shù)器長度
	countersLen := uint64(math.Ceil(math.E / (float64(errorRange) / float64(size))))
	// 哈希個數(shù)
	hashsCnt := int(math.Ceil(math.Log(1.0 / errorRate)))
	hashs := make([]*hash.Hash, hashsCnt)
	counters := make([][]T, hashsCnt)
	for i := 0; i < hashsCnt; i++ {
		hashs[i] = hash.New()
		counters[i] = make([]T, countersLen)
	}
	return &Counter[T]{
		counters:    counters,
		countersLen: countersLen,
		hashs:       hashs,
		maxCount:    T(0) - 1,
	}
}

增加計數(shù)

對于一個元素,我們需要把它根據(jù)每個哈希函數(shù)計算出它在每一行數(shù)組的位置,然后增加對應(yīng)位置計數(shù)器的計數(shù)值。

這里需要注意的是,計數(shù)值可能會溢出,因此我們首先判斷是否溢出,如果溢出則設(shè)置為最大值。

// 增加元素的計數(shù)
func (c *Counter[T]) Add(b []byte, val T) {
	for i, h := range c.hashs {
		index := h.Sum64(b) % c.countersLen
		if c.counters[i][index]+val <= c.counters[i][index] {
			c.counters[i][index] = c.maxCount
		} else {
			c.counters[i][index] += val
		}
	}
}

估算計數(shù)

同增加計數(shù)原理,把元素根據(jù)哈希函數(shù)映射到每一行數(shù)組的對應(yīng)位置,然后選擇所有行中最小的那個計數(shù)值。

// 估算元素的計數(shù)
func (c *Counter[T]) Estimate(b []byte) T {
	minCount := c.maxCount
	for i, h := range c.hashs {
		index := h.Sum64(b) % c.countersLen
		count := c.counters[i][index]
		if count == 0 {
			return 0
		}
		minCount = mmath.Min(minCount, count)
	}
	return minCount
}

到此這篇關(guān)于GO CountMinSketch計數(shù)器(布隆過濾器思想的近似計數(shù)器)的文章就介紹到這了,更多相關(guān)GO CountMinSketch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于go語言載入json可能遇到的一個坑

    關(guān)于go語言載入json可能遇到的一個坑

    Go 語言從新手到大神,每個人多少都會踩一些坑,那么下面這篇文章主要給大家介紹了關(guān)于go語言載入json可能遇到的一個坑,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-07-07
  • Go語言基礎(chǔ)switch條件語句基本用法及示例詳解

    Go語言基礎(chǔ)switch條件語句基本用法及示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)switch條件語句基本用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2021-11-11
  • golang redis中Pipeline通道的使用詳解

    golang redis中Pipeline通道的使用詳解

    本文主要介紹了golang redis中Pipeline通道的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    Golang?依賴注入經(jīng)典解決方案uber/fx理論解析

    這篇文章主要為大家介紹了Golang依賴注入經(jīng)典解決方案uber/fx理論解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計

    Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計

    在一些常見的業(yè)務(wù)場景中可能涉及到用戶的手機號,銀行卡號等敏感數(shù)據(jù),對于這部分的數(shù)據(jù)經(jīng)常需要進行數(shù)據(jù)脫敏處理,就是將此部分數(shù)據(jù)隱私化,防止數(shù)據(jù)泄露,所以本文給大家介紹了Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計,需要的朋友可以參考下
    2024-05-05
  • Go高級特性探究之協(xié)程池詳解

    Go高級特性探究之協(xié)程池詳解

    在并發(fā)編程中,協(xié)程是?Go?語言的核心特性之一,本文將介紹如何使用?Go?協(xié)程池構(gòu)造一個協(xié)程池,并解決函數(shù)傳參問題、優(yōu)雅關(guān)閉協(xié)程池和保證協(xié)程安全的問題,感興趣的可以了解一下
    2023-06-06
  • goland 設(shè)置注釋模板的過程圖文詳解

    goland 設(shè)置注釋模板的過程圖文詳解

    這篇文章主要介紹了goland 設(shè)置注釋模板的過程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-12-12
  • go?goth封裝第三方認證庫示例詳解

    go?goth封裝第三方認證庫示例詳解

    這篇文章主要為大家介紹了go?goth封裝第三方認證庫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Golang中panic的異常處理

    Golang中panic的異常處理

    本文主要介紹了Golang中panic的異常處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • 詳解如何修改Go結(jié)構(gòu)體的私有字段

    詳解如何修改Go結(jié)構(gòu)體的私有字段

    在 Go 語言中,結(jié)構(gòu)體字段的訪問權(quán)限是由字段名的首字母決定的:首字母大寫表示公共字段(public),首字母小寫表示私有字段(private),本文給大家介紹了如何修改Go結(jié)構(gòu)體的私有字段,需要的朋友可以參考下
    2025-01-01

最新評論

汝城县| 玉树县| 洞口县| 嘉善县| 张家界市| 漾濞| 兰考县| 西盟| 台南市| 巴青县| 裕民县| 阳原县| 海兴县| 平塘县| 克山县| 巴青县| 青浦区| 乌苏市| 永城市| 石景山区| 崇信县| 平昌县| 句容市| 锡林郭勒盟| 文水县| 荆门市| 阳山县| 山东省| 南和县| 屏南县| 夏河县| 织金县| 东辽县| 揭东县| 汶川县| 同仁县| 都兰县| 神池县| 凉城县| 保德县| 绥滨县|