如何无需循环从Sobol序列IEnumerable中逐个取值并封装为调用类
Sobol序列单次调用实现方案(VB.NET)
Sobol序列是取值范围为[0,1]的低差异随机数序列,原实现生成时首先需要初始化方向数向量:
Dim V(31) As ULong For i = 1 To 31 V(i-1) = 2 ^ (32 - i) Next
原实现的遍历调用方式如下:
For Each lnumber As ULong In Sobol(V) MessageBox.Show(lnumber / 2 ^ 32) Next
只要生成的数值数量少于2^32(线性同余生成器的典型周期),该方法即可正常运行。
需求说明
需要实现无需循环遍历,单次调用即可抽取一个Sobol数值的能力,预期使用方式如下:
- 任意位置实例化公共类:
Dim ran As New SobolNumber()
- 调用属性/方法获取下一个序列值:
Dim x as Double x = ran.NextSobolNumber
原依赖的Sobol()及Ruler()函数代码如下:
Public Iterator Function Sobol(ByVal v() As ULong) As IEnumerable(Of ULong) Dim s As ULong = 0 For Each r In Ruler() Yield s = s Xor v(r) Next r End Function Public Iterator Function Ruler() As IEnumerable(Of Integer) Yield 0 For Each r In Ruler() Yield r + 1 Yield 0 Next r End Function
最终实现代码
核心思路是将序列枚举器的状态封装到自定义类中,每次调用时移动枚举器返回对应值,完全兼容需求的调用规范:
Public Class SobolNumber ' 私有成员存储方向数向量和序列枚举器,保存迭代状态 Private ReadOnly _directionVectors As ULong() Private ReadOnly _sobolEnumerator As IEnumerator(Of ULong) ' 构造函数:初始化方向数、绑定枚举器 Public Sub New() ReDim _directionVectors(31) For i = 1 To 31 _directionVectors(i - 1) = 2 ^ (32 - i) Next _sobolEnumerator = Sobol(_directionVectors).GetEnumerator() End Sub ' 对外暴露的取值属性 Public ReadOnly Property NextSobolNumber As Double Get If _sobolEnumerator.MoveNext() Then Return _sobolEnumerator.Current / 2 ^ 32 End If ' 超出2^32周期时抛出异常,可根据业务需求调整为返回默认值 Throw New InvalidOperationException("Sobol序列已超出最大生成数量限制(2^32)") End Get End Property ' 封装依赖方法,无需外部调用 Private Iterator Function Sobol(ByVal v() As ULong) As IEnumerable(Of ULong) Dim s As ULong = 0 For Each r In Ruler() Yield s = s Xor v(r) Next r End Function Private Iterator Function Ruler() As IEnumerable(Of Integer) Yield 0 For Each r In Ruler() Yield r + 1 Yield 0 Next r End Function End Class
使用示例
完全符合预期调用逻辑:
' 全局或局部实例化均可 Dim ran As New SobolNumber() ' 任意位置逐次取值 Dim x As Double = ran.NextSobolNumber Dim y As Double = ran.NextSobolNumber Dim z As Double = ran.NextSobolNumber
内容的提问来源于stack exchange,提问作者user16594857
相关产品推荐
相关产品推荐

