@@ -389,7 +389,7 @@ <h2 id="index-_1">前言</h2>
389389<blockquote>
390390<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>
391391</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>
393393<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
394394<h2 id="index-_2">格式约定</h2>
395395<blockquote>
@@ -1066,7 +1066,7 @@ <h2 id="symbols-linkage">符号的链接类型 (linkage)</h2>
10661066<li><a href="#cpp_tricks-map">一边遍历 map,一边删除?</a></li>
10671067<li><a href="#cpp_tricks-vector">高效删除单个 vector 元素</a></li>
10681068<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>
10701070<li><a href="#cpp_tricks-c_1">C++ 随机数的正确生成方式</a></li>
10711071<li><a href="#cpp_tricks-const">const 居然应该后置…</a></li>
10721072<li><a href="#cpp_tricks-auto">函数参数也可以 auto</a></li>
@@ -1501,7 +1501,7 @@ <h2 id="cpp_tricks-vector_1">批量删除部分 vector 元素</h2>
15011501 return x > 0; // 删除所有值大于 0 的元素
15021502});
15031503</code></pre>
1504- <h2 id="cpp_tricks-vector_2">保持有序的 vector</h2>
1504+ <h2 id="cpp_tricks-vector_2">保持有序的 vector 用于二分法 </h2>
15051505<p>如果你想要维护一个有序的数组,用 <code>lower_bound</code> 或 <code>upper_bound</code> 来插入元素,保证插入后仍保持有序:</p>
15061506<pre><code class="language-cpp">vector<int> s;
15071507s.push_back(1);
@@ -1525,27 +1525,39 @@ <h2 id="cpp_tricks-vector_2">保持有序的 vector</h2>
15251525lower_bound(s.begin(), s.end(), 5); // s.begin() + 3;
15261526</code></pre>
15271527<p>有序 vector 应用案例:利用 CDF 积分 + 二分法可以实现生成任意指定分布的随机数。</p>
1528- <p>例如抽卡概率要求 :</p>
1528+ <p>例如数值策划要求的抽卡概率分布是 :</p>
15291529<ul>
15301530<li>2% 出金卡</li>
15311531<li>10% 出蓝卡</li>
15321532<li>80% 出白卡</li>
15331533<li>8% 出答辩</li>
15341534</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>
15351544<pre><code class="language-cpp">vector<double> probs = {0.02, 0.1, 0.8, 0.08};
15361545vector<double> cdf;
15371546// 计算 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,可以运用二分法定位
15401549
15411550vector<string> result = {"金卡", "蓝卡", "白卡", "答辩"};
15421551// 生成 100 个随机数:
15431552for (int i = 0; i < 100; ++i) {
1544- double r = rand() / (RAND_MAX + 1.);
1553+ double r = rand() / (RAND_MAX + 1.0 );
15451554 int index = lower_bound(cdf.begin(), cdf.end(), r) - cdf.begin();
15461555 cout << "你抽到了" << result[index] << endl;
15471556}
15481557</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>
15491561<h2 id="cpp_tricks-c_1">C++ 随机数的正确生成方式</h2>
15501562<pre><code class="language-cpp">// 错误的写法:
15511563int r = rand() % 10; // 这样写是错误的!
0 commit comments