自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手?jǐn)]Golang 基本數(shù)據(jù)結(jié)構(gòu)與算法 k-means聚類算法

開發(fā) 前端 算法
最近閱讀<<我的第一本算法書>>(【日】石田保輝;宮崎修一)本系列筆記擬采用golang練習(xí)之k-means聚類算法。

緣起

最近閱讀<<我的第一本算法書>>(【日】石田保輝;宮崎修一)

本系列筆記擬采用golang練習(xí)之

k-means聚類算法

聚類就是在輸入為多個(gè)數(shù)據(jù)時(shí), 將“相似”的數(shù)據(jù)分為一組的操作。 k-means算法是聚類算法中的一種。 首先隨機(jī)選擇k個(gè)點(diǎn)作為簇的中心點(diǎn), 然后重復(fù)執(zhí)行“將數(shù)據(jù)分到相應(yīng)的簇中”和 “將中心點(diǎn)移到重心的位置”這兩個(gè)操作, 直到中心點(diǎn)不再發(fā)生變化為止。 k-means算法中,隨著操作的不斷重復(fù), 中心點(diǎn)的位置必定會在某處收斂, 這一點(diǎn)已經(jīng)在數(shù)學(xué)層面上得到證明。 摘自 <<我的第一本算法書>> 【日】石田保輝;宮崎修一

場景

  • 某地突然爆發(fā)新冠疫情, 現(xiàn)防疫急需根據(jù)病例分布, 查找可能的病源地
  • 首先將病例分布的坐標(biāo), 錄入系統(tǒng)
  • 然后根據(jù)k-means算法, 按k從1到3, 分別進(jìn)行聚類
  • 聚類的中心點(diǎn), 可能就是病源地

流程

  1. 給定若干樣本, 和樣本距離計(jì)算器, 需要求解k個(gè)樣本中心點(diǎn)
  2. 首先從樣本中隨機(jī)抽取k個(gè)點(diǎn), 作為中心點(diǎn)
  3. 循環(huán)每個(gè)樣本

    1. 分別計(jì)算樣本點(diǎn)到k個(gè)中心點(diǎn)的距離
    2. 判斷距離樣本點(diǎn)最小的中心點(diǎn)
    3. 將樣本劃分到該最小中心點(diǎn)的簇
  4. 計(jì)算每個(gè)簇的中心點(diǎn), 作為新的中心點(diǎn)

    1. 循環(huán)簇中的每個(gè)樣本
    2. 計(jì)算該樣本, 到本簇其他樣本的距離之和
    3. 與其他樣本的距離和最小的點(diǎn), 就是新的中心點(diǎn)
  5. 重復(fù)3-4, 直到中心點(diǎn)不再變化, 計(jì)算完畢

設(shè)計(jì)

  • IPoint: 樣本點(diǎn)接口, 其實(shí)是一個(gè)空接口
  • IDistanceCalculator: 距離計(jì)算器接口
  • IClassifier: 分類器接口, 將samples聚類成k個(gè), 并返回k個(gè)中心點(diǎn)
  • tPerson: 病例樣本點(diǎn), 實(shí)現(xiàn)IPoint接口, 含x,y坐標(biāo)
  • tPersonDistanceCalculator: 病例距離計(jì)算器, 計(jì)算兩點(diǎn)間x,y坐標(biāo)的直線距離
  • tKMeansClassifier: k-means聚類器, 實(shí)現(xiàn)IClassifier接口.

單元測試

k_means_test.go

  1. package others 
  2.  
  3. import ( 
  4.     km "learning/gooop/others/k_means" 
  5.     "strings" 
  6.     "testing" 
  7.  
  8. func Test_KMeans(t *testing.T) { 
  9.     // 創(chuàng)建樣本點(diǎn) 
  10.     samples := []km.IPoint { 
  11.         km.NewPerson(211), 
  12.         km.NewPerson(28), 
  13.         km.NewPerson(26), 
  14.  
  15.         km.NewPerson(312), 
  16.         km.NewPerson(310), 
  17.  
  18.         km.NewPerson(47), 
  19.         km.NewPerson(43), 
  20.  
  21.         km.NewPerson(511), 
  22.         km.NewPerson(59), 
  23.         km.NewPerson(52), 
  24.  
  25.         km.NewPerson(79), 
  26.         km.NewPerson(76), 
  27.         km.NewPerson(73), 
  28.  
  29.         km.NewPerson(812), 
  30.  
  31.         km.NewPerson(93), 
  32.         km.NewPerson(95), 
  33.         km.NewPerson(910), 
  34.  
  35.         km.NewPerson(103), 
  36.         km.NewPerson(106), 
  37.         km.NewPerson(1012), 
  38.  
  39.         km.NewPerson(119), 
  40.     } 
  41.  
  42.     fnPoints2String := func(points []km.IPoint) string { 
  43.         items := make([]string, len(points)) 
  44.         for i,it := range points { 
  45.             items[i] = it.String() 
  46.         } 
  47.         return strings.Join(items, " "
  48.     } 
  49.  
  50.     for k:=1;k<=3;k++ { 
  51.         centers := km.KMeansClassifier.Classify(samples, km.PersonDistanceCalculator, k) 
  52.         t.Log(fnPoints2String(centers)) 
  53.     } 

測試輸出

  1. $ go test -v k_means_test.go  
  2. === RUN   Test_KMeans 
  3.     k_means_test.go:53: p(7,6
  4.     k_means_test.go:53: p(5,9) p(7,3
  5.     k_means_test.go:53: p(9,10) p(3,10) p(7,3
  6. --- PASS: Test_KMeans (0.00s) 
  7. PASS 
  8. ok      command-line-arguments  0.002s 

IPoint.go

樣本點(diǎn)接口, 其實(shí)是一個(gè)空接口

  1. package km 
  2.  
  3. import "fmt" 
  4.  
  5. type IPoint interface { 
  6.     fmt.Stringer 

IDistanceCalculator.go

距離計(jì)算器接口

  1. package km 
  2.  
  3. type IDistanceCalculator interface { 
  4.     Calc(a, b IPoint) int 

IClassifier.go

分類器接口, 將samples聚類成k個(gè), 并返回k個(gè)中心點(diǎn)

  1. package km 
  2.  
  3. type IClassifier interface { 
  4.     // 將samples聚類成k個(gè), 并返回k個(gè)中心點(diǎn) 
  5.     Classify(samples []IPoint, calc IDistanceCalculator, k int) []IPoint 

tPerson.go

病例樣本點(diǎn), 實(shí)現(xiàn)IPoint接口, 含x,y坐標(biāo)

  1. package km 
  2.  
  3. import "fmt" 
  4.  
  5. type tPerson struct { 
  6.     x int 
  7.     y int 
  8.  
  9. func NewPerson(x, y int) IPoint { 
  10.     return &tPerson{x, y, } 
  11.  
  12. func (me *tPerson) String() string { 
  13.     return fmt.Sprintf("p(%v,%v)", me.x, me.y) 

tPersonDistanceCalculator.go

病例距離計(jì)算器, 計(jì)算兩點(diǎn)間x,y坐標(biāo)的直線距離

  1. package km 
  2.  
  3.  
  4. type tPersonDistanceCalculator struct { 
  5.  
  6. var gMaxInt = 0x7fffffff_ffffffff 
  7.  
  8. func newPersonDistanceCalculator() IDistanceCalculator { 
  9.     return &tPersonDistanceCalculator{} 
  10.  
  11. func (me *tPersonDistanceCalculator) Calc(a, b IPoint) int { 
  12.     if a == b { 
  13.         return 0 
  14.     } 
  15.  
  16.     p1, ok := a.(*tPerson) 
  17.     if !ok { 
  18.         return gMaxInt 
  19.     } 
  20.  
  21.     p2, ok := b.(*tPerson) 
  22.     if !ok { 
  23.         return gMaxInt 
  24.     } 
  25.  
  26.     dx := p1.x - p2.x 
  27.     dy := p1.y - p2.y 
  28.  
  29.     d := dx*dx + dy*dy 
  30.     if d < 0 { 
  31.         panic(d) 
  32.     } 
  33.     return d 
  34.  
  35. var PersonDistanceCalculator = newPersonDistanceCalculator() 

tKMeansClassifier.go

k-means聚類器, 實(shí)現(xiàn)IClassifier接口.

  1. package km 
  2.  
  3. import ( 
  4.     "math/rand" 
  5.     "time" 
  6.  
  7. type tKMeansClassifier struct { 
  8.  
  9. type tPointEntry struct { 
  10.     point IPoint 
  11.     distance int 
  12.     index int 
  13.  
  14. func newPointEntry(p IPoint, d int, i int) *tPointEntry { 
  15.     return &tPointEntry{ 
  16.         p, d, i, 
  17.     } 
  18.  
  19. func newKMeansClassifier() IClassifier { 
  20.     return &tKMeansClassifier{} 
  21.  
  22. // 將samples聚類成k個(gè), 并返回k個(gè)中心點(diǎn) 
  23. func (me *tKMeansClassifier) Classify(samples []IPoint, calc IDistanceCalculator, k int) []IPoint { 
  24.     sampleCount := len(samples) 
  25.     if sampleCount <= k { 
  26.         return samples 
  27.     } 
  28.  
  29.     // 初始化, 隨機(jī)選擇k個(gè)中心點(diǎn) 
  30.     rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 
  31.     centers := make([]IPoint, k) 
  32.     for selected, i:= make(map[int]bool, 0), 0;i < k; { 
  33.         n := rnd.Intn(sampleCount) 
  34.         _,ok := selected[n] 
  35.  
  36.         if !ok { 
  37.             selected[n] = true 
  38.             centers[i] = samples[n] 
  39.             i++ 
  40.         } 
  41.     } 
  42.  
  43.  
  44.     // 根據(jù)到中心點(diǎn)的距離, 劃分samples 
  45.     for { 
  46.         groups := me.split(samples, centers, calc) 
  47.  
  48.         newCenters := make([]IPoint, k) 
  49.         for i,g := range groups { 
  50.             newCenters[i] = me.centerOf(g, calc) 
  51.         } 
  52.  
  53.         if me.groupEquals(centers, newCenters) { 
  54.             return centers 
  55.         } 
  56.         centers = newCenters 
  57.     } 
  58.  
  59. // 將樣本點(diǎn)距離中心點(diǎn)的距離進(jìn)行分簇 
  60. func (me *tKMeansClassifier) split(samples []IPoint, centers []IPoint, calc IDistanceCalculator) [][]IPoint { 
  61.     k := len(centers) 
  62.     result := make([][]IPoint, k) 
  63.     for i := 0;i<k;i++ { 
  64.         result[i] = make([]IPoint, 0
  65.     } 
  66.  
  67.     entries := make([]*tPointEntry, k) 
  68.     for i,c := range centers { 
  69.         entries[i] = newPointEntry(c, 0, i) 
  70.     } 
  71.  
  72.     for _,p := range samples { 
  73.         for _,e := range entries { 
  74.             e.distance = calc.Calc(p, e.point) 
  75.         } 
  76.  
  77.         center := me.min(entries) 
  78.         result[center.index] = append(result[center.index], p) 
  79.     } 
  80.  
  81.     return result 
  82.  
  83. // 計(jì)算一簇樣本的重心. 重心就是距離各點(diǎn)的總和最小的點(diǎn) 
  84. func (me *tKMeansClassifier) centerOf(samples []IPoint, calc IDistanceCalculator) IPoint { 
  85.     entries := make([]*tPointEntry, len(samples)) 
  86.     for i,src := range samples { 
  87.         distance := 0 
  88.         for _,it := range samples { 
  89.             distance += calc.Calc(src, it) 
  90.         } 
  91.         entries[i] = newPointEntry(src, distance, i) 
  92.     } 
  93.  
  94.     return me.min(entries).point 
  95.  
  96. // 判斷兩組點(diǎn)是否相同 
  97. func (me *tKMeansClassifier) groupEquals(g1, g2 []IPoint) bool { 
  98.     if len(g1) != len(g2) { 
  99.         return false 
  100.     } 
  101.  
  102.     for i,v := range g1 { 
  103.         if g2[i] != v { 
  104.             return false 
  105.         } 
  106.     } 
  107.  
  108.     return true 
  109.  
  110. // 查找距離最小的點(diǎn) 
  111. func (me *tKMeansClassifier) min(entries []*tPointEntry) *tPointEntry { 
  112.     minI := 0 
  113.     minD := gMaxInt 
  114.     for i,it := range entries { 
  115.         if it.distance < minD { 
  116.             minI = i 
  117.             minD = it.distance 
  118.         } 
  119.     } 
  120.  
  121.     return entries[minI] 
  122.  
  123.  
  124. var KMeansClassifier = newKMeansClassifier() 

 

 

 

責(zé)任編輯:張燕妮 來源: Go語言中文網(wǎng)
相關(guān)推薦

2012-08-09 09:57:54

K-means

2024-04-18 15:44:20

2023-03-08 08:03:09

數(shù)據(jù)結(jié)構(gòu)算法歸并排序

2020-12-31 05:31:01

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

2017-09-12 16:57:43

機(jī)器學(xué)習(xí)K-means算法Python

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2023-10-27 07:04:20

2023-04-27 09:13:20

排序算法數(shù)據(jù)結(jié)構(gòu)

2018-04-25 08:10:50

算法k-means代碼

2023-03-07 08:02:07

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

2023-03-02 08:15:13

2023-03-10 08:07:39

數(shù)據(jù)結(jié)構(gòu)算法計(jì)數(shù)排序

2021-05-12 09:07:09

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

2023-02-08 07:52:36

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

2023-10-30 08:31:42

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

2020-10-20 08:14:08

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

2020-10-12 11:48:31

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

2019-03-29 09:40:38

數(shù)據(jù)結(jié)構(gòu)算法前端

2023-03-13 10:08:31

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

2023-11-06 06:43:23

單鏈表查詢數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號