Engee 文档

矢量化和逻辑索引

矢量化

*矢量化*是对函数进行调整,以同时处理矢量或矩阵的所有元素,而不是使用循环和标量操作。Engee*编程语言Julia支持矢量化操作。矢量化具有以下优点:

  • 减少代码行数,降低出错几率;

  • 矢量化代码通常看起来像数学表达式,因此更容易理解和验证;

  • 矢量化代码运行速度更快,因为 Julia 能高效处理矢量和矩阵运算。

在运算符(".*`"、".^"、"./`")前添加圆点可将其转化为数组运算,从而将其应用于特定的数组元素。支持矢量化的基本运算符包括 `+-**/^ 和比较运算,如 <, ><=>===!=

在 Julia 中,通过在运算符前添加点,几乎所有基本运算都支持矢量化。下面是可以矢量化的基本运算列表:

List of operations that support vectorisation in Julia

以下是 Julia 中支持矢量化的基本操作列表。不过,由于 Julia 中的矢量化扩展到了许多内置函数,因此确切的列表还包括更多的函数。重要的是要明白,矢量化不仅可以应用于基本算术和逻辑运算符,还可以应用于数学函数、标准库中的某些函数(如果它们支持数组运算,例如 .min.max)以及用户自定义函数(如果它们被设计为支持数组运算)。

*算术运算:

  1. .+ - 逐元素加法;

  2. .- - 逐元素减法;

  3. .* - 逐元素乘法;

  4. ./ -元素除法;

  5. .^ - 元素升度;

  6. .% - 元素除法的余数。

*比较运算:

  1. .== - 元素相等;

  2. .!= - 元素不等式;

  3. .> - 元素大;

  4. .< - 元素小;

  5. .>= - 元素大于或等于;

  6. .< - 元素小于或等于。

*逻辑运算:

  1. .& - 元素逻辑 "和";

  2. .| - 元素逻辑 "或";

  3. .⊻ - 元素逻辑 "排他或";

  4. .! - 元素逻辑 "非"。

*位操作:

  1. .>> - 逐元素向右移动;

  2. .<< - 元素向左移动;

  3. .& - 逐元素位操作 "和";

  4. .| - 逐元素按位 "或";

  5. .⊻ - 逐元素位操作 "排他或";

  6. .~ - 逐元素位操作 "非"。

*单位运算:

  1. .√ - 元素平方根;

  2. .log - 元素对数;

  3. .exp - 元素指数变换;

  4. .sin.cos、`.tan`和其他三角函数也支持逐元素运算。

并非所有操作都需要矢量化。例如,赋值运算符 = 和一些特定函数(例如,处理整个数组,如 sort )不支持矢量化。

支持的操作

矢量化允许同时对数组的所有元素进行操作。这减少了对显式循环的需求(显式循环会减慢程序执行速度),并允许编译器优化执行。

在 Julia 中,矢量化通常是通过内置函数和数组操作工具实现的,这样就可以轻松创建与数学公式非常相似的代码。例如,你想计算从 "0 "到 "10 "的 "1001 "个值的正弦值。典型的 Julia 代码如下

y = []
for t in 0:0.01:10
    push!(y, sin(t))
end

那么矢量化后的代码可能是这样的

t = 0:0.01:10
y = sin.(t)

这里的 sin.(t) 表示函数 sin 应用于数组 t 的每个元素。这种代码变体比第一种更快,也更有效地利用了 Julia 作为数学计算语言的能力。

在Julia中,你可以同时对数组中的所有元素进行运算。例如,如果你有关于 "10000 "个几何圆锥的直径 "D "和高度 "H "的数据,你就可以不使用循环来计算它们的体积:

D = rand(10_000)
H = rand(10_000)
V = (1 / 12) * π * D.^2 .* H

逻辑索引

*逻辑索引*是一种根据逻辑条件访问数组元素的方法。它不指定元素索引,而是传递一个逻辑值(true`或`false)数组,其中`true`表示要选择哪些元素。这简化了数据筛选,尤其是在处理大型数组时。逻辑索引允许

  • 快速选择符合特定条件的数据;

  • 消除不正确或不需要的值;

  • 用最少的代码行创建简洁的算法。

示例

循环替换

在执行特定任务时,矢量化尤其有用。例如,查找数组中每五个元素的累计和。如果没有矢量化,代码将如下所示:

x = 1:10000
y = []
for n in 5:5:length(x)
    push!(y, sum(x[1:n]))
end

那么矢量化后的代码将如下所示:

x = 1:10000
y = cumsum(x)[5:5:end]

这里的 cumsum 函数计算所有数组元素的累加和,从而避免了使用循环。

矢量化和逻辑索引

Julia 支持逻辑运算的矢量化。例如,如果我们有圆锥体大小的数组,而其中一些直径值是负数,我们就可以使用矢量化轻松确定正确的值。

为此,首先创建数组 DH 并计算圆锥体的体积:

# Определяем массивы D и H
D = [-0.2, 1.0, 1.5, 3.0, -1.0, 4.2, 3.14]
H = [1.0, 2.0, 2.5, 3.5, 1.5, 4.5, 3.0]

# Рассчитываем объемы
V = (1 / 12) * π * D.^2 .* H

结果将是一个包含计算出的体积的数组 V,您可以在其中应用逻辑索引(参见第.C 节)。 逻辑索引章节),只筛选出正确的值。逻辑运算将创建一个包含 truefalse 的数组,其中 true 代表正值:

valid_D = D .>= 0
Vgood = V[valid_D]

现在,变量 Vgood 只包含直径为正值的锥体的体积:

engee> 5-element Vector{Float64}:
  0.5235987755982988
  1.4726215563702154
  8.246680715673207
 20.78163540349648
  7.743711731833481

例如,在 Julia 中,逻辑索引可以与矢量化相结合:

# Исходные данные (массив температур)
temps = [15.2, -3.0, 22.5, 0.0, 25.1, -10.5, 30.0]

# Условие - отбор только положительных значений
positive_temps = temps[temps .> 0]

println(positive_temps)

逻辑索引不仅可以应用简单条件,还可以应用复合条件。例如

# Исходные данные (массив лет)
ages = [25, 17, 34, 45, 15, 27, 18]

# Условие - отбор совершеннолетних (>= 18), но моложе 30
valid_ages = ages[(ages .>= 18) .& (ages .< 30)]

println(valid_ages)

矩阵操作

通常情况下,矢量化有助于创建所需大小和结构的矩阵。例如,如果需要创建一个所有元素都等于 "10 "的 "5x5 "矩阵,可以使用 "fill "函数:

A = fill(10, 5, 5)

上面的代码将输出结果:

5×5 Matrix{Int64}:
 10  10  10  10  10
 10  10  10  10  10
 10  10  10  10  10
 10  10  10  10  10
 10  10  10  10  10

使用矢量化,如果不同大小的矩阵在操作broadcast 时是兼容的,那么它们就可以被折叠。例如,如果矩阵 A 是一个 3 × 3 矩阵,而 B 是一个长度为 3 的向量:

A = [1 2 3; 4 5 6; 7 8 9]
B = [1, 2, 3]
C = A .+ B

这将创建一个新矩阵 C,其中向量 B 的每个元素都被添加到矩阵 A 的相应列中。结果将是:C = [2 3 4; 6 7 8; 10 11 12]


考虑一下 Julia 中的乘法运算。乘法运算有两种:

  • 执行标准矩阵乘法。 执行与线性代数相对应的矩阵乘法运算。结果矩阵中的每个元素都是通过计算第一个矩阵的行元素与第二个矩阵的列元素的乘积之和得到的;

  • .`是分段乘法。.` 是逐元素乘法,即第一个矩阵的每个元素乘以第二个矩阵的相应元素。

例如

# Определение двух матриц
A = [1 2; 3 4]  # Матрица 2x2
B = [2 0; 1 3]  # Матрица 2x2

# Матричное умножение
C = A * B

# Поэлементное умножение
D = A .* B

结果:

  • 对于 CC = [4 6; 10 12]

  • 对于 DD = [2 0; 3 12]

如果矩阵乘以标量,则两个运算符(.)的作用相同。