@@ -553,7 +553,7 @@ erase_if(v, [](int x) {
553
553
});
554
554
```
555
555
556
- ## 保持有序的 vector
556
+ ## 保持有序的 vector 用于二分法
557
557
558
558
如果你想要维护一个有序的数组,用 `lower_bound` 或 `upper_bound` 来插入元素,保证插入后仍保持有序:
559
559
@@ -585,29 +585,42 @@ lower_bound(s.begin(), s.end(), 5); // s.begin() + 3;
585
585
586
586
有序 vector 应用案例:利用 CDF 积分 + 二分法可以实现生成任意指定分布的随机数。
587
587
588
- 例如抽卡概率要求 :
588
+ 例如数值策划要求的抽卡概率分布是 :
589
589
590
590
- 2% 出金卡
591
591
- 10% 出蓝卡
592
592
- 80% 出白卡
593
593
- 8% 出答辩
594
594
595
+ 那么你转换一下任务。变成随机生成一个 0 到 1 的浮点数,然后判断:
596
+
597
+ - 小于 0.02 时,出金卡
598
+ - 小于 0.12 时,出蓝卡
599
+ - 小于 0.92 时,出白卡
600
+ - 小于 1.00 时,出答辩
601
+
602
+ 这个转换过程就是 CDF 积分。如果你把这 4 个数按照顺序排列,就是一个有序 vector。
603
+
604
+ 标准库提供了 `std::partial_sum`(不精准)或 `std::inclusive_scan`(更精准,C++17 引入)都可以计算一个数组的 CDF 离散积分。
605
+
595
606
```cpp
596
607
vector<double> probs = {0.02, 0.1, 0.8, 0.08};
597
608
vector<double> cdf;
598
609
// 计算 probs 的 CDF 积分,存入 cdf 数组
599
- std::partial_sum (probs.begin(), probs.end(), std::back_inserter(cdf));
600
- // cdf = {0.02, 0.12, 0.92, 1.00} 是一个有序 vector,可以运用二分法
610
+ std::inclusive_scan (probs.begin(), probs.end(), std::back_inserter(cdf));
611
+ // cdf = {0.02, 0.12, 0.92, 1.00} 是一个有序 vector,可以运用二分法定位
601
612
602
613
vector<string> result = {"金卡", "蓝卡", "白卡", "答辩"};
603
614
// 生成 100 个随机数:
604
615
for (int i = 0; i < 100; ++i) {
605
- double r = rand() / (RAND_MAX + 1.);
616
+ double r = rand() / (RAND_MAX + 1.0 );
606
617
int index = lower_bound(cdf.begin(), cdf.end(), r) - cdf.begin();
607
618
cout << "你抽到了" << result[index] << endl;
608
619
}
609
620
```
610
621
622
+ > {{ icon.detail }} 顺便一提,CDF 积分的逆运算是离散微分:` std::adjacent_difference ` ,可以从 ` cdf ` 数组复原出 ` probs ` 数组。
623
+
611
624
## C++ 随机数的正确生成方式
612
625
613
626
``` cpp
0 commit comments