|
看了 半不癫 发的帖子 [原创 [游戏策划] 马尔科夫链强化问题
很幸运他分享了源文件。学习后自己也照着写了一份,感谢
核心算法还是原来的 。我只是改了改界面,和优化了下性能。
觉得可能用起来会方便一点。
从分享中学习,当然也要回到分享。希望大家能指出其中的缺点和不足
装备强化模拟器.rar
(40.33 KB, 下载次数: 219)
- Option Explicit
- Option Base 0
- Public Tag_stop As Boolean '用来停止的开关变量
- 'GO 按钮直接调用此 sub
- Sub Button_Click()
- '获取表单按钮上的文字,判断状态
- If ActiveSheet.[go_btn].Caption = "G0" Then
- Tag_stop = False
- ActiveSheet.[go_btn].Caption = "快住手"
- Call iTimer
- ActiveSheet.[go_btn].Caption = "G0"
- Else
- ActiveSheet.[go_btn].Caption = "G0"
- Tag_stop = True
- End If
-
- End Sub
- '显示测试所用时长
- Sub iTimer()
- Dim T As Single
- T = Timer
- Call test_main '要计时的过程名称放这里
- ActiveSheet.[Consuming_time] = Round(Timer - T, 5) & " 秒"
- ' MsgBox Timer - t & " 秒"
- End Sub
- Sub test_main()
- Dim currentLevel As Byte '当前等级
- Dim goalLevel As Byte '目标等级
- Dim total As Long '强化测试样本次数
- Dim table As Variant '装备强化表
- '-----------------------载入数据----------------------------------------------
- '将 Sheet1 改为 ActiveSheet 也许能避免些麻烦
- With ActiveSheet
- table = .[Data].Resize(.[Data].End(xlDown).Row - 1, 4)
- goalLevel = .[Goal_lvl]
- total = .[Sample_size]
- currentLevel = .[Current_Level]
-
- '表头
- .[Avg_times].Offset(-1, 0) = "次数"
- .[Avg_cost].Offset(-1, 0) = "花费"
-
- '清空log
- .[showLevel].Resize(.[showLevel].End(xlDown).Row - 1, 4).ClearContents
- End With
- '数据检查
- If currentLevel <= 0 Or goalLevel <= currentLevel Or goalLevel > UBound(table) Or total <= 0 Then
- MsgBox "初始等级必须大于等于 1。" & Chr(13) & "目标等级必须大于当前等级,小于配置表中的等级上限" & Chr(13) & "样本总数必须大于等于 1"
- currentLevel = 1
- Exit Sub
- End If
-
- '----------------- 开始测试 -----------------
- test_total currentLevel, goalLevel, total, table
- End Sub
- '强化到目标等级 goalLevel 测试 total 件, table 强化表数据
- Sub test_total(currentLevel As Byte, goalLevel As Byte, total As Long, table As Variant)
- Dim i As Byte, jx As Byte, nextLevel As Byte
- Dim showLevelRow As Integer, showLevelColumn As Integer
- Dim j As Long, tt As Long
- Dim my_mod As Single, lastTime As Single, tc As Single, timesTotal As Single
- showLevelRow = ActiveSheet.[showLevel].Row '获取单位格行坐标
- showLevelColumn = ActiveSheet.[showLevel].Column '获取单位格列坐标
-
- '---------------------------- 显示优化部分 ---------------------------
- 'WorksheetFunction.Log10(n) + 1 求一个数的位数
- '用总数除以一个优化值,得到刷新UI的频率
- my_mod = Application.Max(total / Round(-1.7 * (WorksheetFunction.Log10(total) + 0.5) + 15, 0), 1)
-
- '优化效率,先把字符串拆好
- i = 0
- For i = currentLevel To goalLevel
- table(i, 3) = Split(table(i, 3), "+")
- table(i, 4) = Split(table(i, 4), "+")
- '安全检查
- If UBound(table(i, 3)) <> UBound(table(i, 4)) Then
- MsgBox "【等级范围】 与 【对应概率】字段的数据" & Chr(13) & " 必须一一对应!", , "数据有误!"
- Exit Sub
- End If
- Next i
- lastTime = Timer
- '---------------------------------------------------------------------
- i = tt = tc = 0
- '当前到目标等级,每一级重复采样 total 次
- For i = currentLevel To goalLevel - 1
-
- '---- 每当开始测试新等级前,先刷新和重置参数
- timesTotal = 0
- nextLevel = i + 1
- jx = i - currentLevel
-
- ' '----- 初始化显示 ----- 为了效率在循环中引用单元格时都用 cells(showLevelRow,showLevelColumn)
- With ActiveSheet
- .Cells(showLevelRow, showLevelColumn).Offset(jx, 0) = i & " -> " & i + 1 '从几级强化到几级
- .Cells(showLevelRow, showLevelColumn + 1).Offset(jx, 0) = "计算中..."
- .Cells(showLevelRow, showLevelColumn + 2).Offset(jx, 0) = "计算中..."
- .Cells(showLevelRow, showLevelColumn + 3).Offset(jx, 0) = 0 '进度条初始刻度为 0
- End With
-
- '----- 开始循环采样 -----
- For j = 1 To total
- '---- 中止开关 ------
- If Tag_stop = True Then
- Exit Sub
- End If
-
- '---- 从“当前等级”强化到“下一等级”所用的次数 ------
- Do
- timesTotal = timesTotal + 1
- Loop Until probatilisticChoice(table(i, 3), table(i, 4)) = nextLevel
-
- '----- 间隔刷新进度条 -----
- If j Mod my_mod = 0 Or j = total Then
- ActiveSheet.Cells(showLevelRow, showLevelColumn + 3).Offset(jx, 0) = j / total '更新进度条
- End If
- '---- 适当放手,避免假死 ------
- If Timer - lastTime > 3 Then
- DoEvents
- lastTime = Timer
- End If
-
- Next j
- '----- 刷新显示 -----
- With ActiveSheet
- .Cells(showLevelRow, showLevelColumn + 1).Offset(jx, 0) = total & "件 " & timesTotal & "次,平均:" & Round(timesTotal / total, 2) 'N 件装备强化到 X 的平均次数
- .Cells(showLevelRow, showLevelColumn + 2).Offset(jx, 0) = total & "件,总花费" & timesTotal * table(i, 2) & " 平均:" & Round(timesTotal * table(i, 2) / total, 2) 'N 件装备强化到 X 的平均花费
- End With
- tt = tt + Round(timesTotal / total, 2)
- tc = tc + Round(timesTotal * table(i, 2) / total, 2)
- Next i
-
- '----- 汇总显示 -----
- With ActiveSheet
- .[Avg_times].Offset(-1, 0) = "平均次数: " & tt
- .[Avg_cost].Offset(-1, 0) = "平均花费: " & tc
- End With
- End Sub
- '按概率选择目标。按参数B中的概率,选择参数A中对应的目标
- '参数A = {鱼丸, 粗面, 叉烧, 牛肉}
- '参数B = {10%, 20%, 20%, 50%}
- Public Function probatilisticChoice(ByRef targetList As Variant, ByRef targetProbability As Variant) As Integer
- Dim rnd_num As Integer '随机数
- Dim startPoint As Integer '区间开始点,区间结束点就用 startPoint + targetProbability(i)
- '------------ 初始化参数 ----------------
- Randomize '初始化随机种子
- rnd_num = Int(Rnd() * 10000 + 1) ' 随机区间 [1,10000] 这里因为是用的 万分比。
- startPoint = 0
- '------------ 遍历数组,选择目标 ----------------
- Dim i As Byte
- For i = 0 To UBound(targetProbability)
- '定义区间范围: 开始点 = 结束点,结束点 = 自己 + 向右的偏移量
- If rnd_num <= startPoint + targetProbability(i) Then
- If rnd_num > startPoint Then
- probatilisticChoice = targetList(i) '如果找到目标,直接返回值
- End If
- End If
- Next i
- End Function
复制代码
|
|