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(目前已禁用)。