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