|
| 1 | +# unk_vec |
| 2 | + |
| 3 | +## 题目 |
| 4 | + |
| 5 | +输入维数 n,再输入两个 n 维向量在某直角坐标系内的坐标,输出它们的夹角。 |
| 6 | + |
| 7 | +## 解析 |
| 8 | + |
| 9 | +$$\cos\theta=\frac{\vec{a}\cdot\vec{b}}{|\vec{a}|\cdot|\vec{b}|}$$ |
| 10 | + |
| 11 | +## 答案 |
| 12 | + |
| 13 | +```c |
| 14 | +#include <stdio.h> |
| 15 | +#include <math.h> |
| 16 | +#include <stdlib.h> |
| 17 | + |
| 18 | +// 判断是否为零向量 |
| 19 | +int is_zero_vector(const double* v, int n) { |
| 20 | + for (int i = 0; i < n; i++) { |
| 21 | + if (fabs(v[i]) > 1e-10) return 0; // 使用小阈值判断是否为 0 |
| 22 | + } |
| 23 | + return 1; |
| 24 | +} |
| 25 | + |
| 26 | +double dot_product(const double* a, const double* b, int n) { |
| 27 | + double sum = 0; |
| 28 | + for (int i = 0; i < n; i++) { |
| 29 | + sum += a[i] * b[i]; |
| 30 | + } |
| 31 | + return sum; |
| 32 | +} |
| 33 | + |
| 34 | +double vector_length(const double* v, int n) { |
| 35 | + return sqrt(dot_product(v, v, n)); |
| 36 | +} |
| 37 | + |
| 38 | +int main(void) { |
| 39 | + int n; |
| 40 | + printf("请输入向量维数:"); |
| 41 | + scanf("%d", &n); |
| 42 | + |
| 43 | + if (n <= 0) { |
| 44 | + printf("错误:维数必须为正整数\n"); |
| 45 | + return 1; |
| 46 | + } |
| 47 | + |
| 48 | + double* v1 = malloc(n * sizeof(double)); |
| 49 | + double* v2 = malloc(n * sizeof(double)); |
| 50 | + |
| 51 | + if (!v1 || !v2) { |
| 52 | + printf("错误:内存分配失败\n"); |
| 53 | + free(v1); |
| 54 | + free(v2); |
| 55 | + return 1; |
| 56 | + } |
| 57 | + |
| 58 | + printf("请输入第一个向量的坐标:\n"); |
| 59 | + for (int i = 0; i < n; i++) { |
| 60 | + scanf("%lf", &v1[i]); |
| 61 | + } |
| 62 | + |
| 63 | + printf("请输入第二个向量的坐标:\n"); |
| 64 | + for (int i = 0; i < n; i++) { |
| 65 | + scanf("%lf", &v2[i]); |
| 66 | + } |
| 67 | + |
| 68 | + // 检查零向量 |
| 69 | + if (is_zero_vector(v1, n) || is_zero_vector(v2, n)) { |
| 70 | + printf("错误:零向量没有方向,无法计算夹角\n"); |
| 71 | + free(v1); |
| 72 | + free(v2); |
| 73 | + return 1; |
| 74 | + } |
| 75 | + |
| 76 | + double cos_theta = dot_product(v1, v2, n) / |
| 77 | + (vector_length(v1, n) * vector_length(v2, n)); |
| 78 | + |
| 79 | + // 处理数值误差导致的 cos_theta > 1 或 cos_theta < -1 的情况 |
| 80 | + if (cos_theta > 1) cos_theta = 1; |
| 81 | + if (cos_theta < -1) cos_theta = -1; |
| 82 | + |
| 83 | + double angle = acos(cos_theta) * 180.0 / M_PI; |
| 84 | + |
| 85 | + printf("两向量的夹角为:%.2f度\n", angle); |
| 86 | + |
| 87 | + free(v1); |
| 88 | + free(v2); |
| 89 | + return 0; |
| 90 | +} |
| 91 | +``` |
| 92 | +
|
| 93 | +## 题外话 |
| 94 | +
|
| 95 | +在高维空间中,随机选择两个向量时,这两个向量垂直或接近垂直的概率会随着维数的增加而增大。 |
| 96 | +
|
| 97 | +### 数学证明 |
| 98 | +
|
| 99 | +考虑在 n 维空间中随机选择两个单位向量 $\vec{a}$ 和 $\vec{b}$。它们的夹角 θ 满足: |
| 100 | +
|
| 101 | +$$\cos\theta=\vec{a}\cdot\vec{b}=\sum_{i=1}^n a_i b_i$$ |
| 102 | +
|
| 103 | +根据大数定律,当 n 趋于无穷时,这个点积会趋近于 0(因为每一项 $a_i b_i$ 的期望为 0)。具体地: |
| 104 | +
|
| 105 | +1. 设 $X_i = a_i b_i$,则 $\mathbb{E}(X_i) = 0$(因为 $a_i$ 和 $b_i$ 独立且对称分布) |
| 106 | +
|
| 107 | +2. $\mathrm{Var}(X_i) = \frac{1}{n^2}$(因为是单位向量) |
| 108 | +
|
| 109 | +3. 根据中心极限定理,当 $n \to \infty$ 时: |
| 110 | +
|
| 111 | + $$\frac{\sum_{i=1}^n X_i}{\sqrt{\mathrm{Var}(\sum_{i=1}^n X_i)}} \sim \mathcal{N}(0,1)$$ |
| 112 | +
|
| 113 | +4. 因此: |
| 114 | +
|
| 115 | + $$\mathrm{P}(|\cos\theta| < \epsilon) \to 1 \text{ 当 } n \to \infty$$ |
| 116 | +
|
| 117 | +这说明在高维空间中,随机选择的两个向量很可能接近垂直(夹角接近 90°)。 |
| 118 | +
|
| 119 | +### 直观理解 |
| 120 | +
|
| 121 | +可以这样理解:在高维空间中,向量的分量越多,它们的点积中的正项和负项相互抵消的机会就越大,使得最终的点积趋近于零,即夹角趋近于 90°。 |
0 commit comments