<think>
我们正在讨论`np.partition(x, n)`函数。这个函数是NumPy中用于部分排序的函数,它能够高效地重新排列数组,使得第n个位置的元素是排序后应处的正确位置,同时该元素左侧的所有元素都小于等于它,右侧的所有元素都大于等于它。但注意,左右两侧的元素并不保证有序。
### 功能原理
`np.partition(x, n)`的原理基于快速选择算法,这是一种从无序列表中选择第k小(或大)元素的高效算法,平均时间复杂度为$O(n)$。该函数执行步骤如下:
1. 选取数组`x`中任意一个元素作为基准(pivot)。
2. 将数组划分为两部分:小于等于基准的元素放在左边,大于等于基准的元素放在右边。
3. 如果基准的最终位置恰好是索引`n`,则停止;否则,在包含目标索引的那一部分中递归执行上述步骤。
最终,索引`n`处的元素是排序后应处的元素,且左边的元素都小于等于它,右边的元素都大于等于它。注意,左右两部分内部并不有序。
### 使用方法
函数签名:
```python
numpy.partition(a, kth, axis=-1, kind='introselect', order=None)
```
- `a`: 输入数组。
- `kth`: 一个整数或整数序列,指定将哪个(些)位置的元素放到排序后应处的正确位置。注意,如果提供多个索引,则每个索引对应的位置都会是排序后应处的值,并且整个数组在这些位置之间划分。
- `axis`(可选): 沿着哪个轴进行划分,默认为最后一个轴。
- `kind`(可选): 选择算法,一般为'introselect'(内省选择),它在大多数情况下表现良好。
- `order`(可选): 当数组是结构化数组时,用于指定比较的字段。
返回:划分后的数组(与输入数组同形状)。
### 示例
#### 示例1: 基本使用
```python
import numpy as np
arr = np.array([3, 2, 6, 1, 5, 4])
# 我们希望第3小的元素(索引为2,因为从0开始)在正确位置
result = np.partition(arr, 2)
print(result)
```
输出可能是:
```
[1 2 3 6 5 4]
```
解释:索引2的位置(第三个元素)是3,它确实是第三小的数。3左边的元素(1,2)都小于等于3,右边的元素(6,5,4)都大于等于3。
#### 示例2: 多个kth
```python
arr = np.array([10, 1, 9, 8, 3, 4, 2, 7, 6, 5])
# 我们希望第3小(索引2)和第6小(索引5)的元素在正确位置
result = np.partition(arr, [2, 5])
print(result)
```
输出可能是:
```
[ 1 2 3 5 4 6 8 7 9 10]
```
解释:
- 索引2处的元素是3(第三小),索引5处的元素是6(第六小)。
- 索引0到2(前三个)的元素都小于等于3(即1,2,3)。
- 索引3到5的元素在3和6之间(这里我们看到5,4,6,注意4和5都小于6,但大于3)。
- 索引6到9的元素都大于等于6。
注意:多个kth时,划分会保证每个kth指定的位置都是正确的,并且划分区间是连续的。
#### 示例3: 多维数组
```python
arr = np.array([[3, 2, 1], [6, 5, 4]])
# 沿着最后一个轴(即每行内部)进行划分,以第1个位置(索引1)为划分点
result = np.partition(arr, 1, axis=-1)
print(result)
```
输出:
```
[[1 2 3] # 第一行:索引1的位置是2,左边1<=2,右边3>=2
[4 5 6]] # 第二行:索引1的位置是5,左边4<=5,右边6>=5
```
### 注意事项
1. **时间复杂度**:平均$O(n)$,最坏情况$O(n^2)$,但由于使用了内省选择(introselect),最坏情况在实际中很少见。
2. **稳定性**:划分操作是不稳定的,即相等元素的相对位置可能改变。
3. **应用场景**:当需要获取数组的前k小(或前k大)元素而不需要完全排序时,使用`np.partition`比完全排序($O(n\log n)$)更高效。
### 相关函数
- `np.argpartition`: 返回划分后元素的原始索引,便于从原始数组中获取划分后的值。
- `np.sort`: 完全排序。
### 相关问题
1. 如何使用`np.argpartition`函数获取数组中最小的k个元素的索引?
2. 在什么情况下应该使用部分排序而不是完全排序?
3. `np.partition`函数的时间复杂度如何分析?
</think>
### `np.partition(x, n)` 详解
`np.partition(x, n)` 是 NumPy 中用于**部分排序**的高效函数,其核心原理基于快速选择算法(Quickselect)。它能在 $O(n)$ 平均时间复杂度内完成操作,比完全排序($O(n\log n)$)更高效。
---
#### **功能原理**
1. **划分机制**
将数组 `x` 重新排列,使得:
- 索引 `n` 位置的元素是其排序后应处的正确位置
- 所有 `n` 左侧元素 $\leq$ `x[n]`
- 所有 `n` 右侧元素 $\geq$ `x[n]`
数学表达:
$$
\forall i < n,\ x_{\text{part}}[i] \leq x_{\text{part}}[n] \quad \text{且} \quad \forall j > n,\ x_{\text{part}}[j] \geq x_{\text{part}}[n]
$$
2. **算法基础**
基于快速排序的分区思想,但不保证左右两侧完全有序(仅保证相对基准值的大小关系)。
---
#### **参数说明**
```python
numpy.partition(a, kth, axis=-1, kind='introselect', order=None)
```
- **`a`**:输入数组
- **`kth`**:关键参数
- 整数:划分点的索引位置(如 `n=3` 表示第4小元素)
- 列表:支持多划分点(如 `kth=[2,5]`)
- **`axis`**:操作轴(默认最后一个轴)
- **`kind`**:选择算法(通常为 `'introselect'`)
- **`order`**:结构化数组的排序字段
---
#### **使用示例**
##### 1. 基本划分(单索引)
```python
import numpy as np
x = np.array([30, 10, 40, 20, 50])
# 将第2小元素(索引1)放到正确位置
result = np.partition(x, 1)
print(result) # 输出: [10 20 40 30 50]
# 解释:
# - 索引1的值20是第2小元素
# - 左侧 [10] ≤ 20
# - 右侧 [40,30,50] ≥ 20(但不保证右侧有序)
```
##### 2. 多划分点
```python
x = np.array([5, 8, 1, 3, 6, 2, 7, 4])
# 同时划分第2小(索引1)和第5小(索引4)元素
result = np.partition(x, [1, 4])
print(result) # 可能输出: [1 2 3 4 5 6 7 8]
# 解释:
# - 索引1的值2是第2小
# - 索引4的值5是第5小
# - [1] ≤ 2 ≤ [3,4] ≤ 5 ≤ [6,7,8]
```
##### 3. 多维数组应用
```python
arr = np.array([[9, 2, 5], [7, 1, 8]])
# 沿每行划分,取第1小元素(索引0)
result = np.partition(arr, 0, axis=1)
print(result)
# 输出:
# [[2 5 9] # 索引0的值2是该行最小
# [1 7 8]] # 索引0的值1是该行最小
```
---
#### **注意事项**
1. **非稳定性**
不保留相等元素的原始顺序(非稳定排序)。
2. **性能优势**
当只需部分元素(如前k小值)时,比 `np.sort()` 更高效:
- `np.partition(x, k)[:k]` → 前k小值
- `np.partition(x, -k)[-k:]` → 前k大值
3. **内存效率**
直接修改数组视图,默认返回新数组(需用 `a.partition(...)` 进行原地操作)。
---
#### **与相关函数对比**
| 函数 | 特点 | 时间复杂度 |
|-----------------------|--------------------------|--------------|
| `np.partition(x, n)` | 部分排序 | $O(n)$ |
| `np.sort(x)` | 完全排序 | $O(n\log n)$ |
| `np.argpartition()` | 返回划分后的索引 | $O(n)$ |
---
### 相关问题
1. 如何用 `np.partition()` 高效获取数组中第k小的元素?
2. `np.partition()` 和 `np.argpartition()` 的主要区别是什么?
3. 在多维数组中如何沿指定轴进行部分排序?[^1]