github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/les/randselect.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:39</date> 10 //</624450095894499328> 11 12 13 //包les实现轻以太坊子协议。 14 package les 15 16 import ( 17 "math/rand" 18 ) 19 20 //WRSitem接口应由要从中选择的任何条目实现 21 //加权随机选择集。注意,重新计算单调递减项 22 //允许按需重量(无需不断调用更新) 23 type wrsItem interface { 24 Weight() int64 25 } 26 27 //WeightedRandomSelect能够从一组项目中对随机选择进行加权 28 type weightedRandomSelect struct { 29 root *wrsNode 30 idx map[wrsItem]int 31 } 32 33 //new weightedrandomselect返回新的weightedrandomselect结构 34 func newWeightedRandomSelect() *weightedRandomSelect { 35 return &weightedRandomSelect{root: &wrsNode{maxItems: wrsBranches}, idx: make(map[wrsItem]int)} 36 } 37 38 //更新更新更新项目的权重,如果不存在则添加该权重,如果 39 //新的重量是零。请注意,不需要显式更新递减权重。 40 func (w *weightedRandomSelect) update(item wrsItem) { 41 w.setWeight(item, item.Weight()) 42 } 43 44 //移除从集合中移除项 45 func (w *weightedRandomSelect) remove(item wrsItem) { 46 w.setWeight(item, 0) 47 } 48 49 //setweight将项目的权重设置为特定值(如果为零,则移除该值) 50 func (w *weightedRandomSelect) setWeight(item wrsItem, weight int64) { 51 idx, ok := w.idx[item] 52 if ok { 53 w.root.setWeight(idx, weight) 54 if weight == 0 { 55 delete(w.idx, item) 56 } 57 } else { 58 if weight != 0 { 59 if w.root.itemCnt == w.root.maxItems { 60 //添加新的级别 61 newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches} 62 newRoot.items[0] = w.root 63 newRoot.weights[0] = w.root.sumWeight 64 w.root = newRoot 65 } 66 w.idx[item] = w.root.insert(item, weight) 67 } 68 } 69 } 70 71 //随机选择从集合中选择一个项目,其机会与其 72 //当前重量。如果所选元素的重量自 73 //最后一个存储值,以newweight/oldweight的概率返回它,否则 74 //更新其权重并选择另一个权重 75 func (w *weightedRandomSelect) choose() wrsItem { 76 for { 77 if w.root.sumWeight == 0 { 78 return nil 79 } 80 val := rand.Int63n(w.root.sumWeight) 81 choice, lastWeight := w.root.choose(val) 82 weight := choice.Weight() 83 if weight != lastWeight { 84 w.setWeight(choice, weight) 85 } 86 if weight >= lastWeight || rand.Int63n(lastWeight) < weight { 87 return choice 88 } 89 } 90 } 91 92 const wrsBranches = 8 //Wrsnode树中的最大分支数 93 94 //wrsnode是树结构的一个节点,可以存储wrsitems或其他wrsnodes。 95 type wrsNode struct { 96 items [wrsBranches]interface{} 97 weights [wrsBranches]int64 98 sumWeight int64 99 level, itemCnt, maxItems int 100 } 101 102 //递归插入将新项插入树并返回项索引 103 func (n *wrsNode) insert(item wrsItem, weight int64) int { 104 branch := 0 105 for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) { 106 branch++ 107 if branch == wrsBranches { 108 panic(nil) 109 } 110 } 111 n.itemCnt++ 112 n.sumWeight += weight 113 n.weights[branch] += weight 114 if n.level == 0 { 115 n.items[branch] = item 116 return branch 117 } 118 var subNode *wrsNode 119 if n.items[branch] == nil { 120 subNode = &wrsNode{maxItems: n.maxItems / wrsBranches, level: n.level - 1} 121 n.items[branch] = subNode 122 } else { 123 subNode = n.items[branch].(*wrsNode) 124 } 125 subIdx := subNode.insert(item, weight) 126 return subNode.maxItems*branch + subIdx 127 } 128 129 //setweight更新某个项目(应该存在)的权重并返回 130 //存储在树中的最后一个权重值的更改 131 func (n *wrsNode) setWeight(idx int, weight int64) int64 { 132 if n.level == 0 { 133 oldWeight := n.weights[idx] 134 n.weights[idx] = weight 135 diff := weight - oldWeight 136 n.sumWeight += diff 137 if weight == 0 { 138 n.items[idx] = nil 139 n.itemCnt-- 140 } 141 return diff 142 } 143 branchItems := n.maxItems / wrsBranches 144 branch := idx / branchItems 145 diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight) 146 n.weights[branch] += diff 147 n.sumWeight += diff 148 if weight == 0 { 149 n.itemCnt-- 150 } 151 return diff 152 } 153 154 //递归选择从树中选择一个项并返回其权重 155 func (n *wrsNode) choose(val int64) (wrsItem, int64) { 156 for i, w := range n.weights { 157 if val < w { 158 if n.level == 0 { 159 return n.items[i].(wrsItem), n.weights[i] 160 } 161 return n.items[i].(*wrsNode).choose(val) 162 } 163 val -= w 164 } 165 panic(nil) 166 } 167