Octave 编程教程/循环和条件
循环用于重复执行一段代码,具体次数根据循环类型而定,可能是已知的,也可能是未知的。使用循环,你可以绘制一些漂亮的图案,例如分形和随机点绘制的形状。
我们使用 for 循环来重复执行一段代码,针对已知值列表。例如,我们将计算一个值列表的平均值。平均值由下式计算得出
我们设置一个包含一些值的向量
octave:1> x = [1.2, 6.3, 7.8, 3.6];
并使用以下代码计算平均值
octave:2> sum = 0; octave:3> for entry = x, octave:4> sum = sum + entry; octave:5> end; octave:6> x_mean = sum / length(x)
第 2 行:将 sum 设置为 0。
第 3 行:对于 x 中的每个值,将其分配给 entry。
第 4 行:将 sum 增加 entry。
第 5 行:当 x 中没有更多成员时,结束 for 循环。
第 6 行:将 sum 的最终值除以 x 的长度,并将结果分配给 x_mean。
待办:提供一个更好的示例并解释代码。
一般来说,我们编写 for 循环的格式如下
for variable = vector ... end
... 代表代码块,该代码块对于 vector 中的每个值都会执行一次。
谢尔宾斯基三角形是一种分形,可以通过一个非常简单的算法生成。
- 从等边三角形的顶点开始。
- 随机选择三角形的一个顶点。
- 移动到当前位置与所选顶点之间中点的点。
- 从步骤 2 开始重复。
通过遵循此过程绘制你访问的点,可以生成以下图像。

生成该分形的代码如下所示。请注意,此代码使用一个非常简单的 for 循环来生成分形(for i = 1:N ; ... ; end)
axis ([-1, 1, -0.75, 1.25], "square");
figure(1, "visible", "off"); % no plotting window
hold on;
% defining the vertices of an equilateral triangle (symmetric to y-axis)
V = [ 0, 1; % top vertex
cos( (pi/2)+(2*pi/3) ), sin( (pi/2)+(2*pi/3) ); % left vertex
cos( (pi/2)-(2*pi/3) ), sin( (pi/2)-(2*pi/3) ) % right vertex
];
r = floor(3*rand(1)+0.9999999999); % integer random number in [1:3]
x = [ V(r,1), V(r,2) ]; % initializing x on random vertex
for i = 1:1000 % !!! 100000 => time=130m57.343s
r = floor(3*rand(1)+0.9999999999); % integer random number in [1:3]
x = ( x+V(r,[1:2]) )./2; % halfway, current to random vertex
plot(x(1),x(2), ".");
endfor
print -dpng "sierpinski_m.png";
出于性能原因,最好在 'for' 循环中执行尽可能少的任务,并且应尽可能将 plot 这样的图形操作移到循环之外。通过简单地将所有 x 值存储在矩阵中,然后像下面这样绘制,代码生成此图的运行时间会缩短到几秒,即使迭代超过 100,000 个元素(上面的代码警告不要这样做)。
axis ([-1, 1, -0.75, 1.25], "square");
figure(1, "visible", "off"); % no plotting window
hold on;
% defining the vertices of an equilateral triangle (symmetric to y-axis)
V = [ 0, 1; % top vertex
cos( (pi/2)+(2*pi/3) ), sin( (pi/2)+(2*pi/3) ); % left vertex
cos( (pi/2)-(2*pi/3) ), sin( (pi/2)-(2*pi/3) ) % right vertex
];
r = floor(3*rand(1)+0.9999999999); % integer random number in [1:3]
x(1,:) = [ V(r,1), V(r,2) ]; % initializing x as a matrix this time.
for i = 2:100000 % Safe now: 100000 => time=7.85346s
r = floor(3*rand(1)+0.9999999999); % integer random number in [1:3]
x(i,:) = ( x(i-1,:)+V(r,[1:2]) )./2; % Add each newly calculated x value to the matrix
endfor
plot(x(:,1),x(:,2), "."); % plot the entire matrix just once
print -dpng "sierpinski_m.png";
通过提前将 x 初始化为完整最终大小的矩阵,可以在现代硬件上将处理时间进一步缩短到 1 或 2 秒。一般来说,如果循环可以用向量化替换,那么就应该这样做。
- 编写一个脚本,对前 N 个整数求和。你可以使用公式 检查你的结果。
- 编写一个脚本,执行与
linspace函数相同的功能。它应该从某个值xstart开始,在xstop处结束,并创建一个向量,其中包含 N 个从xstart到xstop均匀分布的值。你可以使用zeros函数来创建一个填充了零的正确大小的向量。使用help zeros了解该函数的工作原理。
while 循环也会重复执行一段代码多次,但停止执行取决于一个逻辑条件。例如
x = 1.0; while x < 1000 disp(x); x = x*2; endwhile
将 x 乘以 2,直到其值超过 1000。这里,x < 1000 是循环的条件。只要条件成立(为真),循环就会继续执行。一旦条件不成立,循环就会终止,并执行循环后的第一条指令。
while 循环的一般形式如下
while condition ... endwhile
- 编写一个脚本,计算最小的正整数 n,使得 对于某些实数 a 和 b。(也就是说,找到 a 的最小幂,该幂至少为 b。)使用
log函数被认为作弊。
曼德勃罗特集合是另一个分形,它是通过检查复数变大的速度来生成的。对于每个复数 c,
- 从 开始。
- 令
- 找到第一个 i,使得 .
我们将记录所有这些i值,并为每个值分配一种颜色。这用于生成类似于此的图像。

您可以从Mandelbrot.m下载生成此分形的代码。请注意,有一个 while 循环(在一些 for 循环内),用于测试复数z的模量是否小于2
while (count < maxcount) & (abs(z) < 2) ... endwhile
while 循环中的第一个条件检查我们是否没有执行太多迭代。对于一些c值,如果我们让它继续,迭代将永远持续下去。
另请参阅Christopher Wellons的另一个版本
do...until语句
[edit | edit source]这些循环与 while 循环非常相似,因为它们根据给定条件为真还是假来继续执行。但是,while 和 do...until 循环之间有一些重要的区别。
while循环的条件位于循环的开头;do...until循环的条件位于循环的结尾。while循环只要条件为真就重复;do...until循环只要条件为假就继续。while将执行 0 次或更多次(因为条件位于开头);do...until循环将执行 1 次或更多次(因为条件位于结尾)。
do...until 循环的一般形式是
do ... until condition
练习
[edit | edit source]编写一个脚本,计算两个正整数的最大公约数(GCD)。您可以使用欧几里得算法来实现。
挑战
[edit | edit source]编写一个脚本,生成均匀分布的随机数对(a,b)
- 在圆盘(下面的第一个图像);
- 如下面的第二个图像所示
break 和 continue 语句
[edit | edit source]有时需要在循环执行的中间某个位置停止循环,或者继续到 for 循环中的下一个值,而无需为当前值执行循环代码的其余部分。这就是 break 和 continue 语句有用的地方。
以下代码演示了 break 语句的工作原理。
total = 0;
while true
x = input('Value to add (enter 0 to stop): ');
if x == 0
break;
endif
total = total+x;
disp(['Total: ', num2str(total)]);
endwhile
如果没有 break 语句,循环将永远继续执行,因为 while 循环的条件始终为真。break 允许您跳过循环的末尾(到 endwhile 之后的语句)。
break 语句可以用于任何循环:for、while 或 do...until。
continue 语句也从循环内部跳转,但返回到循环的开头,而不是到循环的末尾。在一个
for循环中,向量中的下一个值将被分配给 for 变量(如果有的话),并使用该值重新启动循环;while循环中,将重新测试循环开头的条件,如果条件仍然为真,则继续循环;do...until循环中,将测试循环末尾的条件,如果条件仍然为假,则从开头继续循环。
例如,以下代码将用 1 填充方阵的下三角部分,其余部分用 0 填充。
N = 5;
A = zeros(N); % Create an N x N matrix filled with 0s
for row = 1:N
for column = 1:N
if column > row
continue;
endif
A(row, column) = 1;
endfor
endfor
disp(A);
请注意,内部 for 跳过(继续)为 A 的条目分配 1 的代码,只要列索引大于行索引。
if 语句
[edit | edit source]if 语句的一般形式是
if condition1 ... elseif condition2 ... else ... endif
如果 condition1 评估为真,则执行紧随 if 之后的块中的语句。如果 condition1 为假,则检查下一个条件(elseif 中的 condition2),如果为真则执行其语句。您可以拥有任意数量的 elseif 语句。如果所有条件都评估为假,则执行 else 之后的最后一组语句。请注意,if 语句的 elseif 和 else 部分是可选的。
以下都是有效的 if 语句
% Take the log of the absolute value of x
if x > 0
y = log(x);
elseif x < 0
y = log(-x);
else
disp("Cannot take the log of zero.");
endif
x = input("Enter a value: ");
if x > 0
disp("The number is positive");
endif
if x < 0
disp("The number is negative");
endif
if x == 0
disp("The number is zero");
endif
示例:分形蕨
[edit | edit source]此算法还不完整。请查看可从http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=4372&objectType=file获得的 .m 文件。
右侧的图像可以使用以下算法生成
1. Let x1 and y1 be random values between 0 and 1.
2. Choose one of the linear transformations below to calculate (xi+1, yi+1) from (xi, yi):
1. xi+1 = 0
yi+1 = 0.16yi
2. xi+1 = 0.20xi − 0.26yi
yi+1 = 0.23xi + 0.22yi + 1.6
3. xi+1 = −0.15xi + 0.28yi
yi+1 = 0.26xi + 0.24yi + 0.44
4. xi+1 = 0.85xi + 0.04yi
yi+1 = −0.04xi + 0.85yi + 1.6
The first transformation is chosen if probability 0.01, the second and third with probability 0.07 each and the fourth with probability 0.85.
3. Calculate these values for i up to at least 10,000.
您可以下载生成此分形的代码为 fracfern.m(目前已禁用)。