Julia 教程 / 数组与矩阵运算
数组与矩阵运算
1. 数组构造
字面量
# 一维数组(Vector)
v = [1, 2, 3, 4, 5]
typeof(v) # Vector{Int64}(即 Array{Int64, 1})
# 二维数组(Matrix)
m = [1 2 3; 4 5 6]
# 2×3 Matrix{Int64}:
# 1 2 3
# 4 5 6
# 混合类型
mixed = [1, 2.0, "three"]
typeof(mixed) # Vector{Any}
# 空数组
empty_int = Int[] # 空的 Int64 数组
empty_float = Float64[] # 空的 Float64 数组
构造函数
# 未初始化
A = Array{Float64}(undef, 3, 4) # 3×4 未初始化矩阵
# 全零
Z = zeros(3, 3) # 3×3 Float64 零矩阵
Z = zeros(Int, 3, 3) # 3×3 Int64 零矩阵
# 全一
O = ones(2, 3) # 2×3 Float64 全一矩阵
# 单位矩阵
I = Matrix{Float64}(I, 3, 3) # 3×3 单位矩阵
# 或
using LinearAlgebra
I = I(3) # 3×3 UniformScaling
# 对角矩阵
D = Diagonal([1, 2, 3])
# 等间距向量
r = range(0, 1, length=11) # 0.0:0.1:1.0
r = collect(0:0.1:1.0) # 转为数组
# 随机数组
rand(3) # 3 个 [0,1) 均匀分布随机数
randn(3, 3) # 3×3 标准正态分布随机数
rand(1:10, 5) # 5 个 [1,10] 随机整数
rand(['a','b','c'], 5) # 5 个随机字符
# 填充
fill(42, 3, 3) # 3×3 全 42 矩阵
fill("x", 5) # 5 个 "x"
2. 多维数组与 reshape
# 创建 1D 数组
v = collect(1:12)
# reshape 为 2D
m = reshape(v, 3, 4)
# 3×4 Matrix{Int64}:
# 1 4 7 10
# 2 5 8 11
# 3 6 9 12
# 注意:列优先(column-major)排列
# reshape 为 3D
t = reshape(v, 2, 3, 2)
# 使用 : 自动计算维度
reshape(v, 3, :) # 3×4(自动计算第二维)
# 展平为 1D
vec(m) # 12 元素向量
# 维度查询
ndims(m) # 2
size(m) # (3, 4)
size(m, 1) # 3(行数)
size(m, 2) # 4(列数)
length(m) # 12
⚠️ 注意: Julia 使用列优先(column-major) 存储,与 MATLAB 相同,与 C/Python(行优先)不同。
reshape按列填充。
3. 数组索引
整数索引
A = [10 20 30; 40 50 60; 70 80 90]
A[1, 1] # 10(行,列)
A[2, 3] # 60
A[end, end] # 90(最后一个元素)
A[end-1, :] # [40, 50, 60](倒数第二行)
# 线性索引(按列展开)
A[1] # 10
A[2] # 40
A[5] # 50
范围索引
A[1:2, :] # 前两行
A[:, 2:3] # 后两列
A[1:2, 1:2] # 左上角 2×2 子矩阵
A[2:end, :] # 第二行到最后一行
逻辑索引
A = [1, -2, 3, -4, 5]
# 布尔掩码
mask = A .> 0 # [true, false, true, false, true]
A[mask] # [1, 3, 5]
# 一步到位
A[A .> 0] # [1, 3, 5]
A[isodd.(A)] # [1, 3, 5]
# 二维逻辑索引
M = [1 2; 3 4; 5 6]
M[M .> 3] # [5, 4, 6](按列展开)
笛卡尔索引
A = reshape(1:12, 3, 4)
# CartesianIndex
A[CartesianIndex(2, 3)] # 8(第 2 行第 3 列)
# 用于 findmax/findmin
val, idx = findmax(A)
# val = 12, idx = CartesianIndex(3, 4)
4. 视图 vs 拷贝
A = [1 2 3; 4 5 6; 7 8 9]
# 切片返回拷贝(默认)
B = A[1:2, 1:2]
B[1, 1] = 999
A[1, 1] # 仍然是 1(未被修改)
# 视图(共享内存)
V = @view A[1:2, 1:2]
V[1, 1] = 999
A[1, 1] # 999(被修改了!)
# 等价写法
V = view(A, 1:2, 1:2)
# @views 宏(整块代码使用视图)
@views begin
B = A[1:2, 1:2] # 自动使用视图
C = A[:, 1] # 自动使用视图
end
💡 提示:
@view不分配新内存,性能更好。但持有视图会阻止原数组被垃圾回收。处理大数组时推荐使用视图。
5. 数组推导式
# 一维
squares = [x^2 for x in 1:10]
# 二维(生成矩阵)
M = [i + j for i in 1:3, j in 1:3]
# 3×3 Matrix{Int64}:
# 2 3 4
# 3 4 5
# 4 5 6
# 带过滤
evens = [x for x in 1:20 if iseven(x)]
# 嵌套推导式
coords = [(x, y) for x in 1:3 for y in 1:3]
# 生成器(惰性求值,不分配内存)
sum_of_squares = sum(x^2 for x in 1:1_000_000)
# 条件赋值
result = [isodd(x) ? x : x÷2 for x in 1:10]
6. 数组拼接
A = [1 2; 3 4]
B = [5 6; 7 8]
# 垂直拼接(行方向)
vcat(A, B)
# 4×2 Matrix{Int64}:
# 1 2
# 3 4
# 5 6
# 7 8
# 水平拼接(列方向)
hcat(A, B)
# 2×4 Matrix{Int64}:
# 1 2 5 6
# 3 4 7 8
# 语法糖
[A; B] # vcat
[A B] # hcat
# 沿任意维度拼接
cat(A, B, dims=1) # 等价于 vcat
cat(A, B, dims=2) # 等价于 hcat
# 三维拼接
cat(A, B, dims=3) # 2×2×2 数组
# 矩阵与向量拼接
v = [9, 10]
hcat(A, v) # 2×3 矩阵
7. 广播(Broadcasting)
广播是 Julia 最强大的数组操作之一,使用 . 语法:
# 标量与数组运算(自动广播)
A = [1, 2, 3, 4, 5]
A .+ 1 # [2, 3, 4, 5, 6]
A .* 2 # [2, 4, 6, 8, 10]
A .^ 2 # [1, 4, 9, 16, 25]
# 数组与数组广播
B = [10, 20, 30, 40, 50]
A .+ B # [11, 22, 33, 44, 55]
# 函数广播
sqrt.(A) # [1.0, 1.414, 1.732, 2.0, 2.236]
log.(A) # [0.0, 0.693, 1.099, 1.386, 1.609]
# 二维广播
M = [1 2 3; 4 5 6]
M .+ [10, 20] # 列广播(每列加不同的值)
M .+ [10 20 30] # 行广播(每行加不同的值)
# 广播赋值
A = zeros(3, 3)
A .= sin.([1, 2, 3]) .* [1 2 3] # 原地修改
# 广播与条件
x = [-2, -1, 0, 1, 2]
result = x .> 0 ? x : 0 # ERROR: 不行
result = ifelse.(x .> 0, x, 0) # [0, 0, 0, 1, 2]
# 嵌套广播
f.(g.(x)) # 等价于
@. f(g(x)) # @. 宏自动给所有操作加点
💡 提示:
@.宏将表达式中所有操作都变为广播,非常方便:
x = [1.0, 2.0, 3.0]
# 手动加点
y = sqrt.(log.(abs.(x)) .+ 1)
# 使用 @.
y = @. sqrt(log(abs(x)) + 1)
8. Array{T,N} 类型参数
# T = 元素类型,N = 维度数
Array{Int64, 1} # Vector{Int64}
Array{Float64, 2} # Matrix{Float64}
Array{Any, 3} # 3D Any 数组
# 类型别名
const Vector{T} = Array{T, 1}
const Matrix{T} = Array{T, 2}
# 类型层次
supertype(Vector{Int}) # AbstractVector{Int}
supertype(Matrix{Float64}) # AbstractMatrix{Float64}
# 类型参数约束
function typed_sum(v::Vector{T}) where T<:Number
s = zero(T)
for x in v
s += x
end
return s
end
# 稀疏矩阵
using SparseArrays
S = sprand(1000, 1000, 0.01) # 1000×1000,1% 非零
nnz(S) # 非零元素数
9. 常用数组操作速查表
查询
| 函数 | 说明 |
|---|---|
size(A) | 维度大小 |
length(A) | 元素总数 |
ndims(A) | 维度数 |
eltype(A) | 元素类型 |
isempty(A) | 是否为空 |
count(!iszero, A) | 非零元素数 |
findall(x -> x > 0, A) | 满足条件的索引 |
findfirst(isequal(3), A) | 第一个匹配的索引 |
变换
| 函数 | 说明 |
|---|---|
copy(A) | 浅拷贝 |
deepcopy(A) | 深拷贝 |
reverse(A) | 反转 |
sort(A) | 排序 |
sort!(A) | 原地排序 |
permute!(A, p) | 按排列重排 |
circshift(A, n) | 循环移位 |
unique(A) | 去重 |
rotl90(A) | 逆时针旋转 90° |
transpose(A) | 转置 |
归约
| 函数 | 说明 |
|---|---|
sum(A) | 总和 |
prod(A) | 总积 |
maximum(A) | 最大值 |
minimum(A) | 最小值 |
extrema(A) | (最小, 最大) |
mean(A) | 均值(需要 using Statistics) |
std(A) | 标准差 |
cumsum(A) | 累积和 |
cumprod(A) | 累积积 |
diff(A) | 差分 |
多维归约
M = [1 2 3; 4 5 6]
sum(M, dims=1) # 按列求和 → [5 7 9]
sum(M, dims=2) # 按行求和 → [6; 15]
maximum(M, dims=1) # 按列取最大 → [4 5 6]
# dropdims 去掉长度为 1 的维度
dropdims(sum(M, dims=1), dims=1) # [5, 7, 9]
10. 矩阵运算
using LinearAlgebra
A = [1.0 2.0; 3.0 4.0]
B = [5.0 6.0; 7.0 8.0]
# 矩阵乘法
A * B # 矩阵乘法
A \ b # 求解 Ax = b
A / B # A * inv(B)
# 转置
A' # 共轭转置
transpose(A) # 非共轭转置(实数矩阵相同)
# 逆矩阵
inv(A)
# 行列式
det(A)
# 特征值
eigen(A)
# 奇异值分解
svd(A)
# 秩
rank(A)
# 条件数
cond(A)
# 解线性方程组
b = [1.0, 2.0]
x = A \ b # 求解 Ax = b(推荐,比 inv(A)*b 更快更稳定)
# LU 分解
lu(A)
# QR 分解
qr(A)
# Cholesky 分解(正定矩阵)
C = A * A'
cholesky(C)
| 运算 | 函数 | 说明 |
|---|---|---|
| 矩阵乘法 | A * B | 矩阵乘法 |
| 矩阵求逆 | inv(A) | 逆矩阵 |
| 线性求解 | A \ b | Ax=b 的解 |
| 行列式 | det(A) | 行列式 |
| 转置 | A' 或 transpose(A) | 转置 |
| 特征分解 | eigen(A) | 特征值与特征向量 |
| SVD | svd(A) | 奇异值分解 |
| 迹 | tr(A) | 主对角线和 |
| 秩 | rank(A) | 矩阵的秩 |
| 范数 | norm(A) | Frobenius 范数 |
| 条件数 | cond(A) | 条件数 |
11. 业务场景:图像处理基础
using Statistics
# 模拟灰度图像(2D 数组)
img = rand(UInt8, 256, 256)
# 图像裁剪
cropped = img[50:150, 50:150]
# 亮度调整
brighter = clamp.(img .+ 50, 0, 255)
# 对比度调整
mean_val = mean(img)
adjusted = clamp.(round.(UInt8, mean_val .+ 1.5 .* (img .- mean_val)), 0, 255)
# 简单模糊(均值滤波 3×3)
function blur(img, k=3)
h, w = size(img)
result = similar(img)
pad = k ÷ 2
for i in 1:h, j in 1:w
region = img[max(1,i-pad):min(h,i+pad), max(1,j-pad):min(w,j+pad)]
result[i, j] = round(UInt8, mean(region))
end
return result
end
# 水平翻转
flipped_h = img[:, end:-1:1]
# 垂直翻转
flipped_v = img[end:-1:1, :]
# 旋转 90°
rotated = rotl90(img)
12. 性能优化提示
# ❌ 避免:循环中按索引赋值(会导致类型不稳定)
function bad_example(n)
result = []
for i in 1:n
push!(result, i^2)
end
return result
end
# ✅ 推荐:预分配数组
function good_example(n)
result = Vector{Int}(undef, n)
for i in 1:n
result[i] = i^2
end
return result
end
# ✅ 推荐:使用原地操作
A = rand(1000, 1000)
B = rand(1000, 1000)
# ❌ 每次分配新内存
C = A + B
# ✅ 原地修改
C = similar(A)
C .= A .+ B
# ✅ 使用 @views 避免拷贝
@views function compute(A)
return sum(A[2:end-1, 2:end-1])
end
⚠️ 注意: Julia 数组是列优先(column-major) 存储。遍历二维数组时,外层循环应遍历列(第二维),内层循环遍历行(第一维),以获得最佳缓存命中率。
# ✅ 正确的遍历顺序(列优先)
function col_major_sum(A)
s = zero(eltype(A))
for j in 1:size(A, 2) # 外层:列
for i in 1:size(A, 1) # 内层:行
s += A[i, j]
end
end
return s
end
扩展阅读
- Julia 官方文档 — Arrays
- Julia 官方文档 — Linear Algebra
- Julia 官方文档 — Broadcasting
- Performance Tips — Pre-allocating outputs
- SparseArrays.jl 文档
📌 本章小结: Julia 数组是列优先存储的同构容器。使用
zeros/ones/rand构造,reshape变换维度。索引支持整数、范围和布尔掩码。@view创建零拷贝视图,广播使用.语法。LinearAlgebra提供完整的矩阵运算支持。预分配和原地操作是关键性能优化手段。