您好,欢迎来到12图资源库!分享精神,快乐你我!我们只是素材的搬运工!!
  • 首 页
  • 当前位置:首页 > 开发 > WEB开发 >
    手撸Golang 基本数据结构与算法 k-means聚类算法
    时间:2021-03-03 12:32 来源:网络整理 作者:网络 浏览:收藏 挑错 推荐 打印

    本系列笔记拟采用golang练习之

    k-means聚类算法

    聚类就是在输入为多个数据时, 将“相似”的数据分为一组的操作。 k-means算法是聚类算法中的一种。 首先随机选择k个点作为簇的中心点, 然后重复执行“将数据分到相应的簇中”和 “将中心点移到重心的位置”这两个操作, 直到中心点不再发作变化为止。 k-means算法中,随着操作的不断重复, 中心点的位置必定会在某处收敛, 这一点曾经在数学层面上失掉证明。 摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一

    场景

    某地突然迸发新冠疫情, 现防疫急需依据病例散布, 查找能够的病源地

    首先将病例散布的坐标, 录入系统

    然后依据k-means算法, 按k从1到3, 辨别停止聚类

    聚类的中心点, 能够就是病源地

    手撸Golang 基本数据结构与算法 k-means聚类算法

    流程

    给定若干样本, 和样本距离计算器, 需求求解k个样本中心点

    首先从样本中随机抽取k个点, 作为中心点

    循环每个样本

    辨别计算样本点到k个中心点的距离

    判别距离样本点最小的中心点

    将样本划分到该最小中心点的簇

    计算每个簇的中心点, 作为新的中心点

    循环簇中的每个样本

    计算该样本, 到本簇其他样本的距离之和

    与其他样本的距离和最小的点, 就是新的中心点

    重复3-4, 直到中心点不再变化, 计算终了

    设计

    IPoint: 样本点接口, 其实是一个空接口

    IDistanceCalculator: 距离计算器接口

    IClassifier: 分类器接口, 将samples聚类成k个, 并前往k个中心点

    tPerson: 病例样本点, 完成IPoint接口, 含x,y坐标

    tPersonDistanceCalculator: 病例距离计算器, 计算两点间x,y坐标的直线距离

    tKMeansClassifier: k-means聚类器, 完成IClassifier接口.

    单元测试

    k_means_test.go

    package others 

     

    import ( 

        km "learning/gooop/others/k_means" 

        "strings" 

        "testing" 

     

    func Test_KMeans(t *testing.T) { 

        // 创立样本点 

        samples := []km.IPoint { 

            km.NewPerson(211), 

            km.NewPerson(28), 

            km.NewPerson(26), 

     

            km.NewPerson(312), 

            km.NewPerson(310), 

     

            km.NewPerson(47), 

            km.NewPerson(43), 

     

            km.NewPerson(511), 

            km.NewPerson(59), 

            km.NewPerson(52), 

     

            km.NewPerson(79), 

            km.NewPerson(76), 

            km.NewPerson(73), 

     

            km.NewPerson(812), 

     

            km.NewPerson(93), 

            km.NewPerson(95), 

            km.NewPerson(910), 

     

            km.NewPerson(103), 

            km.NewPerson(106), 

            km.NewPerson(1012), 

     

            km.NewPerson(119), 

        } 

     

        fnPoints2String := func(points []km.IPoint) string { 

            items := make([]string, len(points)) 

            for i,it := range points { 

                items[i] = it.String() 

            } 

            return strings.Join(items, " "

        } 

     

        for k:=1;k<=3;k++ { 

            centers := km.KMeansClassifier.Classify(samples, km.PersonDistanceCalculator, k) 

            t.Log(fnPoints2String(centers)) 

        } 

    测试输入

    $ go test -v k_means_test.go  

    === RUN   Test_KMeans 

        k_means_test.go:53: p(7,6

        k_means_test.go:53: p(5,9) p(7,3

        k_means_test.go:53: p(9,10) p(3,10) p(7,3

    --- PASS: Test_KMeans (0.00s) 

    PASS 

    ok      command-line-arguments  0.002s 

    IPoint.go

    样本点接口, 其实是一个空接口

    package km 

     

    import "fmt" 

     

    type IPoint interface { 

        fmt.Stringer 

    IDistanceCalculator.go

    距离计算器接口

    package km 

     

    type IDistanceCalculator interface { 

        Calc(a, b IPoint) int 

    IClassifier.go

    分类器接口, 将samples聚类成k个, 并前往k个中心点

    package km 

     

    type IClassifier interface { 

        // 将samples聚类成k个, 并前往k个中心点 

        Classify(samples []IPoint, calc IDistanceCalculator, k int) []IPoint 

    tPerson.go

    病例样本点, 完成IPoint接口, 含x,y坐标

    package km 

     

    import "fmt" 

     

    type tPerson struct { 

        x int 

        y int 

     

    func NewPerson(x, y int) IPoint { 

        return &tPerson{x, y, } 

     

    func (me *tPerson) String() string { 

        return fmt.Sprintf("p(%v,%v)", me.x, me.y) 

    tPersonDistanceCalculator.go

    病例距离计算器, 计算两点间x,y坐标的直线距离

    package km 

     

     

    type tPersonDistanceCalculator struct { 

     

    var gMaxInt = 0x7fffffff_ffffffff 

     

    func newPersonDistanceCalculator() IDistanceCalculator { 

        return &tPersonDistanceCalculator{} 

     

    func (me *tPersonDistanceCalculator) Calc(a, b IPoint) int { 

        if a == b { 

            return 0 

        } 

     

        p1, ok := a.(*tPerson) 

        if !ok { 

            return gMaxInt 

        } 

     

        p2, ok := b.(*tPerson) 

        if !ok { 

            return gMaxInt 

        } 

     

        dx := p1.x - p2.x 

        dy := p1.y - p2.y 

     

        d := dx*dx + dy*dy 

        if d < 0 { 

            panic(d) 

        } 

        return d 

     

    var PersonDistanceCalculator = newPersonDistanceCalculator() 

    tKMeansClassifier.go

    k-means聚类器, 完成IClassifier接口.

    package km 

     

    import ( 

        "math/rand" 

        "time" 

     

    type tKMeansClassifier struct { 

     

    type tPointEntry struct { 

        point IPoint 

        distance int 

        index int 

     

    func newPointEntry(p IPoint, d int, i int) *tPointEntry { 

        return &tPointEntry{ 

            p, d, i, 

        } 

     

    func newKMeansClassifier() IClassifier { 

        return &tKMeansClassifier{} 

     

    // 将samples聚类成k个, 并前往k个中心点 

    func (me *tKMeansClassifier) Classify(samples []IPoint, calc IDistanceCalculator, k int) []IPoint { 

        sampleCount := len(samples) 

        if sampleCount <= k { 

            return samples 

        } 

     

        // 初始化, 随机选择k个中心点 

        rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 

        centers := make([]IPoint, k) 

        for selected, i:= make(map[int]bool, 0), 0;i < k; { 

            n := rnd.Intn(sampleCount) 

            _,ok := selected[n] 

     

            if !ok { 

                selected[n] = true 

                centers[i] = samples[n] 

                i++ 

            } 

        } 

     

     

        // 依据到中心点的距离, 划分samples 

        for { 

            groups := me.split(samples, centers, calc) 

     

            newCenters := make([]IPoint, k) 

            for i,g := range groups { 

                newCenters[i] = me.centerOf(g, calc) 

            } 

     

            if me.groupEquals(centers, newCenters) { 

                return centers 

            } 

            centers = newCenters 

        } 

     

    // 将样本点距离中心点的距离停止分簇 

    func (me *tKMeansClassifier) split(samples []IPoint, centers []IPoint, calc IDistanceCalculator) [][]IPoint { 

        k := len(centers) 

        result := make([][]IPoint, k) 

        for i := 0;i<k;i++ { 

            result[i] = make([]IPoint, 0

        } 

     

        entries := make([]*tPointEntry, k) 

        for i,c := range centers { 

            entries[i] = newPointEntry(c, 0, i) 

        } 

     

        for _,p := range samples { 

            for _,e := range entries { 

                e.distance = calc.Calc(p, e.point) 

            } 

     

            center := me.min(entries) 

            result[center.index] = append(result[center.index], p) 

        } 

     

        return result 

     

    // 计算一簇样本的重心. 重心就是距离各点的总和最小的点 

    func (me *tKMeansClassifier) centerOf(samples []IPoint, calc IDistanceCalculator) IPoint { 

        entries := make([]*tPointEntry, len(samples)) 

        for i,src := range samples { 

            distance := 0 

            for _,it := range samples { 

                distance += calc.Calc(src, it) 

            } 

            entries[i] = newPointEntry(src, distance, i) 

        } 

     

        return me.min(entries).point 

     

    // 判别两组点能否相反 

    func (me *tKMeansClassifier) groupEquals(g1, g2 []IPoint) bool { 

        if len(g1) != len(g2) { 

            return false 

        } 

     

        for i,v := range g1 { 

            if g2[i] != v { 

                return false 

            } 

        } 

     

        return true 

     

    // 查找距离最小的点 

    func (me *tKMeansClassifier) min(entries []*tPointEntry) *tPointEntry { 

        minI := 0 

        minD := gMaxInt 

        for i,it := range entries { 

            if it.distance < minD { 

                minI = i 

                minD = it.distance 

            } 

        } 

     

        return entries[minI] 

     

     

    var KMeansClassifier = newKMeansClassifier() 

    【编辑引荐】

    最接地气的数据剖析详细流程,看这篇就够了!

    趣说“容器技术”,女冤家让我白话它的精彩故事!

    面向数据中心基础设备管理的5个关键物联网运用

    大数据的关键技术有哪些?

    从美国继续网络训练环境(PCTE),看国际网络靶场技术实际

    (责任编辑:admin)