zoukankan      html  css  js  c++  java
  • Filcoin矿工出块权计算数学原理

    Filcoin出块权算法原理

    万丈高楼从地起, 所以需要先从最基本的说起.

    排列

    (n) 个人中 取出 (m) 个人,如果取出来的顺序不同就是不同的结果的话,选第一个人时有 n 种选择,
    选第二个人时, 对于前面$ n $种选择, 每一种, 都有 (n - 1) 种选择,。。。 最后一个人有 (n - m + 1) 种选择.
    所以排列的数量有:

    (P_n^m = n(n-1)(n-2)...(n-m+1))

    组合

    假设从 (n) 个人中取出 (m) 个人, 有 (C_n^m) 种方法, 对于每一种取法, 对取出的 (m) 个人进行排序, 其总和等于(P_n^m),所以下面的等式成立:

    (egin{aligned} C_n^m * m! &= P_n^m => \ C_n^m &= frac{P_n^m}{m!}end{aligned})

    二项式分布

    二项式分布概念

    假设下面的情况:

    老王是条单身狗的屌丝程序员, 想着怎么找一个妹子, 老王想了一个大面积撒网,小面积培养的办法:遇到一个妹子, 就去跟她表白;
    老王觉得自己还挺有魅力, 所以他假设自己每表白4次就会成功1次,
    所以每次表白成功的概率为$p=0.25; $则表白失败的概率为 (q=1-p=0.75)
    老王现在想算一下,如果自己对100个妹子表白, 被其中10个妹子看中的概率是多少呢?

    对于100次都表白, 我们每次都用(x)来表示表白的结果:

    [underbrace{x,x,cdots,x}_{100} ]

    这个(x)有可能是 (p) 的概率(成功), 也可能是(1-p)(失败), 由于有10次成功, 我们可以把这10次成功随机的放到100个(x)的位置上.

    一共有多少种放法?这是一个组合, 即:(C_{100}^{10}),而基于出现在(C_{100}^{10})中的每一种情况, 其出现的概率都是(p^{10}(q)^{90}),只是p和q出现的顺序不一样而已.

    所以, 可以得出如果老王对100个女孩表白, 成功10次的概率是:

    [C_{100}^{10}p^{10}(1-p)^{90} ]

    概率质量函数(probability mass function)

    根据上面的结果可以得出一个普遍的公式, 对于是n个独立的成功/失败试验中成功的次数的离散概率分布,

    其中每次试验的成功概率为p,失败的概率为1-p, 则实验n次成功k次的概率为一个函数记为:

    [f(k, n, p) = C_n^mp^k(1-p)^{n-k} ]

    这个(f(k,n,p))函数称为:'概率质量函数',用于计算对于概率为p的随机事件(伯努利实验), 重复试验n次, 出现k次的概率

    在wolfram上可以看到表白这件事的概率分布如下图:

    累积分布函数(Cumulative Distribution Function)

    老王作为一条单身汪, 也不是没有原因的, 想着想着, 竟然开始研究概率问题了:

    老王想知道我要是表白100次,成功10次到15次的概率是多少呢?

    所以老王把自己成功10次, 11次, 12..15次的概率加起来,得出下面的函数:

    [F(K, n, p) = sum_{k=10}^{15}{C_n^mp^k(1-p)^{n-k}}qquad(p=0.25, n=100) ]

    (F(K,n,p))函数就是老王表白成功的累积分布函数如图:
    累积分布函数图形

    老王表白是一个离散的事件, 在连续的情况下, 累积分布函数应该是取积分:

    [F(K, n, p) = int olimits_{10}^{15}{C_n^mp^k(1-p)^{n-k}}qquad(p=0.25, n=100) ]

    泊松分布

    老王每天没事就在路边对着女性表白,骚扰女性!常在路边走哪有不湿鞋, 出来混迟早要还的!!!
    突然有一天老王就被一道闪电劈中了, 但是, 老王因祸得福, 居然拥有了闪电的速度, 他变成了闪电侠!
    所以老王的速度越来越快, 他的每天表白的次数n可以接近无限,每天可以成功的次数变成了 (lambda),

    所以这个时候,老王表白成功的概率可以算成(P=frac{lambda}{n}), 然后我们就可以推算出泊松分布的公式了, 如下:

    (egin{aligned} f(k, n, p) &= C_n^kp^k(1-p)^{n-k} \ &=frac{{n(n-1)...(n-k+1)}}{k!}p^k(1-p)^{n-k} \ &=frac{{n(n-1)...(n-k+1)}}{k!}(frac{lambda}{n})^k(1-frac{lambda}{n})^{n-k} \ &=frac{{n(n-1)...(n-k+1)}}{n^k}frac{lambda^k}{k!}(1-frac{lambda}{n})^{n-k} \ &=egin{cases}limlimits_{n o infty}frac{{n(n-1)...(n-k+1)}}{n^k}end{cases}frac{lambda^k}{k!}(1-frac{lambda}{n})^{n-k} \ &=egin{cases}limlimits_{n o infty}frac{overbrace{n(n-1)...(n-k+1)}^{k个}}{underbrace{n * n ... * n}_{k个}}end{cases}frac{lambda^k}{k!}(1-frac{lambda}{n})^{n-k} \ &= 1*frac{lambda^k}{k!}(1-frac{lambda}{n})^{n-k} \ &= frac{lambda^k}{k!}(1-frac{lambda}{n})^{n}egin{cases}limlimits_{n o infty}(1-frac{lambda}{n})^{-k}end{cases} \ &= frac{lambda^k}{k!}(1-frac{lambda}{n})^{n}*1 \ &= frac{lambda^k}{k!}egin{cases}limlimits_{n o infty}(1+frac{-lambda}{n})^{n}end{cases} \ &= frac{lambda^k}{k!}e^{-lambda} end{aligned})

    至于最后一步推导(egin{cases}limlimits_{n o infty}(1 - frac{lambda}{n}) = e^{-lambda}end{cases})的证明,请看这里

    所以, 当二项式分布测试样本n非常大的时候, 就可以逼近泊松分布了.

    Filcoin中出块权的计算

    有了上面的基础, 就可以更加深入, 开始研究Filcoin出块权的计算的原理了.

    Filcoin计算出块wincount的代码如下, 后面会分几个部分进行详细的讨论:

    // ComputeWinCount uses VRFProof to compute number of wins
    // The algorithm is based on Algorand's Sortition with Binomial distribution
    // replaced by Poisson distribution.
    func (ep *ElectionProof) ComputeWinCount(power BigInt, totalPower BigInt) int64 {
    	h := blake2b.Sum256(ep.VRFProof)
    	lhs := BigFromBytes(h[:]).Int // 256bits, assume Q.256 so [0, 1)
    	// We are calculating upside-down CDF of Poisson distribution with
    	// rate λ=power*E/totalPower
    	// Steps:
    	//  1. calculate λ=power*E/totalPower
    	//  2. calculate elam = exp(-λ)
    	//  3. Check how many times we win:
    	//    j = 0
    	//    pmf = elam
    	//    rhs = 1 - pmf
    	//    for h(vrf) < rhs: j++; pmf = pmf * lam / j; rhs = rhs - pmf
    	lam := lambda(power.Int, totalPower.Int) // Q.256
    	p, rhs := newPoiss(lam)
    	var j int64
    	for lhs.Cmp(rhs) < 0 && j < MaxWinCount {
    		rhs = p.next()
    		j++
    	}
    	return j
    }
    

    关于计算(lambda)的解释

    矿工出块的概率理论受自身算力的影响和全网算力的影响,在Filcoin出块权限计算的时候, 这个(lambda)的计算方法为:

    // computes lambda in Q.256
    func lambda(power, totalPower *big.Int) *big.Int {
    	lam := new(big.Int).Mul(power, blocksPerEpoch.Int)   // Q.0
    	lam = lam.Lsh(lam, precision)                        // Q.256
    	lam = lam.Div(lam /* Q.256 */, totalPower /* Q.0 */) // Q.256
    	return lam
    }
    

    列成代数表达式可以是这样的:

    (lambda = frac{blocks\_per\_epoch * power}{total\_power} * 2^{256}, {block\_per\_epoch=5})

    根据前面的内容, 可以知道二项式分布中概率(p),当重复试验次数n趋于正无穷的时候, 等式成立:(p = egin{cases}limlimits_{n o infty}frac{lambda}{n}end{cases})

    所以, 这个(lambda)的值现在可以认为是在重复(2^{256})个高度以后, 矿工理论上应该获得的wincount数量(数学期望)

    关于(1-CDF[Poisson[lambda], k])的意义

    对于Filcoin计算Wincount部分的泊松分布, 根据前面的了解,可以知道,

    累积分布(CDF[Poisson[lambda], k])的意义表示:

    (n o infty)时, 矿工出块数为(0 o k)之间的概率.

    故而(1-CDF[Poisson[lambda], k])的意义表示:

    (n o infty)时, 矿工出块数为(k o n (n o infty))的概率.

    所以在Filcoin计算wincount的下面这一部分代码中:

    p, rhs := newPoiss(lam)
    var j int64
    for lhs.Cmp(rhs) < 0 && j < MaxWinCount {
    	rhs = p.next()
    	j++
    }
    

    newPoisson 返回值rhs, 是计算(1-CDF[Poisson[lambda], k], {k=0})的值
    p.next 返回的rhs, 是计算(1-CDF[Poisson[lambda], k], {k=k+1})

    再返回来看这段(for(...))循环, 其功能为:

    • newPoissn(lam), k=0, 返回出块数(>=1)的概率
    • p.next(), k=1, 返回出块数(>=2)的概率
    • p.next(), k=2, 返回出块数(>=3)的概率
    • ....

    关于lhs(hash256[VRFProof])的理解

    VRFProof是Filcoin的分布式随机数生成服务生成的256bit的随机变量,

    由于Filcoin已经把poisson分布中的概率, 映射到了([0, 2^{256}))之间,概率上可以视为([0,1))之间的一个值.

    h := blake2b.Sum256(ep.VRFProof)
    lhs := BigFromBytes(h[:]).Int // 256bits, assume Q.256 so [0, 1)
    ...
    for lhs.Cmp(rhs) < 0 && j < MaxWinCount {
    		rhs = p.next()
    		j++
    }
    

    这一部分代码可以看成, 分布式随机数生成的服务器在每一轮出块计算时, 给出一个随机概率阈值记为(phi),

    这个值对于所有的矿工来说都一样, 计算矿工本轮的wincount就是计算:(max(0, k), k in (1-CDF[Poisson[lambda],k] > phi)) ,

    所以这个wincount在满足条件的情况下, 是符合其出块的数学期望的.

    验证Wincount是否符合其算力对等的数学期望

    下面的代模拟了证了10万, 100万轮出块, 矿工算力为全网30%情况下, 矿工赢得的wincount:

    func TestWinCounts(t *testing.T) {
    	totalPower := NewInt(100)
    	power := NewInt(30)
    
    	ep := &ElectionProof{VRFProof: nil}
    
    	round := 100000.0
    	networkWincount := round * float64(blocksPerEpoch.Int64())
    	expectedWincount := networkWincount * 30 / 100
    	realWincount := int64(0)
    	for i := uint64(0); i < uint64(round); i++ {
    		i := i + 1
    		ep.VRFProof = []byte{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24), byte(i >> 32)}
    		j := ep.ComputeWinCount(power, totalPower)
    		realWincount += j
    	}
    	fmt.Printf("round=%d, expected wincount = %d, actually wincount=%d
    ",
    		int64(round), int64(expectedWincount), realWincount)
    }
    
    

    10万轮的情况, 预期赢票数为150000, 实际赢票为:150031

    === RUN   TestWinCounts
    round=100000, expected wincount = 150000, actually wincount=150031
    --- PASS: TestWinCounts (0.36s)
    PASS
    

    100万轮的情况, 预期赢票为1500000, 实际赢票为:1498847

    === RUN   TestWinCounts
    round=1000000, expected wincount = 1500000, actually wincount=1498847
    --- PASS: TestWinCounts (3.26s)
    PASS
    
  • 相关阅读:
    安卓开发笔记——高仿新浪微博文字处理(实现关键字高亮,自定义表情替换并加入点击事件实现)
    安卓开发笔记——自定义广告轮播Banner(实现无限循环)
    EBS中启用OAF页面个性化三个配置
    R12_专题知识总结提炼-AR模块
    AP创建会计科目
    Sla子分类账表结构
    EBS中后台AP发票审批脚本
    Oracle 中新增字段后patch
    R12将银行和分行都使用TCA管理
    EBS中OPM成本更新处理流程及对应的表结构、SLA表
  • 原文地址:https://www.cnblogs.com/zl03jsj/p/14873863.html
Copyright © 2011-2022 走看看