cdn fix more
This commit is contained in:
198
Html/apps/static/68bacdfadf5aeae0912f7f18-第四章:查询-分位统计量.html
Normal file
198
Html/apps/static/68bacdfadf5aeae0912f7f18-第四章:查询-分位统计量.html
Normal file
@@ -0,0 +1,198 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<!-- 你的其他样式 -->
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
<script>
|
||||
MathJax.config = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$'], ['\(', '\)']]
|
||||
},
|
||||
svg: {
|
||||
fontCache: 'global'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>分位统计量</h1>
|
||||
<h2>分位统计量</h2>
|
||||
<h3>第K大的数</h3>
|
||||
<p>在许多算法与数据分析任务中,我们不仅需要排序整个数组,还需要找出某个特定顺序统计量,例如:
|
||||
• 一个数组中第 K 大的元素;
|
||||
• 所有元素的中位数(K = n/2);
|
||||
• 某个分位点(25%、75%等)…
|
||||
<br>
|
||||
最直接的办法是先对数组进行排序,然后取第 K 个位置的元素。这种方法的时间复杂度为 <eq>O(n \log n)</eq>,因为我们需要对所有元素进行排序。
|
||||
<br>
|
||||
但我们真正关心的,只是一个位置的元素 —— 我们是否有更快的方法?
|
||||
<br></p>
|
||||
<h4>快速选择(QuickSelect)</h4>
|
||||
<p><strong>快排中的“轴枢”启发</strong>
|
||||
我们在快速排序中,每次选一个枢轴 pivot,通过划分操作把数组分成两部分:
|
||||
• 左边元素 <= pivot;
|
||||
• 右边元素 >= pivot。
|
||||
最终 pivot 会被放置固定位置上。
|
||||
<br>
|
||||
可见:</p>
|
||||
<blockquote>
|
||||
<p>若 pivot 恰好是我们要找的第 K 大元素,就不必继续递归。</p>
|
||||
</blockquote>
|
||||
<br>
|
||||
因此可得快速选择的具体做法,先令target等于N-K,即找第N-K+1小的数,其下标为target:
|
||||
1. 随机选一个枢轴;
|
||||
2. 利用快速排序的 partition 划分函数,找出 pivot 的位置 idx;
|
||||
3. 比较该位置与目标索引:
|
||||
• 若 idx == target,返回 pivot;
|
||||
• 若 idx > target,在左半边递归查找;
|
||||
• 否则在右半边递归查找。
|
||||
<br>
|
||||
理解做法并分析时间复杂度。
|
||||
### 快速选择的实现
|
||||
<p>实现一个 <code>quickselect(arr, k)</code> 函数,返回第 K 大的元素
|
||||
(也就是增序列下标N-K位置的数)</p>
|
||||
<pre><code class="language-python">import random
|
||||
|
||||
def partition(arr, low, high):
|
||||
"""
|
||||
对arr序列从low到high下标,找到一个枢轴,并返回其下标。(在arr中,比枢轴小的数都在idx左边,大的在右边)
|
||||
"""
|
||||
|
||||
def quickselect(arr, k):
|
||||
"""
|
||||
快速选择第K大元素(转换为第n-k小)
|
||||
:param arr: 输入数组
|
||||
:param k: 要找的第K大元素
|
||||
:return: 该元素值
|
||||
"""
|
||||
n = len(arr)
|
||||
target = n - k # 第k大转为第n-k小(序列下标)
|
||||
low, high = 0, n - 1
|
||||
|
||||
while low <= high:
|
||||
idx = partition(arr, low, high)
|
||||
if idx == target:
|
||||
return arr[idx]
|
||||
elif idx < target:
|
||||
low = idx + 1
|
||||
else:
|
||||
high = idx - 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
#测试1:随机数组
|
||||
random.seed(42) # 设置随机种子,确保结果可重现
|
||||
arr1 = [random.randint(1, 100) for _ in range(10)]
|
||||
k1 = 3
|
||||
result1 = quickselect(arr1.copy(), k1) # 使用副本避免修改原数组
|
||||
sorted_arr1 = sorted(arr1)
|
||||
expected1 = sorted_arr1[-k1]
|
||||
print(f"测试1 - 随机数组:")
|
||||
print(f"原数组: {arr1}")
|
||||
print(f"第{k1}大元素: {result1}, 预期值: {expected1}")
|
||||
print(f"测试{'通过' if result1 == expected1 else '失败'}\n")
|
||||
|
||||
#测试2:包含重复元素的数组
|
||||
arr2 = [5, 3, 8, 8, 1, 5, 9, 3, 5]
|
||||
k2 = 2
|
||||
result2 = quickselect(arr2.copy(), k2)
|
||||
sorted_arr2 = sorted(arr2)
|
||||
expected2 = sorted_arr2[-k2]
|
||||
print(f"测试2 - 包含重复元素:")
|
||||
print(f"原数组: {arr2}")
|
||||
print(f"第{k2}大元素: {result2}, 预期值: {expected2}")
|
||||
print(f"测试{'通过' if result2 == expected2 else '失败'}\n")
|
||||
|
||||
#测试3:边界条件 - k=1(最大元素)
|
||||
arr3 = [10, 20, 30, 40, 50]
|
||||
k3 = 1
|
||||
result3 = quickselect(arr3.copy(), k3)
|
||||
sorted_arr3 = sorted(arr3)
|
||||
expected3 = sorted_arr3[-k3]
|
||||
print(f"测试3 - 最大元素:")
|
||||
print(f"原数组: {arr3}")
|
||||
print(f"第{k3}大元素: {result3}, 预期值: {expected3}")
|
||||
print(f"测试{'通过' if result3 == expected3 else '失败'}\n")
|
||||
</code></pre>
|
||||
<h3>BFPRT 算法:中位数的中位数算法</h3>
|
||||
<p>快速选择的平均复杂度为 <eq>O(n)</eq>,但可能退化为 <eq>O(n^2)</eq>。</p>
|
||||
<h4>BFPRT分治法</h4>
|
||||
<p>BFPRT算法和二分查找中对“矩阵”进行分治的做法很像,也是通过矩阵的中心数,来排除一部分数据。
|
||||
<img src="https://hsamooc-cdn-1374354408.cos.ap-guangzhou.myqcloud.com/image_4d626b28-8e7e-4c19-8238-daaaba079a5f" alt="image" />
|
||||
<br>
|
||||
我们通过以下几个步骤确保选出的“轴枢”可以高概率地“削减”足够多的元素,从而保证递归过程每次至少减少 30% 左右的输入规模:
|
||||
<br></p>
|
||||
<ol>
|
||||
<li>将原始数组划分为若干个 5 个元素的组
|
||||
请看图中所有的竖列(粉红色背景小矩形):每个小矩形内有 5 个点,这就是我们将原始数据分成的小组。
|
||||
组的个数 ≈ n / 5(设 n 是总元素数量)
|
||||
不足 5 个的组,可以用一个全局最小值(如 -1)补全。
|
||||
<br></li>
|
||||
<li>对每组内部排序,取出中位数
|
||||
图中每个竖列的小组中,绿色圆点表示每组的元素。中间处于红框中的数为每组中位数。
|
||||
每列上两个数小于中位数,下两个大于。
|
||||
<br></li>
|
||||
<li>将所有组的中位数组成新数组,对其递归调用本算法找中位数。
|
||||
图中红色矩形框住的内容,正是“所有组中位数”的集合。这些点来自所有小组的中位点,组成新的长度为 n/5 的数组。
|
||||
对这个新数组再次使用本算法,递归找出它的中位数,作为我们最终要用的“枢轴”。即图中棕色点。
|
||||
<br></li>
|
||||
<li>这个中位数(棕色圆点)就是我们分治的“枢轴”
|
||||
有如下重要性质:
|
||||
a. 棕点及左上角(图中蓝框的点)均小于等于枢轴
|
||||
b. 棕点及右下角(图中黑框的点)均大于等于枢轴
|
||||
<br></li>
|
||||
<li>进行排除,分解,先令x=n-k+1;找第k大数就是找第x小数
|
||||
a. 若x>蓝框数数量,则在非蓝框数中找第k大
|
||||
b. 若k>黑框数数量,则在非黑框数中找第(k-黑框数数量)大
|
||||
c. 当k=1或n'(n'是当前子问题的数数量),或n'<=5,均可在不大于O(n')的用时完成找数。
|
||||
可以证明在c不成立的情况下,a,b两点至少成立一个。
|
||||
<br></li>
|
||||
</ol>
|
||||
<p>与AI教师讨论每一步在做的事和最终想要实现的效果。可以联想你之前学习到的二分查找中的矩阵分治。</p>
|
||||
<h3>BFPRT 复杂度分析</h3>
|
||||
<p>在理解了“中位数的中位数”算法的流程之后,我们接下来要正式分析它的<strong>时间复杂度</strong>,并证明该算法的整体运行时间为 <eq>O(n)</eq>。</p>
|
||||
<hr />
|
||||
<h4>写出递推式</h4>
|
||||
<p>根据 BFPRT 算法的执行过程,依次与AI教师讨论每步的时间复杂度:
|
||||
<br></p>
|
||||
<ol>
|
||||
<li><strong>将 <eq>n</eq> 个元素分组、找出每组中位数</strong>
|
||||
<ul>
|
||||
<li>分成 <eq>n/5</eq> 组,每组排序并取中位数。
|
||||
<br></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>递归地找所有中位数的中位数(枢轴)</strong>
|
||||
<ul>
|
||||
<li>子问题规模为 <eq>n/5</eq>。
|
||||
<br></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>额外一次线性 partition</strong>
|
||||
<ul>
|
||||
<li>对全数组按枢轴划分:蓝框部分、黑框部分,此外递归时是对非蓝框或非黑框的补集部分。
|
||||
<br></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>将原始数组按枢轴划分,并在一侧继续递归</strong>
|
||||
<ul>
|
||||
<li>对非蓝框或非黑框数进行递归查询。
|
||||
<br></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p><img src="https://hsamooc-cdn-1374354408.cos.ap-guangzhou.myqcloud.com/image_4d626b28-8e7e-4c19-8238-daaaba079a5f" alt="image" /></p>
|
||||
<p>从而得到最终的递推式。</p>
|
||||
<hr />
|
||||
<h4>证明 <eq>T(n) = O(n)</eq></h4>
|
||||
<p>这里的证明并不复杂,使用高中学习的数学归纳法即可。
|
||||
提示:先假设满足T(n)<=cn;然后证明递推式<=d*cn<=cn,其中d小于1。</p>
|
||||
<p>与AI教师探讨证明流程。</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user