Page 2 of 9

一份友好的 NumPy 入门代码清单

NumPy 安装

使用 pip 可以方便地安装 NumPy。

python -m pip install numpy

NumPy 概览

# 引入 NumPy 包,习惯上引入为 np,方便书写
import numpy as np

# 将一个列表转换为 NumPy 数组
a = np.array([[1,2,3],[4,5,6]])

# 打印数组
print(a)

# 打印数组的维度、形状和大小
print('number of dim:', a.ndim)
print('shape', a.shape)
print('size:', a.size)
[[1 2 3]
 [4 5 6]]
number of dim: 2
shape (2, 3)
size: 6

NumPy 创建 Array

从 Python 列表创建

使用 np.array 从 Python 列表创建 NumPy Array

a = np.array([6,67,7])
print(a)
[6 67  7]

指定数据类型

使用 dtype 属性指定 NumPy Array 元素的类型。
例如要制定类型为整数型,则可以使用 np.int,可以使用 np.int32 进一步指定其为 32 位整数型。
从下面程序的输出结果看到,若不指定位数,np.int 默认为 64 位。

a = np.array([6,67,7], dtype=np.int)
print(a.dtype)
a = np.array([6,67,7], dtype=np.int32)
print(a.dtype)
a = np.array([6,67,7], dtype=np.int64)
print(a.dtype)
int32
int32
int64

其他类型,例如 float 同理。

a = np.array([6,67,7], dtype=np.float)
print(a.dtype)
a = np.array([6,67,7], dtype=np.float32)
print(a.dtype)
a = np.array([6,67,7], dtype=np.float64)
print(a.dtype)
float64
float32
float64

创建一个二维数组

a = np.array([[6,67,7],[6,67,7]])
print(a)
[[6 67  7]
 [6 67  7]]

全零数组

使用 np.zeros() 创建全零数组,以 (x_dim,y_dim) 的形式指定维度。

a = np.zeros((6,7) )
print(a)
[[0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]]

全一数组

使用 np.ones() 创建全一数组

a = np.ones((6,7) )
print(a)
[[1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]]

空数组

使用 np.empty() 创建全零数组
要注意的是,空矩阵填充的是内存中当前存放的值,是随机的。

a = np.empty((6,7) )
print(a)
[[1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1.]]

随机数组

使用 np.random.random() 创建随机数组

a = np.random.random((2,4))
print(a)
[[0.68673006 0.75950035 0.59310212 0.02855161]
 [0.23618792 0.82036725 0.68761253 0.4201456]]

np.arange()

使用 np.arange(),三个参数分别指定开始值、终值和步长,创建一个等差数列一维数组(不包含终值)。

a = np.arange(67,73,1)
print(a)
[67 68 69 70 71 72]

np.linspace()

使用 np.linspace(),三个参数依次为开始值、终值和节点数,创建一个等差数列一维数组。可以通过 endpoint 参数指定是否包含终值,默认值为 True。

a = np.linspace(67,73,7)
print(a)
[67. 68. 69. 70. 71. 72. 73.]

Array.reshape()

使用 Array.reshape() 方法,重塑数组。

a = np.arange(12).reshape((3,4))
print(a)
[[0  1  2  3]
 [4  5  6  7]
 [8  9 10 11]]

NumPy 基础运算

加减

直接使用 + - 符号完成数组加减

# 示例数据
a = np.array([10,20,30,40])
b = np.arange(4)
print('a:',a,'b:',b)

c = a-b
print('a-b:',c)

c = a+b
print('a+b:',c)
a: [10 20 30 40] b: [0 1 2 3]
a-b: [10 19 28 37]
a+b: [10 21 32 43]

幂次方

使用 ** 符号完成针对数组元素的单独幂次方。

# 示例数据
b = np.arange(4)
print('b:',b)

c = b**2
print('b^2:',c)
b: [0 1 2 3]
b^2: [0 1 4 9]

三角函数

使用 np.sin() np.cos() np.tan() 等完成三角函数计算

# 示例数据
a = np.array([10,20,30,40])
b = np.arange(4)
print('a:',a,'\nb:',b)

c = np.sin(a)
print('sin(a):',c)

c = np.cos(a)
print('cos(a):',c)

c = np.tan(a)
print('tan(a):',c)
a: [10 20 30 40]
b: [0 1 2 3]
sin(a): [-0.54402111  0.91294525 -0.98803162  0.74511316]
cos(a): [-0.83907153  0.40808206  0.15425145 -0.66693806]
tan(a): [ 0.64836083  2.23716094 -6.4053312  -1.11721493]

大小比较

# 示例数据
b = np.arange(4)
print('b:',b)

print(b<3)
b: [0 1 2 3]
[True  True  True False]

乘法

使用 * 实现逐个相乘,使用 np.dot 实现矩阵乘法,也可以 Array.dot()

a = np.array([[1,1],[1,0]])
b = np.arange(4).reshape((2,2))

print('a:\n',a)
print('b:\n',b)

# 逐个相乘
c = a*b
print('c:\n',c)

# 矩阵乘法
c_dot = np.dot(a,b)
print('c_dot\n',c_dot)

c_cot = a.dot(b) # 另一种形式
print('another c_dot:\n',c_dot)
a:
 [[1 1]
 [1 0]]
b:
 [[0 1]
 [2 3]]
c:
 [[0 1]
 [2 0]]
c_dot
 [[2 4]
 [0 1]]
another c_dot:
 [[2 4]
 [0 1]]

求和、求最大值、求最小值

使用 np.sum() 求数组和,使用 np.max() 寻找数组中的最大值,使用 np.min() 寻找数组中的最小值。
可以使用 axis 参数指定求和的维度,在二维数组中,axis=0 代表在列方向上求数值,axis=1 代表在行方向上求数值。

a = np.random.random((2,4))
print('a:',a)

a_sum = np.sum(a)
a_min = np.min(a)
a_max = np.max(a)

print('\na_sum:',a_sum,'\na_min:',a_min,'\na_max:',a_max)

a_sum_0 = np.sum(a,axis=0) # 列
a_sum_1 = np.sum(a,axis=1) # 行
print('\na_sum_0:',a_sum_0)
print('a_sum_1:',a_sum_1)
a: [[0.24204778 0.87497235 0.38096849 0.17361687]
 [0.94614194 0.97785151 0.88869463 0.41283545]]

a_sum: 4.897129017685974
a_min: 0.17361687468327414
a_max: 0.9778515067213707

a_sum_0: [1.18818972 1.85282386 1.26966312 0.58645233]
a_sum_1: [1.67160549 3.22552353]

求最大 / 最小值索引

使用 np.argmax() np.argmin() 来求得最大值和最小值的位置

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

argmin = np.argmin(a)
print('argmin:',argmin)

argmax = np.argmax(a)
print('argmax:',argmax)
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
argmin: 11
argmax: 0

求均值、中位数

使用 np.mean() 求数组均值,使用 np.median() 求数组中位数。这些函数同样支持 axis 参数。

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

mean = np.mean(a)
print('\nmean:',mean)
print('a.mean():',a.mean())

median = np.median(a)
print('\nmedian:',median)

mean_0 = np.mean(a, axis=0) # 列
mean_1 = np.mean(a, axis=1) # 行
print('\nmean_0:',mean_0)
print('mean_1:',mean_1)
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]

mean: 8.5
a.mean(): 8.5

median: 8.5

mean_0: [10.  9.  8.  7.]
mean_1: [12.5  8.5  4.5]

求累加和(Cumulative Sum)

使用 np.cumsum() 求数组累加和

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

cumsum = np.cumsum(a)
print('cumsum:',cumsum)
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
cumsum: [14  27  39  50  60  69  77  84  90  95  99 102]

求间差

使用 np.diff() 求间差

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

diff = np.diff(a)
print('diff:',diff)
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
diff: [[-1 -1 -1]
 [-1 -1 -1]
 [-1 -1 -1]]

求非零索引

使用 np.nonzero() 输出非零数的索引

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

nonzero = np.nonzero(a)
print('nonzero:',nonzero) # 输出非零数的索引
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
nonzero: (array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], dtype=int64), array([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], dtype=int64))

逐行排序

使用 np.sort() 对数组进行逐行排序

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

sort_a = np.sort(a)
print('sort_a:',sort_a) #逐行排序
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
sort_a: [[11 12 13 14]
 [7  8  9 10]
 [3  4  5  6]]

转置

使用 np.transpose() 进行转置操作。使用 Array.T 也可以获取转置矩阵值。

# 示例数据
a = np.arange(14,2,-1).reshape((3,4))
print('a:',a)

transpose = np.transpose(a)
print('transpose:',transpose)
print('a.T:',a.T)
a: [[14 13 12 11]
 [10  9  8  7]
 [6  5  4  3]]
transpose: [[14 10  6]
 [13  9  5]
 [12  8  4]
 [11  7  3]]
a.T: [[14 10  6]
 [13  9  5]
 [12  8  4]
 [11  7  3]]

np.clip()

使用 np.clip(a,l,h),可以将 a 数组中小于 l 的元素置为 l,大于 h 的元素置为 h

# 示例数据
a = np.arange(3,15).reshape((3,4)
print('a:',a)

clip = np.clip(a,5,9)
print('clip:',clip)
  File "<ipython-input-26-7905e4d8b1e1>", line 3
    print('a:',a)
        ^
SyntaxError: invalid syntax

NumPy 索引

请看下面的示例:

# 示例数据
a = np.arange(3,15).reshape((3,4))
print('a:',a)

print("\n 索引一行:")
print(a[2]) # 索引第三行

print("\n 索引一个元素:")
print(a[2][1]) # 索引第三行第二列的数字
print(a[2,1]) # 另一种方式


print("\n「:」的用法:")
print(a[2,:]) # 选中第三行
print(a[:,1]) # 选中第二列
print(a[2,0:2]) # 选中第三行,第 1 至 2 个元素

print("\n 遍历行:")
for row in a:
  print(row)

print("\n 遍历列:")
for column in a.T:
  print(column)

print("\n 遍历每一个元素:")
print(a.flatten())
for item in a.flat:
  print(item)
a: [[3  4  5  6]
 [7  8  9 10]
 [11 12 13 14]]

索引一行:
[11 12 13 14]

索引一个元素:
12
12

「:」的用法:
[11 12 13 14]
[4  8 12]
[11 12]

遍历行:
[3 4 5 6]
[7  8  9 10]
[11 12 13 14]

遍历列:
[3  7 11]
[4  8 12]
[5  9 13]
[6 10 14]

遍历每一个元素:
[3  4  5  6  7  8  9 10 11 12 13 14]
3
4
5
6
7
8
9
10
11
12
13
14

Numpy Array 的合并

# 示例数据
a = np.array([1,1,1])
b = np.array([2,2,2])

# 上下合并
print("上下合并:")
print(np.vstack((a,b)))

# 横向合并
print("\n 横向合并:")
print(np.hstack((a,b)))

# 行向新维度
print("\n 创建一个行方向的新维度:")
print(a[np.newaxis,:])

# 列向新维度
print("\n 创建一个列方向的新维度:")
print(a[:,np.newaxis])

# 通过控制 axis 参数,融合了 np.vstack() 和 np.hstack()
print("\nnp.concatenate():")
c = np.concatenate((a,b),axis=0)
print(c)
上下合并:
[[1 1 1]
 [2 2 2]]

横向合并:
[1 1 1 2 2 2]

创建一个行方向的新维度:
[[1 1 1]]

创建一个列方向的新维度:
[[1]
 [1]
 [1]]

np.concatenate():
[1 1 1 2 2 2]

NumPy Array 的分割

使用 np.split() 切割 NumPy Array。第一个参数为要切割的数组。第二个参数表示分割方法,可以填写一个常数 n 表示平均分割乘 n 块,也可以填写一个列表用于不均等分割。axis 参数用于控制分割方向。

# 示例数据
a = np.arange(3,15).reshape((3,4))
print('a:',a)

print("纵向均等:")
print(np.split(a,2,axis=1)) #在纵向将 a 均等分为两块
print("横向均等:")
print(np.split(a,3,axis=0)) #在横向将 a 均等分为三块
print("纵向不均等:")
print(np.split(a,[1,1,2],axis=1)) #在纵向不均等分割
a: [[3  4  5  6]
 [7  8  9 10]
 [11 12 13 14]]
纵向均等:
[array([[ 3,  4],
       [7,  8],
       [11, 12]]), array([[ 5,  6],
       [9, 10],
       [13, 14]])]
横向均等:
[array([[3, 4, 5, 6]]), array([[ 7,  8,  9, 10]]), array([[11, 12, 13, 14]])]
纵向不均等:
[array([[ 3],
       [7],
       [11]]), array([], shape=(3, 0), dtype=int32), array([[ 4],
       [8],
       [12]]), array([[ 5,  6],
       [9, 10],
       [13, 14]])]

axis 参数的理解:前面提到的 axis 通常是,0 表示行方向,1 表示列方向,与这里的纵向横向直观上不太贴合。可以这么理解:axis=0 就是要将「列」给切开,axis=1 就是要将「行」切开。

NumPy Array 的复制

浅复制

如果使用赋值符号 = 直接复制 NumPy Array,实际上是创建了一个引用,而不是复制了一份数组中的数值。改变原数组,复制的数组也会改变,改变复制的数组,原数组也会改变。

a = np.arange(4)
print(a)
# 浅复制
b = a

# 查看 a 和 b 是否完全等价
print(b is a)

# 改变原数组
a[0] = 67
print(a)
print(b)

# 改变复制的数组
b[3] = 73
print(a)
print(b)
[0 1 2 3]
True
[67  1  2  3]
[67  1  2  3]
[67  1  2 73]
[67  1  2 73]

深复制

使用 Array.copy() 进行深复制,通过深复制的数组,进行了复制但不关联,不会相互影响。

a = np.arange(4)
print(a)

# 深复制
b = a.copy()

a[0] = 67
print(a)
print(b)
[0 1 2 3]
[67  1  2  3]
[0 1 2 3]

理解 C++ 中的引用 (Reference)

什么是引用

引用是一个别名,也就是某个已存在的变量的另一个名字。对某个对象的引用进行操作,就是直接对这个对象进行操作。

创建一个引用

创建一个引用的语句如下:

类型标识符 & 引用变量名 = 目标变量名;

例如:

// 原始变量
int    a;
double b;

// 声明引用变量
int&    ref_a = a;
double& ref_b = b;

我们尝试对原始变量赋值,并且打印原始变量和引用变量的值:

#include <iostream>

using namespace std;

int main(){
    // 原始变量
    int    a;
    double b;

    // 声明引用变量
    int&    ref_a = a;
    double& ref_b = b;

    a = 9;
    cout << "a:" << i << endl;
    cout << "ref_a:" << r << endl;

    b = 6.7;
    cout << "b:" << d << endl;
    cout << "ref_b:" << s << endl;

    return 0;
}

编译运行,输出结果是:

a: 9
ref_a: 9
b: 6.7
ref_b: 6.7

这个输出结果是符合预期的。

引用的特点

与指针相区别,引用有以下的特点:

首先,不存在空引用。引用 只能也必须 在创建时被初始化为一个已存在的变量(连接到一块合法内存)。而创建指针时可以不赋初值,可以在任何时候被初始化,也可以为空。

int a = 9;
int& ref_a = a; // 正确,引用变量 ref_a 被初始化为 a
int& ref_a; // 错误,未给 ref_a 初值,编译会出错

第二,引用不能更换目标。一旦引用被初始化为一个对象,就不能被指向另一个对象。而指针可以在任何时候重新指向另一个对象。

double a = 17.09;
double b = 6.7;
double& ref_a = a; // 至此正确
double& ref_a = b; // 出错,不能多次指向

引用的应用

引用作为参数:以实现一个交换函数为例

下面我们以实现一个交换函数为例,对比指针和引用的使用方法。

void swap(__,__){

}

int main (){
    double a = 17.09, b = 6.7;
    swap(__,__);
    return 0;
}

引用实现

void swap(double &x, double &y){
    double temp;
    temp = y;
    y = x;
    x = temp;
}

调用时使用:

swap(a,b);

指针实现

void swap(double *x, double *b){
    double temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

调用时使用:

swap(&a,&b);

常引用

声明方式:const 类型标识符 & 引用变量名 = 目标变量名;

以 const 方式声明的引用,则不能通过引用对目标变量的值进行修改,从而使目标变量成为 const,比较安全。

int a;
const int &ref_a = a;
ref_a = 1; // 错误
a = 1; // 正确

引用作为返回值

声明方式:

类型标识符 & 函数名 (形参列表及类型说明){
    函数体
}

当函数返回一个引用时,实际上返回一个指向返回值的隐式指针。这使得函数就可以放在赋值语句的左边。例如:

#include <iostream>

using namespace std;

int vals[] = {6, 0, 7};

int& setValues(int i){
    return vals[i]; // 返回第 i 个元素的引用
}

int main(){
    setValues(1) = 1; // 改变第 2 个元素
    return 0;
}

更多细节

  • 存在指针数组,但不存在引用数组,也不能建立数组的引用。
    int* a[3] = {&x,&y,&z}; // 正确,定义了一个包含三个整型指针变量的指针数组
    int& a[3] = {x,y,z}; // 错误,不存在引用数组
    
    int b[2] = {6,7};
    int& ref_b = b; // 错误,不能对数组的建立引用
    
  • 引用本身不是一种数据类型,编译器不给引用分配存储单元。

  • 区分 & 用作引用变量和用作取地址符。

    int a = 9;
    int& ref_a = a; // & 用于引用变量的创建
    
    int b = 100;
    int *p_b;
    p_b = &N; // & 用作取地址符
    
  • &&r &*p 不被接受,而 *&p 可被接受。
    例如,以下的程序是不被接受的。

    int a;
    int &&ref = a; // 错误
    int &*p = a; // 错误
    

    但是如下程序是正确的:

    int *p;
    int *& q = p;
    

    (int *)(&q) = p;,相当于给指针 p 起了别名 q。

  • 引用在底层仍然是指针实现的,但引用更加符合面向对象和隐藏实现细节的原则,通过使用引用来替代指针,可以使 C++ 程序更容易阅读和维护,一定程度上避免了指针的几个缺点。例如指针可以为空,对指代对象不能为空的指针要做 null 检查,指针在赋值方面更加灵活,当然也更容易出错。

Overleaf 指南:30 分钟 LaTeX 入门

本文是对 Overleaf 提供的 Learn LaTeX in 30 minutes (30 分钟 LaTeX 入门指南)的中文翻译。

在这份指南中,我们希望给你关于 \LaTeX 的首个介绍。这份指南不需要你在之前有任何关于 \LaTeX 的知识,跟随这份指南你将完成你的第一份 \LaTeX 文档,并将对 \LaTeX 提供的一些基本功能有很好的了解。

什么是 LaTeX

\LaTeX(发音为 LAY-tekLAH-tek)是一个用于创建具有专业外观的文档的工具。它基于所见即所得的思想,这意味着写作者只需要关注文档的内容,而计算机负责将其格式化。用户不再需要像 Microsoft Word 或 LibreOffice Writer 中那样,在页面上用空格来控制格式,而是只需要输入纯文本,让 \LaTeX 处理剩下一切。

为什么要学习 LaTeX

\LaTeX 被广泛应用于科学文档、书籍以及许多其他出版物。它不仅可以创建精美的排版文档,而且还使得用户可以很快速地处理复杂的排版问题,比如输入数学公式、创建目录、管理引用、创建书目、保持布局一致等等。由于可用的开源软件包数量众多(稍后会详细介绍),因此 \LaTeX 有无限的可能性。这些软件包赋予了用户更多的能力,例如添加脚注,绘制原理图,创建表格等。
人们使用 \LaTeX 的最重要原因之一就是它分离了文档的内容与样式。这意味着你只需要编写文档的内容,我们就可以轻松更改其外观。同样,你也可以创建一个文档模板,用它来统一许多不同文档的外观,这样学术期刊可以创建投稿模板。这些模板具有预制的布局,只需要往里面添加内容即可。实际上,\LaTeX 有数百种模板,覆盖从简历到幻灯片的所有内容。

编写你的第一段 LaTeX

第一步是创建一个新的 \LaTeX 项目。你可以在自己的电脑上创建 .tex 文件,也可以 在 Overleaf 中启动新项目。让我们从最简单的示例开始:

\documentclass{article}

\begin{document}
First document. This is a simple example, with no
extra parameters or packages included.
\end{document}

可以看到,\LaTeX 已经对文本进行了格式化(如,首行缩进)。下面我们仔细看一下上面这段代码每个部分的功能。

代码的第一行声明了文档的类型,称为 类 (class)。类控制文档的整体外观,不同类型的文档需要选择不同的类,比如,简历与论文需要不同的类。在这个例子中,类是 article,是最简单和最常见的 \LaTeX 类。其他类型的文档可能需要使用不同的类,例如 bookreport

然后,在 \begin {document}\end {document} 这两个标记之间写入文档内容。这部分就是文档的 主体 (body),你可以在此处开始编写和更改文本。要在 PDF 中查看更改的结果,必须首先编译文档。在 Overleaf 中只需单击 重新编译(Recompile)。还可以单击重新编译按钮旁边的小箭头,并将 “自动编译” 设置为 “开”,这样编辑文件时项目将会自动重新编译。

如果你使用的是文本编辑器,例如 gedit、emacs、vim、sublime、记事本等,就必须手动编译文档。编译文档的命令是 pdflatex <your document>。更多关于编译的信息请 参见此处

如果你使用的是专用的 LaTeX 编辑器(例如 TeXmaker 或 TeXworks),也只需单击 “重新编译” 按钮。如果不确定位置在哪里,请查阅程序文档。

现在我们已经了解了如何向我们的文档中添加内容,下一步就是撰写标题。为此,我们必须讲一下文档的 序言 (preamble)

文档的序言

在上一个示例中,文本是在 \begin {document} 命令之后输入的。在这个命令之前 .tex 文件中的所有内容都称为 序言 (preamble)。在序言中,可以定义要编写的文档的类型,要编写的语言,要使用的包(稍后会详细介绍)和其他的元素。例如,普通文档的序言如下所示:

\documentclass[12pt, letterpaper]{article}
\usepackage[utf8]{inputenc}

下面我们详细解释一下这两行的作用。

\documentclass[12pt, letterpaper]{article}

如前所述,这条命令定义了文档的类型。跟上个示例不一样的是,方括号中还有两个参数,这些参数必须用逗号分隔。在这个示例中,这两个额外的参数分别设置字体大小(12pt)和纸张大小(letterpaper)。当然,可以使用其他字体大小(9pt11pt12pt 等等),但是如果未指定,则默认的字体大小为 10pt。纸张尺寸还可以设置为 a4paperLegalpaper;更多有关 页面大小和边距 的信息,可以参阅这篇文章。

\usepackage[utf8]{inputenc}

这行命令指定了文档的编码,可以省略或更改为其他编码,但建议使用 utf-8。除非特别需要其他编码,否则请将此行添加到序言中。

添加标题、作者和日期

要将标题、作者和日期添加到文档中,就必须 在序言中(不是文章的主体中)添加下面三行。它们是:

\title{First document}
这是文章的标题。

\author{Hubert Farnsworth}
在此处输入作者的姓名。

\thanks{funded by the Overleaf team}
author 命令的大括号里添加这条命令,可以添加上标和脚注。如果你需要在文章中感谢一个机构,这个功能将非常有用。

\date{February 2014}
你可以手动输入日期,或使用 \today 命令,以便在编译文档时自动更新日期。

现在,序言部分应该长这样:

\documentclass[12pt, letterpaper, twoside]{article}
\usepackage[utf8]{inputenc}

\title{First document}
\author{Hubert Farnsworth \thanks{funded by the Overleaf team}}
\date{February 2017}

现在,你已经为文档指定了标题、作者和日期,现在可以使用 \maketitle 命令在文档上打印这些信息。这条命令应该写在文档 主体 (body) 中你想要打印标题的位置。

\begin{document}

\maketitle

We have now added a title, author and date to our first \LaTeX{} document!

\end{document}

添加注释

与其他代码一样,注释是非常有用的。注释文本不会被打印,也不会以任何方式影响文档。在调试时,注释文本对于组织工作,做笔记或注释行 / 节很有用。要在 \LaTeX 中添加注释,只需在行首写一个%符号,如下所示:

\begin{document}

\maketitle

We have now added a title, author and date to our first \LaTeX{} document!

% This line here is a comment. It will not be printed in the document.

\end{document}

加粗、斜体和下划线

现在让我们来看一些简单的格式化命令。

  • 加粗:在 \LaTeX 中,加粗字体使用 \textbf{} 命令。
  • 斜体:在 \LaTeX 中,斜体使用 \textit{} 命令。
  • 下划线:在 \LaTeX 中,下划线使用 \underline{} 命令。

下面是示例:

Some of the \textbf{greatest}
discoveries in \underline{science}
were made by \textbf{\textit{accident}}.

另一个非常有用的命令是 \emph{...} 命令。 \emph 是强调文本命令,她完成的操作取决于上下文:在普通文本中,强调的文本是斜体,但是如果在斜体文本中使用,则将文字变为普通文本,请参见以下示例:

Some of the greatest \emph{discoveries}
in science
were made by accident.

\textit{Some of the greatest \emph{discoveries}
in science
were made by accident.}

\textbf{Some of the greatest \emph{discoveries}
in science
were made by accident.}

不过,有一些包(例如 Beamer),会改变 \emph 命令的作用。

添加图片

现在我们来看,如何向 \LaTeX 文档添加图片。在 Overleaf 中,你需要首先上传图片。

下面是一个如何添加图片的示例:

\documentclass{article}
\usepackage{graphicx}
\graphicspath{{images/} }

\begin{document}
The universe is immense and it seems to be homogeneous,
in a large scale, everywhere we look at.

\includegraphics{universe}

There's a picture of a galaxy above
\end{document}

\LaTeX 本身不能管理图像,因此需要使用一个 包 (package)。包可用于更改 \LaTeX 文档的默认外观,或实现更多功能。在这个例子中,要实现在文档中添加图片,因此需要使用 graphicx 包。graphicx 包提供了新的命令 \includegraphics{...}\graphicspath{...}。要使用 graphicx 软件包,要现在序言中添加:\usepackage{graphicx}

\graphicspath{{images/} } 告诉 \LaTeX,这些图像保存在当前目录下名为 images 的文件夹中。
\includegraphics {universe} 命令是将图像实际包含在文档中的命令。在这里,universe 是包含不带扩展名的图片文件的名称,图片文件名不应包含空格或多个点。

注意:\LaTeX 允许包含文件扩展名,但是最好忽略它。如果省略文件扩展名,它将提示 \LaTeX 搜索所有支持的格式。在上传图像文件时,通常也建议使用小写字母作为文件扩展名。有关更多详细信息,请参见有关 生成高分辨率和低分辨率图像 的内容。

标题、标签和引用

我们可以像下面这样,在 figure 环境中对图片添加标题、标签和引用。

\begin{figure}[h]
    \centering
    \includegraphics[width=0.25\textwidth]{mesh}
    \caption{a nice plot}
    \label{fig:mesh1}
\end{figure}

As you can see in the figure \ref{fig:mesh1}, the
function grows near 0. Also, in the page \pageref{fig:mesh1}
is the same example.

在这个示例中,有三个重要的命令:

  • \caption{a nice plot}:此命令为图形设置标题。你可以将这条命令放置在图的上方或下方。
  • \label{fig:mesh1}:如果你需要在文档中引用图像,请使用这条命令为图像设置标签。标签可以为图像编上号,并与下一个命令结合,对图片进行引用。
  • \ref{fig:mesh1}:这条命令在编译后将显示替换为被引用图片对应的编号。

将图像放置在 \LaTeX 文档中时,应始终将它们放置在 figure 环境或类似环境中,以便 \LaTeX 适配图像和文字。

注意:如果你在自己的计算机上使用标题和引用功能,则必须两次编译文档才能使引用正常工作。Overleaf 会自动完成此操作。”

在 LaTeX 中创建列表

\LaTeX 中创建列表非常简单。你可以使用不同的 环境 (environment) 来创建不同形式的列表。环境 (environment) 是我们文档中具有不同呈现形式的各个部分。它们以 \begin{...} 命令开始,以 \end{...} 命令结束。

列表主要有两种类型,有序列表和无序列表。分别使用不同的环境。

无序列表

无序列表是由 itemize 环境生成的。每个条目之前必须有 \item,如下所示。

\begin{itemize}
  \item The individual entries are indicated with a black dot, a so-called bullet.
  \item The text in the entries may be of any length.
\end{itemize}

默认情况下,各个条目用黑点表示。条目中的文本可以是任何长度。

有序列表

有序列表在 enumerate 环境中创建,针对条目的语法与无序列表一致。

\begin{enumerate}
  \item This is the first entry in our list
  \item The list numbers increase with each entry we add
\end{enumerate}

与无序列表一样,每个条目前必须添加 \item,它将自动生成标记该项目的数字,由从 1 开始。

在 LaTeX 中添加数学表达式

\LaTeX 的主要优点之一是易于编写数学表达式。\LaTeX 中有两种模式用于数学表达式:** 内联 (inline)** 模式和 显示 (display) 模式。第一种模式(内联)编写的公式是文本中的一部分,第二种模式(显示)编写的公式不在段落中,而是放在单独的行上。让我们看一个内联模式的例子:

In physics, the mass-energy equivalence is stated
by the equation E=mc^2, discovered in 1905 by Albert Einstein.

要在内联模式下添加数学表达式,可以使用以下定界符之一:\(... \)$ ... $\begin{math} ... \end{math}。它们作用相同,选择哪个完全取决于个人喜好。

而显示模式有两种版本:编号和非编号。

The mass-energy equivalence is described by the famous equation

\[E=mc^2 \]

discovered in 1905 by Albert Einstein.
In natural units (c = 1), the formula expresses the identity

\begin{equation}
E=m
\end{equation}

要在显示模式下打印方程式,可以使用以下定界符之一:\[... \]\begin{displaymath} ... \end{displaymath}\begin{equation} ... \end{equation}不鼓励 使用 $$ ... $$,因为它会产生不一致的间距,而且可能不适用于某些数学软件包。

重要说明:equation* 环境是由外部软件包提供的,请参阅 amsmath 文章

许多数学命令都需要用到 amsmath 包,因此在编写数学表达式时请确保引入了这个包。下面列举了一些基本的数学命令。

Subscripts in math mode are written as a_b and superscripts are written as a^b. These can be combined an nested to write expressions such as

% 上标和下表分别用 a_ba^b,将它们组合起来使用可以写出下面的表达式。

\[T^{i_1 i_2 \dots i_p}_{j_1 j_2 \dots j_q} = T(x^{i_1},\dots,x^{i_p},e_{j_1},\dots,e_{j_q}) \]

We write integrals using \int and fractions using \frac{a}{b}. Limits are placed on integrals using superscripts and subscripts:

% 我们使用 \int 编写积分,使用 \frac{a}{b} 编写分数。极限使用上标和下标放置在积分上:

\[\int_0^1 \frac{1}{e^x} =  \frac{e-1}{e} \]

Lower case Greek letters are written as \omega \delta etc. while upper case Greek letters are written as \Omega \Delta.

% 小写希腊字母像这样写:\omega \delta,大写希腊字母像这样写:\Omega \Delta

Mathematical operators are prefixed with a backslash as \sin(\beta), \cos(\alpha), \log(x) etc.

% 数学运算符的前缀为反斜杠,例如 \sin(\beta), \cos(\alpha), \log(x) 等。

还有太多有关在 \LaTeX 中书写数学表达式的知识无法在这里一一提及。可以查看下面这些文章:

基本格式

现在,我们来研究如何编写摘要以,及如何将 \LaTeX 文档格式化为不同的章节,分节和段落。

概要

在科学文献中,通常会在概要部分里面简述论文的主要内容。在 \LaTeX 中有针对概要部分设计的环境。概要环境会将文本以特殊格式放在文档顶部。

\begin{document}

\begin{abstract}
This is a simple paragraph at the beginning of the
document. A brief introduction about the main subject.
\end{abstract}
\end{document}

段落和新行

\begin{document}

\begin{abstract}
This is a simple paragraph at the beginning of the
document. A brief introduction about the main subject.
\end{abstract}

Now that we have written our abstract, we can begin writing our first paragraph.

This line will start a second Paragraph.
\end{document}

编写文档内容时,如果需要开始新段落,则必须按两次 “Enter” 键(以插入双空行)。注意,\LaTeX 会自动缩进段落。
要在开始新段落的情况下开始新行,请插入一个 换行点,可以通过 \\(在示例中为双反斜杠)或 \newline 命令来完成。

注意不要使用多个 \\\newlines 来 “模拟” 段落之间具有较大间距,因为这可能会干扰 \LaTeX 的排版算法。推荐的方法是继续使用双空行来创建没有任何 \\ 的新段落,然后将 \usepackage{parskip} 添加到序言中。

你可以在这篇有关 段落和换行 的文章中找到更多信息。

章节和分段

用来组织文档的命令因文档类型而异,最简单的组织形式是分节,它对所有文档格式均可用。

\chapter{First Chapter}

\section{Introduction}

This is the first section.

Lorem  ipsum  dolor  sit  amet,  consectetuer  adipiscing
elit.   Etiam  lobortisfacilisis sem.  Nullam nec mi et
neque pharetra sollicitudin.  Praesent imperdietmi nec ante.
Donec ullamcorper, felis non sodales...

\section{Second Section}

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Etiam lobortis facilisissem.  Nullam nec mi et neque pharetra
sollicitudin.  Praesent imperdiet mi necante...

\subsection{First Subsection}
Praesent imperdietmi nec ante. Donec ullamcorper, felis non sodales...

\section*{Unnumbered Section}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Etiam lobortis facilisissem

命令 \section{} 标记一个新分节的开始,在大括号内设置标题。分节编号是自动的,也可以通过在命令中加一个 * 来禁用编号,像这样:\section*{}。我们也可以有 \subsection{},甚至 \subsubsection{}。下面列出了基本的标题深度级别:

深度 标记
-1 \part{part}
0 \chapter{chapter}
1 \section{section}
2 \subsection{subsection}
3 \subsubsection{subsubsection}
4 \paragraph{paragraph}
5 \subparagraph{subparagraph}

请注意,\part\chapter 仅在 report 和 book 类中可用。
有关文档结构的更完整讨论,请参阅 这篇文章

创建表格

在 LaTeX 中创建一个简单的表格

\begin{center}
\begin{tabular}{ c c c }
 cell1 & cell2 & cell3 \\
 cell4 & cell5 & cell6 \\
 cell7 & cell8 & cell9
\end{tabular}
\end{center}

tabular 环境是创建表的默认 \LaTeX 方法。你必须为此环境指定一个参数,这个例子里是 {c c c}。这告诉 \LaTeX,表格将有三列,每列中的文本必须居中。你还可以使用 r 将文本向右对齐,使用 l 进行左对齐。符号 & 是分隔符,每行中的分隔符必须始终少于列数。要转到表格的下一行,需要使用换行命令 \\。我们将整个表包装在 center 环境中,以让它出现在页面的中心。

添加边框

tabular 环境很灵活,你可以在每列之间放置分隔线。

\begin{center}
\begin{tabular}{ |c|c|c| }
 \hline
 cell1 & cell2 & cell3 \\
 cell4 & cell5 & cell6 \\
 cell7 & cell8 & cell9 \\
 \hline
\end{tabular}
\end{center}

你可以使用水平线命令 \hline 和垂直线参数 | 来添加边框。

  • {|c|c|c|}:这声明表中将会有由垂直线分隔的三列。| 符号指定这些列应由垂直线分隔。
  • \hline:这条命令将插入一条水平线。这个示例中,我们在表格的顶部和底部加入了水平线。\hline 的使用次数没有限制。

在下面你可以看到第二个示例。

\begin{center}
 \begin{tabular}{||c c c c||}
 \hline
 Col1 & Col2 & Col2 & Col3 \\ [0.5ex]
 \hline\hline
 1 & 6 & 87837 & 787 \\
 \hline
 2 & 7 & 78 & 5415 \\
 \hline
 3 & 545 & 778 & 7507 \\
 \hline
 4 & 545 & 18744 & 7560 \\
 \hline
 5 & 88 & 788 & 6344 \\ [1ex]
 \hline
\end{tabular}
\end{center}

\LaTeX 中创建表有时会有些棘手,因此你可能需要 TablesGenerator.com 这样的在线工具导出表格的 \LaTeX 代码。“文件”>“粘贴表数据” 选项从电子表格软件粘贴数据。

标题、标签和引用

你可以使用与图片几乎相同的方式来为表格添加标题、标签和引用。唯一的区别是,使用 table 环境代替了 figure 环境。

Table \ref{table:data} is an example of referenced \LaTeX{} elements.

\begin{table}[h!]
\centering
\begin{tabular}{||c c c c||}
 \hline
 Col1 & Col2 & Col2 & Col3 \\ [0.5ex]
 \hline\hline
 1 & 6 & 87837 & 787 \\
 2 & 7 & 78 & 5415 \\
 3 & 545 & 778 & 7507 \\
 4 & 545 & 18744 & 7560 \\
 5 & 88 & 788 & 6344 \\ [1ex]
 \hline
\end{tabular}
\caption{Table to test captions and labels}
\label{table:data}
\end{table}

注意:如果你在自己的计算机上使用标题和引用功能,则必须两次编译文档才能使引用正常工作。Overleaf 会自动完成此操作

添加目录

创建目录很简单,使用 \tableofcontents 即可完成所有工作:

\documentclass{article}
\usepackage[utf8]{inputenc}

\title{Sections and Chapters}
\author{Gubert Farnsworth}
\date{ }

\begin{document}

\maketitle

\tableofcontents

\section{Introduction}

This is the first section.

Lorem  ipsum  dolor  sit  amet,  consectetuer  adipiscing
elit.   Etiam  lobortisfacilisis sem.  Nullam nec mi et
neque pharetra sollicitudin.  Praesent imperdietmi nec ante.
Donec ullamcorper, felis non sodales...

\addcontentsline{toc}{section}{Unnumbered Section}
\section*{Unnumbered Section}

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Etiam lobortis facilisissem.  Nullam nec mi et neque pharetra
sollicitudin.  Praesent imperdiet mi necante...

\section{Second Section}

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Etiam lobortis facilisissem.  Nullam nec mi et neque pharetra
sollicitudin.  Praesent imperdiet mi necante...

\end{document}

section/subsection/chapter 将会自动加入目录当中。如果需要加入手动添加目录条目,比如需要将非编号的 section 加入目录,需要使用 \addcontentsline,像示例中那样。

下载你完成了的文档

你可以通过单击左上角的 “菜单” 按钮来下载完成的 PDF。还有一个更快的方法,就是单击 PDF 查看器上的 “下载 PDF” 按钮,如下所示。

Windows 下 manim 引擎的安装

manim 引擎是由 3Blue1Brown 开源的动画引擎,可以通过编程的方式创建动画。3Blue1Brown 使用这一引擎制作了大量的数学领域的可视化视频。

manim 的 GitHub 仓库:https://github.com/3b1b/manim
3Blue1Brown 的视频频道:YouTube 官方中文 bilibili

依赖安装

manim 依赖于 Cario, FFmpeg, SoX, LaTeX 运行,LaTeX 事实上是可选的,但是考虑到需要 LaTeX 来更加优雅地展示数学公式,通常还是需要安装 LaTeX。

Cario

Cairo 是一个 2D 图形库,支持多种输出设备,旨在在所有输出媒介上,产生一致的输出,同时利用显示硬件加速。

下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo

选择对应的版本下载,并使用 pip 安装即可:
python3 -m pip install ***.whl

FFmpeg

FFmpeg 是一个领先的跨平台多媒体框架,支持解码、编码、转码、混流等等多媒体操作。

下载地址:http://ffmpeg.org/download.html

选择 Windows 分类下的 build 下载即可,下载解压后得到如下文件:

此时需要将 bin 目录的路径添加到系统环境变量中。方法是:在「此电脑」上右击,点击「属性」,点击「高级系统设置」,点击「环境变量」,将 bin 目录的路径添加到 Path 变量中。

SoX

SoX(SoX eXchange)是一个跨平台的命令行实用程序,可以将各种格式的计算机音频文件转换为其他格式。它还可以对这些声音文件应用各种效果,此外,SoX 还可以在大多数平台上播放和录制音频文件。

下载地址:https://sourceforge.net/projects/sox/files/sox/

下载安装即可。

LaTeX

这里我们选择 MiKTeX。

下载地址:https://miktex.org/download

下载后安装即可。

manim 库安装

manim 库基于 Python3.7 运行,所以至少需要保证 Python 版本在 3.7 及以上。

我们可以使用 pip 直接安装 manim 库:

python3 -m pip install manimlib

我在实际安装时遇到了如下的报错:

UnicodeDecodeError: 'gbk' codec can't decode byte 0x9c in position 1903: illegal multibyte sequence

从报错信息可以看出,这个问题是由编码导致的,好消息是,在 Windows 10 平台上,已经可以通过修改系统默认编码的方式很便捷地解决这个问题,具体地操作步骤如下:

打开「时间和语言」设置,选择「区域」。

点击「其他日期、时间和区域设置」。

点击「区域」。

在「管理」选项卡中点击「更改系统区域设置」。

勾选「Beta 版:使用 Unicode UTF-8 提供全球语言支持」。

随后重新跑 pip 安装,无报错。根据系统具体版本的不同,以上设置的具体显示可能不同,但位置大致相似。

Windows 下 VSCode 搭建 C 开发环境

本文介绍在 Windows 下进行 VSCode C 运行环境搭建的方法。本文介绍的是使用 Code Runner 的配置方法,在对调试功能要求不高时,Code Runner 使用起来非常方便。针对调试功能的配置可以参考文末推荐的两篇文章。

一、安装 VSCode C/C++ 插件

C/C++ 插件由 Microsoft 官方提供,可以为 C/C++ 程序提供智能提示和调试功能。

二、安装 MinGW-w64

MinGW-w64 改进自原始 mingw.org 项目,是个精简的Windows平台的 C/C++、ADA 及 Fortran 编译器,相比 Cygwin 而言,体积更小,使用较为方便。MinGW编译的可执行文件能够独立在Windows上运行。

MinGW-w64下载地址:https://sourceforge.net/projects/mingw-w64/

安装的配置选项如下:
* Version:指定 GCC 版本,选择最高版本即可。
* Architecture:指定架构,64位系统选择 x86_64。
* Threads:指定 API 模式,使用默认选项即可。
* Exception:指定异常处理方式,seh 面向 64 位,sjlj 可兼容 32 位。
* Build revision:指定修订版本,使用默认选项即可。

下一步,指定安装位置,开始联网下载安装即可。

三、环境变量配置

按以下步骤打开系统环境变量配置:
1. 在「此电脑」上右击,选择「属性」

  1. 点击「高级系统设置」

  2. 点击「环境变量」

  3. 找到并打开 Path 变量

  4. 添加环境变量
    将 MinGW-w64 安装路径中的 bin 目录添加到环境变量。bin 目录的路径通常为%MinGW-w64安装路径%\mingw64\bin
    例如我的路径是:C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin

接下来测试环境变量是否配置正确:
在命令行输入gcc --version,如果看到正确返回版本信息,则配置成功。

四、Code Runner 插件安装与配置

Code Runner 插件可以极其方便地完成一键编译运行,免去设置launch.jsontask.json的麻烦,除非要进行极其复杂的dubug,简单的任务通过 Code Runner 都可以完成。

插件商店搜索 Code Runner 并安装。

让我们来准备一段简单的测试程序吧!

#include <stdio.h>  

int main(){  
    printf("hello world!\n");  
}  

可以看到,Code Runner 插件安装完毕之后右上角出现了一个三角形的运行按钮,点按按钮即可开始程序运行。

程序的执行结果显示在输出面板中,同时还会生成一个 .exe 的可执行文件。这相当于是在目录中执行了 gcc temp.c -o temp

但这是有一个问题,上面的执行方法没有办法处理有输入的情况,所以我们需要进行下面的设置:

第一步,打开扩展设置:

第二步,更改 Run in Terminal 设置:

勾选 Run in Terminal 前的复选框,程序将在VSCode的内嵌 Terminal 中运行,满足输入的需要。

以上我们通过 Code Runner 完成了 VSCode 的简单 C 运行环境搭建。如果需要进行调试,则依赖于在 VSCode 中进行 json 配置(可以参考推荐文章)。

推荐文章:
万字长文把 VSCode 打造成 C++ 开发利器
VS Code 搭建 C/C++ 编译运行环境的四种方案