Skip to content

Commit e3f1f07

Browse files
committed
小修改
1 parent c6a8572 commit e3f1f07

File tree

8 files changed

+355
-10
lines changed

8 files changed

+355
-10
lines changed

docs/教程/正文/数据结构与算法/算法/3_算法的时间和空间复杂度.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@
2424

2525
:::
2626

27-
其中,第 2.4.5. 可以更快速地用极限定义:
27+
可以更快速地用极限定义:
2828

29-
1. $$f(n) \in O(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=C$$
30-
2. $$f(n) \in o(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=0$$
31-
3. $$f(n) \in \omega(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=\infty$$
29+
1. $$f(n) \in \Theta(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=C$$
30+
2. $$f(n) \in O(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=0\space或\space C$$
31+
3. $$f(n) \in \Omega(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=\infty\space或\space C$$
32+
4. $$f(n) \in o(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=0$$
33+
5. $$f(n) \in \omega(g(n)) \Leftrightarrow \lim_{n \to \infty} \frac{f(n)}{g(n)}=\infty$$
34+
35+
其中 $C$ 是一个正常数。
3236

3337
### 2.2. 性质
3438

docs/教程/正文/语法和标准库/5_语句/5_2_选择语句/5_2_1_if_else.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ if (condition) {
5454

5555
```c
5656
if (hp == 0)
57-
state = dead;
57+
state = PLAYER_DEAD;
5858
if ( /* 其他条件 */ )
5959
// 其他代码
6060
```
@@ -63,19 +63,19 @@ if ( /* 其他条件 */ )
6363

6464
```c
6565
if (hp == 0)
66-
state = dead;
66+
state = PLAYER_DEAD;
6767
printf("角色死亡\n");
6868
if ( /* 其他条件 */ )
6969
// 其他代码
7070
```
7171
72-
但是,当运行的时候,我会发现,无论角色生命值是否等于 0,都会打印消息。这是因为,`if` 语句只会包含其后的第一条语句,即 `state = dead;`,因此即使角色生命值不等于 0,`printf("角色死亡\n");` 也会被执行。
72+
但是,当运行的时候,我会发现,无论角色生命值是否等于 0,都会打印消息。这是因为,`if` 语句只会包含其后的第一条语句,即 `state = PLAYER_DEAD;`,因此即使角色生命值不等于 0,`printf("角色死亡\n");` 也会被执行。
7373
7474
为了避免这种情况,建议永远使用大括号包裹 `if` 之后的语句,使之成为复合语句,如下所示:
7575
7676
```c
7777
if (hp == 0) {
78-
state = dead;
78+
state = PLAYER_DEAD;
7979
}
8080
if ( /* 其他条件 */ ) {
8181
// 其他代码
@@ -86,7 +86,7 @@ if ( /* 其他条件 */ ) {
8686

8787
```c
8888
if (hp == 0) {
89-
state = dead;
89+
state = PLAYER_DEAD;
9090
printf("角色死亡\n");
9191
}
9292
if ( /* 其他条件 */ )
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# 20241215_1_4
2+
3+
## 1. 大数阶乘
4+
5+
### 题目
6+
7+
计算并打印出 50! 的值。
8+
9+
### 解析
10+
11+
由于 50! 远超过基本整数类型的范围,需要用数组存储每一位。计算过程中:
12+
13+
1. 用数组 `digits[]` 存储结果的每一位
14+
2. 从 1 乘到 50,每次乘法都需要处理进位
15+
3. 最后倒序输出数组即为结果
16+
17+
### 答案
18+
19+
```c
20+
#include <stdio.h>
21+
#include <string.h>
22+
23+
#define MAX_DIGITS 65 // 50! 的位数为 65
24+
25+
void multiply(int digits[], int n, int* len) {
26+
int carry = 0;
27+
28+
for(int i = 0; i < *len; i++) {
29+
int prod = digits[i] * n + carry;
30+
digits[i] = prod % 10;
31+
carry = prod / 10;
32+
}
33+
34+
while(carry) {
35+
digits[*len] = carry % 10;
36+
carry /= 10;
37+
(*len)++;
38+
}
39+
}
40+
41+
int main(void) {
42+
int digits[MAX_DIGITS] = {1}; // 初始值为 1
43+
int len = 1;
44+
45+
// 计算阶乘
46+
for(int i = 2; i <= 50; i++) {
47+
multiply(digits, i, &len);
48+
}
49+
50+
// 倒序输出结果
51+
printf("50! = ");
52+
for(int i = len - 1; i >= 0; i--) {
53+
printf("%d", digits[i]);
54+
}
55+
printf("\n");
56+
57+
return 0;
58+
}
59+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 投飞镖估算π
2+
3+
## 题目
4+
5+
“投飞镖”可以近似 $\pi$ 的值。取 $[-1,1]\times[-1,1]$ 的区域,向其中随机投掷 M 个飞镖,会产生 M 个落点。若其中有 m 个落点落在单位圆内,则 $\pi$ 的近似值为 $\frac{4m}{M}$。设计程序,输入投掷次数,估算 $\pi$ 的值。
6+
7+
## 解析
8+
9+
这是蒙特卡洛方法的典型应用。原理:
10+
11+
1. 在正方形区域内随机投掷点,正方形边长为 2,中心在原点。
12+
2. 统计落在单位圆内的点数。
13+
3. $\pi$ 值近似等于:$\pi\approx\frac{4N_{\text{圆内}}}{N_{\text{总}}}$
14+
15+
这是因为单位圆与其外接正方形的面积比为 $\frac{\pi r^2}{(2r)^2}=\frac{\pi}{4}$。当采样点数量足够大时,点的分布比例会近似于面积比。
16+
17+
## 答案
18+
19+
```c
20+
#include <stdio.h>
21+
#include <stdlib.h>
22+
#include <time.h>
23+
24+
int main(void) {
25+
int M;
26+
printf("输入投掷次数:");
27+
scanf("%d", &M);
28+
29+
srand(time(NULL));
30+
int in_circle = 0;
31+
32+
for(int i = 0; i < M; i++) {
33+
// 使用 -1 到 1 的范围以充分利用圆形区域
34+
double x = 2.0 * rand() / RAND_MAX - 1.0;
35+
double y = 2.0 * rand() / RAND_MAX - 1.0;
36+
if(x*x + y*y <= 1) {
37+
in_circle++;
38+
}
39+
}
40+
41+
double pi = 4.0 * in_circle / M;
42+
printf("π ≈ %.6f\n", pi);
43+
return 0;
44+
}
45+
```
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# 投针估算π
2+
3+
## 题目
4+
5+
“投针”可以近似 $\pi$ 的值。在无限大的桌面上设置间距为 d 的一组平行线。将 M 个长度为 d 的针随机投掷到桌面上,若其中有 m 个针压到了某条线,则 $\pi$ 的近似值为 $\frac{2M}{m}$。
6+
7+
## 解析
8+
9+
布丰投针问题的数学原理:
10+
11+
1. 设针长为 $\ell$,平行线间距为 $d$
12+
2. 针与任意一条线相交的概率为 $P(\text{相交})=\frac{2\ell}{\pi d}$
13+
3. 当 $\ell = d$ 时,$\pi = \frac{2N_{\text{总}}}{N_{\text{相交}}}$,其中:
14+
- $N_{\text{总}}$ 是投掷总次数
15+
- $N_{\text{相交}}$ 是相交次数
16+
17+
判断相交的条件:针中点到最近平行线的距离 $y \leq \frac{\ell}{2}|\sin(\theta)|$,其中 $\theta$ 是针与线的夹角。
18+
19+
### 证明
20+
21+
设针中点到最近平行线的距离为 $y$,针与线的夹角为 $\theta$,则:
22+
23+
1. $y$ 在 $[0,\frac{d}{2}]$ 上均匀分布
24+
2. $\theta$ 在 $[0,\pi]$ 上均匀分布
25+
3. 相交概率为:
26+
27+
$$P(\text{相交})=\frac{2}{\pi}\int_0^{\pi/2}\frac{\ell\sin\theta}{d}d\theta=\frac{2\ell}{\pi d}$$
28+
29+
## 答案
30+
31+
```c
32+
#include <stdio.h>
33+
#include <stdlib.h>
34+
#include <time.h>
35+
#include <math.h>
36+
37+
int main(void) {
38+
int M;
39+
printf("输入投掷次数:");
40+
scanf("%d", &M);
41+
42+
srand(time(NULL));
43+
int cross = 0;
44+
const double d = 1.0; // 线间距
45+
const double l = 1.0; // 针长
46+
47+
for(int i = 0; i < M; i++) {
48+
// y是针中点到最近线的距离
49+
double y = (double)rand() / RAND_MAX * (d/2);
50+
// theta是针与线的夹角
51+
double theta = (double)rand() / RAND_MAX * M_PI;
52+
53+
// 判断是否相交
54+
if(y <= (l/2) * sin(theta)) {
55+
cross++;
56+
}
57+
}
58+
59+
double pi = 2.0 * M / cross;
60+
printf("π ≈ %.6f\n", pi);
61+
return 0;
62+
}
63+
```
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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

Comments
 (0)