randselect.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2016 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package les implements the Light Ethereum Subprotocol.
  17. package les
  18. import (
  19. "math/rand"
  20. )
  21. // wrsItem interface should be implemented by any entries that are to be selected from
  22. // a weightedRandomSelect set. Note that recalculating monotonously decreasing item
  23. // weights on-demand (without constantly calling update) is allowed
  24. type wrsItem interface {
  25. Weight() int64
  26. }
  27. // weightedRandomSelect is capable of weighted random selection from a set of items
  28. type weightedRandomSelect struct {
  29. root *wrsNode
  30. idx map[wrsItem]int
  31. }
  32. // newWeightedRandomSelect returns a new weightedRandomSelect structure
  33. func newWeightedRandomSelect() *weightedRandomSelect {
  34. return &weightedRandomSelect{root: &wrsNode{maxItems: wrsBranches}, idx: make(map[wrsItem]int)}
  35. }
  36. // update updates an item's weight, adds it if it was non-existent or removes it if
  37. // the new weight is zero. Note that explicitly updating decreasing weights is not necessary.
  38. func (w *weightedRandomSelect) update(item wrsItem) {
  39. w.setWeight(item, item.Weight())
  40. }
  41. // remove removes an item from the set
  42. func (w *weightedRandomSelect) remove(item wrsItem) {
  43. w.setWeight(item, 0)
  44. }
  45. // setWeight sets an item's weight to a specific value (removes it if zero)
  46. func (w *weightedRandomSelect) setWeight(item wrsItem, weight int64) {
  47. idx, ok := w.idx[item]
  48. if ok {
  49. w.root.setWeight(idx, weight)
  50. if weight == 0 {
  51. delete(w.idx, item)
  52. }
  53. } else {
  54. if weight != 0 {
  55. if w.root.itemCnt == w.root.maxItems {
  56. // add a new level
  57. newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches}
  58. newRoot.items[0] = w.root
  59. newRoot.weights[0] = w.root.sumWeight
  60. w.root = newRoot
  61. }
  62. w.idx[item] = w.root.insert(item, weight)
  63. }
  64. }
  65. }
  66. // choose randomly selects an item from the set, with a chance proportional to its
  67. // current weight. If the weight of the chosen element has been decreased since the
  68. // last stored value, returns it with a newWeight/oldWeight chance, otherwise just
  69. // updates its weight and selects another one
  70. func (w *weightedRandomSelect) choose() wrsItem {
  71. for {
  72. if w.root.sumWeight == 0 {
  73. return nil
  74. }
  75. val := rand.Int63n(w.root.sumWeight)
  76. choice, lastWeight := w.root.choose(val)
  77. weight := choice.Weight()
  78. if weight != lastWeight {
  79. w.setWeight(choice, weight)
  80. }
  81. if weight >= lastWeight || rand.Int63n(lastWeight) < weight {
  82. return choice
  83. }
  84. }
  85. }
  86. const wrsBranches = 8 // max number of branches in the wrsNode tree
  87. // wrsNode is a node of a tree structure that can store wrsItems or further wrsNodes.
  88. type wrsNode struct {
  89. items [wrsBranches]interface{}
  90. weights [wrsBranches]int64
  91. sumWeight int64
  92. level, itemCnt, maxItems int
  93. }
  94. // insert recursively inserts a new item to the tree and returns the item index
  95. func (n *wrsNode) insert(item wrsItem, weight int64) int {
  96. branch := 0
  97. for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) {
  98. branch++
  99. if branch == wrsBranches {
  100. panic(nil)
  101. }
  102. }
  103. n.itemCnt++
  104. n.sumWeight += weight
  105. n.weights[branch] += weight
  106. if n.level == 0 {
  107. n.items[branch] = item
  108. return branch
  109. }
  110. var subNode *wrsNode
  111. if n.items[branch] == nil {
  112. subNode = &wrsNode{maxItems: n.maxItems / wrsBranches, level: n.level - 1}
  113. n.items[branch] = subNode
  114. } else {
  115. subNode = n.items[branch].(*wrsNode)
  116. }
  117. subIdx := subNode.insert(item, weight)
  118. return subNode.maxItems*branch + subIdx
  119. }
  120. // setWeight updates the weight of a certain item (which should exist) and returns
  121. // the change of the last weight value stored in the tree
  122. func (n *wrsNode) setWeight(idx int, weight int64) int64 {
  123. if n.level == 0 {
  124. oldWeight := n.weights[idx]
  125. n.weights[idx] = weight
  126. diff := weight - oldWeight
  127. n.sumWeight += diff
  128. if weight == 0 {
  129. n.items[idx] = nil
  130. n.itemCnt--
  131. }
  132. return diff
  133. }
  134. branchItems := n.maxItems / wrsBranches
  135. branch := idx / branchItems
  136. diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight)
  137. n.weights[branch] += diff
  138. n.sumWeight += diff
  139. if weight == 0 {
  140. n.itemCnt--
  141. }
  142. return diff
  143. }
  144. // choose recursively selects an item from the tree and returns it along with its weight
  145. func (n *wrsNode) choose(val int64) (wrsItem, int64) {
  146. for i, w := range n.weights {
  147. if val < w {
  148. if n.level == 0 {
  149. return n.items[i].(wrsItem), n.weights[i]
  150. }
  151. return n.items[i].(*wrsNode).choose(val)
  152. }
  153. val -= w
  154. }
  155. panic(nil)
  156. }