@@ -389,7 +389,7 @@ <h2 id="index-_1">前言</h2>
389
389
<blockquote>
390
390
<p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 本书还在持续更新中……要追番的话,可以在 <a href="https://github.com/parallel101/cppguidebook">GitHub</a> 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一封电子邮件,提醒你小彭老师更新了。</p>
391
391
</blockquote>
392
- <p>更新时间:2024年08月28日 22:32:41 (UTC+08:00)</p>
392
+ <p>更新时间:2024年08月28日 22:39:05 (UTC+08:00)</p>
393
393
<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
394
394
<h2 id="index-_2">格式约定</h2>
395
395
<blockquote>
@@ -1066,7 +1066,7 @@ <h2 id="symbols-linkage">符号的链接类型 (linkage)</h2>
1066
1066
<li><a href="#cpp_tricks-map">一边遍历 map,一边删除?</a></li>
1067
1067
<li><a href="#cpp_tricks-vector">高效删除单个 vector 元素</a></li>
1068
1068
<li><a href="#cpp_tricks-vector_1">批量删除部分 vector 元素</a></li>
1069
- <li><a href="#cpp_tricks-vector_2">保持有序的 vector</a></li>
1069
+ <li><a href="#cpp_tricks-vector_2">保持有序的 vector 用于二分法 </a></li>
1070
1070
<li><a href="#cpp_tricks-c_1">C++ 随机数的正确生成方式</a></li>
1071
1071
<li><a href="#cpp_tricks-const">const 居然应该后置…</a></li>
1072
1072
<li><a href="#cpp_tricks-auto">函数参数也可以 auto</a></li>
@@ -1501,7 +1501,7 @@ <h2 id="cpp_tricks-vector_1">批量删除部分 vector 元素</h2>
1501
1501
return x > 0; // 删除所有值大于 0 的元素
1502
1502
});
1503
1503
</code></pre>
1504
- <h2 id="cpp_tricks-vector_2">保持有序的 vector</h2>
1504
+ <h2 id="cpp_tricks-vector_2">保持有序的 vector 用于二分法 </h2>
1505
1505
<p>如果你想要维护一个有序的数组,用 <code>lower_bound</code> 或 <code>upper_bound</code> 来插入元素,保证插入后仍保持有序:</p>
1506
1506
<pre><code class="language-cpp">vector<int> s;
1507
1507
s.push_back(1);
@@ -1525,27 +1525,39 @@ <h2 id="cpp_tricks-vector_2">保持有序的 vector</h2>
1525
1525
lower_bound(s.begin(), s.end(), 5); // s.begin() + 3;
1526
1526
</code></pre>
1527
1527
<p>有序 vector 应用案例:利用 CDF 积分 + 二分法可以实现生成任意指定分布的随机数。</p>
1528
- <p>例如抽卡概率要求 :</p>
1528
+ <p>例如数值策划要求的抽卡概率分布是 :</p>
1529
1529
<ul>
1530
1530
<li>2% 出金卡</li>
1531
1531
<li>10% 出蓝卡</li>
1532
1532
<li>80% 出白卡</li>
1533
1533
<li>8% 出答辩</li>
1534
1534
</ul>
1535
+ <p>那么你转换一下任务。变成随机生成一个 0 到 1 的浮点数,然后判断:</p>
1536
+ <ul>
1537
+ <li>小于 0.02 时,出金卡</li>
1538
+ <li>小于 0.12 时,出蓝卡</li>
1539
+ <li>小于 0.92 时,出白卡</li>
1540
+ <li>小于 1.00 时,出答辩</li>
1541
+ </ul>
1542
+ <p>这个转换过程就是 CDF 积分。如果你把这 4 个数按照顺序排列,就是一个有序 vector。</p>
1543
+ <p>标准库提供了 <code>std::partial_sum</code>(不精准)或 <code>std::inclusive_scan</code>(更精准,C++17 引入)都可以计算一个数组的 CDF 离散积分。</p>
1535
1544
<pre><code class="language-cpp">vector<double> probs = {0.02, 0.1, 0.8, 0.08};
1536
1545
vector<double> cdf;
1537
1546
// 计算 probs 的 CDF 积分,存入 cdf 数组
1538
- std::partial_sum (probs.begin(), probs.end(), std::back_inserter(cdf));
1539
- // cdf = {0.02, 0.12, 0.92, 1.00} 是一个有序 vector,可以运用二分法
1547
+ std::inclusive_scan (probs.begin(), probs.end(), std::back_inserter(cdf));
1548
+ // cdf = {0.02, 0.12, 0.92, 1.00} 是一个有序 vector,可以运用二分法定位
1540
1549
1541
1550
vector<string> result = {"金卡", "蓝卡", "白卡", "答辩"};
1542
1551
// 生成 100 个随机数:
1543
1552
for (int i = 0; i < 100; ++i) {
1544
- double r = rand() / (RAND_MAX + 1.);
1553
+ double r = rand() / (RAND_MAX + 1.0 );
1545
1554
int index = lower_bound(cdf.begin(), cdf.end(), r) - cdf.begin();
1546
1555
cout << "你抽到了" << result[index] << endl;
1547
1556
}
1548
1557
</code></pre>
1558
+ <blockquote>
1559
+ <p><img src="../img/question.png" height="30px" width="auto" style="margin: 0; border: none"/> 顺便一提,CDF 积分的逆运算是离散微分:<code>std::adjacent_difference</code>,可以从 <code>cdf</code> 数组复原出 <code>probs</code> 数组。</p>
1560
+ </blockquote>
1549
1561
<h2 id="cpp_tricks-c_1">C++ 随机数的正确生成方式</h2>
1550
1562
<pre><code class="language-cpp">// 错误的写法:
1551
1563
int r = rand() % 10; // 这样写是错误的!
0 commit comments