在MATLAB中,数组只是向量的另一个名称。那么,为什么要用一章的大部分篇幅来讲解数组,而我们在本书的大部分时间里都在使用向量呢?当我们关注通过下标处理单个元素,而不是作为一个整体处理向量时,谈论数组(而不是向量)是有帮助的。因此,在本章的前三节中,我们将看到一些问题,这些问题最好通过将向量视为数组来解决,通常借助for循环。在本章的最后三节中,我们将讨论其他更高级的数据结构。
在第8章(更新过程)中,我们考虑了计算罐装橙汁在冰箱冷却时的温度的问题。这是一个更新过程的示例,其中主变量在一段时间内被重复更新。现在我们研究一下如何更普遍地解决这个问题。
一罐温度为25°C的橙汁放在冰箱里,环境温度为10°C。我们想知道橙汁的温度在一段时间内是如何变化的。解决这类问题的一个标准方法是将时间周期分成若干小步,每小步的长度为 dt, 如果 是第 步开始时的温度,我们可以用下面的模型从 得到 :
其中K是一个常数参数,取决于罐头的绝缘性能和橙汁的热性能。假设选择了单位,时间以分钟为单位。
我们首先用单位时间步长求解,即 。最简单的方法是使用标量表示时间和温度,就像我们在第八章看到的那样 (虽然我们没有使用单位时间步长):
K = 0.05;
F = 10;
T = 25; % initial temperature of OJ
for time = 1:100 % time in minutes
T=T-K * (T - F); % dt = 1
if rem(time, 5) == 0
disp( [time T] )
end
end;
注意,使用rem每5分钟显示一次结果:当time是5的整数倍时,它除以5的余数为零。
虽然这无疑是编写脚本最简单的方法,但我们不能轻易地绘制温度随时间的曲线图。为了做到这一点,时间和T必须是向量。for循环的索引必须用作t的每个元素的下标。下面是脚本(update1.m)
:
K = 0.05;
F = 10;
time = 0:100; % initialize vector time
T = zeros(1,101); % pre-allocate vector T
T(1) = 25; % initial temperature of orange juice
for i = 1:100 % time in minutes
T(i+1) = T(i) - K * (T(i) - F); % construct T
end;
disp([ time(1:5:101)' T(1:5:101)' ]); % display results
plot(time, T), grid
time = 0:100
为time设置了一个(行)向量,其中time(1)
的值为0分钟,time(101)
的值为100分钟。这是必要的,因为MATLAB向量的第一个下标必须是1。T = zero(101)
为温度设置了一个对应的(行)向量,其中每个元素初始化为0(同样必须有101个元素,因为第一个元素代表时间为0时的温度)。这个过程称为预分配。在式 中取并不总是合适或足够准确。在 MATLAB 中,生成解向量的标准方式是使用更一般的符号来表示,以适应几乎任何 dt(时间步长)的值。
设初始时间为a
,最终时间为b
,如果我们想要时间步长为dt
,那么这样的步长为m = (b − a)/dt
, 第i
步结束时的时间是a + i dt
下面是实现这个方案的脚本 update2.m,它具有一些附加功能。它会提示您输入 dt
的值,并检查该值是否得到了步长数 m
的整数值。它还会要求您输入输出间隔 opint
(以分钟为单位,在这些间隔内结果以表格形式显示),并检查该间隔是否是 dt
的整数倍。尝试使用相同的示例值运行脚本,例如 dt = 0.4
和 opint = 4
。
K = 0.05;
F = 10;
a = 0; % 初始时间
b = 100; % 终止时间
load train
dt = input('dt: ');
opint = input('output interval (minutes): ');
if opint/dt ~= fix(opint/dt)
sound(y, Fs)
disp('output interval is not a multiple of dt!')
return
end
m = (b - a) / dt; % m 步,每步长度为 dt
if fix(m) ~= m
sound(y, Fs)
disp('m is not an integer - try again!')
return
end
T = zeros(1,m+1); % 预分配 (m+1) 个元素的向量
time = a:dt:b;
T(1) = 25; % 初始温度
for i = 1:m
T(i+1) = T(i) - K * dt * (T(i) - F);
end
disp([time(1:opint/dt:m+1)' T(1:opint/dt:m+1)'])
plot(time, T), grid
向量 T 和 time 必须分别具有 m + 1 个元素,因为有 m 个时间步长,我们还需要一个额外的元素用于每个向量的初始值。
向量 T 是用于存储温度值的向量,而 time 是用于存储时间值的向量。由于包括初始值,所以需要 m + 1 个元素来存储 m 个时间步长的数据。
在这段代码中,通过 T = zeros(1,m+1)
和 time = a:dt:b
进行了预分配,确保向量 T 和 time 具有适当的长度。
解决这个问题的一个非常好的方法是编写一个函数来完成。这样做可以更容易地为不同的 dt 值生成结果表格,比如从命令行中使用这个函数。以下是被重写为函数 cooler.m
的 update2.m
:
function [time, T, m] = cooler(a, b, K, F, dt, T0)
m = (b - a) / dt; % m 步,每步长度为 dt
if fix(m) ~= m
disp('m is not an integer - try again!');
return
end
T = zeros(1, m + 1); % 预分配 (m+1) 个元素的向量
time = a:dt:b;
T(1) = T0; % 初始温度
for i = 1:m
T(i+1) = T(i) - K * dt * (T(i) - F);
end
end
通过将 update2.m
重写为函数 cooler.m
,我们可以更方便地从命令行中调用该函数,并为不同的 dt 值生成结果表格。
函数 cooler
接受六个参数:a、b、K、F、dt 和 T0。这些参数分别代表初始时间、终止时间、常数 K、常数 F、时间步长和初始温度。
在函数体内部,我们进行计算并返回了时间向量 time、温度向量 T 和步数 m。
下面是使用函数生成不同 dt 值的结果表格的示例代码:
dt = 1;
[t, T, m] = cooler(0, 100, 0.05, 10, dt, 25);
table(:, 1) = t(1:5/dt:m+1)';
table(:, 2) = T(1:5/dt:m+1)';
dt = 0.1;
[t, T, m] = cooler(0, 100, 0.05, 10, dt, 25);
table(:, 3) = T(1:5/dt:m+1)';
format bank
disp(table)
0 25.00 25.00
5.00 21.61 21.67
10.00 18.98 19.09
15.00 16.95 17.07
20.00 15.38 15.50
25.00 14.16 14.28
30.00 13.22 13.33
35.00 12.49 12.60
40.00 11.93 12.02
45.00 11.49 11.57
50.00 11.15 11.22
55.00 10.89 10.95
60.00 10.69 10.74
65.00 10.53 10.58
70.00 10.41 10.45
75.00 10.32 10.35
80.00 10.25 10.27
85.00 10.19 10.21
90.00 10.15 10.16
95.00 10.11 10.13
100.00 10.09 10.10
在这个示例代码中,我们首先使用 dt = 1 调用 cooler
函数,并将结果保存在变量 t、T 和 m 中。然后,我们从结果中选择每隔 5 分钟的数据,并保存到表格中的第一列。
接着,我们使用 dt = 0.1 调用 cooler
函数,并将结果保存在变量 t、T 和 m 中。然后,我们选择每隔 5 分钟的数据,并保存到表格中的第三列。
最后,我们使用 format bank
命令将表格中的数值格式设置为货币格式,并使用 disp
函数显示表格。
使用生成向量输出变量的函数的优势是,即使在函数内部忘记预分配向量(使用 zeros 函数),MATLAB 在从函数返回之前会自动清除任何先前版本的输出向量。
变量 table
是一个二维数组(或矩阵)。回想一下,冒号操作符可以用于指示矩阵中的所有元素(行或列)。因此,table(:,1) 表示每一行和第一列的元素,也就是整个第一列。向量 t 和 T 是行向量,因此在插入到 table 的第一列和第二列之前,它们必须被转置。第三列的插入方式类似。
对于 dt = 0.1,结果在第三列中会更加精确。这是因为当时间步长减小(dt = 0.1)时,计算的间隔更小,导致更多的数据点被用于计算温度的变化。因此,结果更加精确。
当 dt 值较大时,数据点之间的时间间隔变大,可能会导致温度变化的细节被忽略,从而使结果不够准确。通过使用更小的 dt 值,我们可以获得更精确的温度值。然而,这也会增加计算的时间,因为计算的时间步长更小。因此,在选择 dt 值时,需要权衡精确性和计算效率之间的关系。
在给定的问题中,我们有一个冷却问题,需要计算温度随时间的变化。根据给定的公式:
其中,是时间 时刻的温度,是初始温度, 是环境温度,是一个常量。
要将这个精确解添加到表的第四列中,我们可以使用向量化的方式,如下所示:
table(:,4) = 10 + (T(1)-10)*exp(-0.05 * t(1:5/dt:m+1)');
其中,table 表示我们的表格,t 是一个时间向量,m 是时间步数。
在给定的例子中,我们假设每个单位时间的增量dt等于5。通过这个精确解,我们可以生成一个类似于如下这样的增加了精确解的完整表格:
0 25.00 25.00 25.00
5.00 21.61 21.67 21.68
10.00 18.98 19.09 19.10
15.00 16.95 17.07 17.09
...
95.00 10.11 10.13 10.13
100.00 10.09 10.10 10.10
注意,随着时间步长dt的减小,数值解会越来越接近精确解。这是因为当dt趋近于0时,精确解的方程可以从数值解的方程中推导出来。因此,在数值计算中,通过减小时间步长dt,我们可以使数值结果更加准确,逼近精确解。
联系客服