跳至内容

Octave 编程教程/循环和条件

来自 Wikibooks,开放世界中的开放书籍

循环用于重复执行一段代码,具体次数根据循环类型而定,可能是已知的,也可能是未知的。使用循环,你可以绘制一些漂亮的图案,例如分形和随机点绘制的形状。

for 循环

[编辑 | 编辑源代码]

我们使用 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 中的每个值都会执行一次。

示例:谢尔宾斯基三角形

[编辑 | 编辑源代码]

谢尔宾斯基三角形是一种分形,可以通过一个非常简单的算法生成。

  1. 从等边三角形的顶点开始。
  2. 随机选择三角形的一个顶点。
  3. 移动到当前位置与所选顶点之间中点的点。
  4. 从步骤 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 秒。一般来说,如果循环可以用向量化替换,那么就应该这样做。

  1. 编写一个脚本,对前 N 个整数求和。你可以使用公式 检查你的结果。
  2. 编写一个脚本,执行与 linspace 函数相同的功能。它应该从某个值 xstart 开始,在 xstop 处结束,并创建一个向量,其中包含 N 个从 xstartxstop 均匀分布的值。你可以使用 zeros 函数来创建一个填充了零的正确大小的向量。使用 help zeros 了解该函数的工作原理。

while 循环

[编辑 | 编辑源代码]

while 循环也会重复执行一段代码多次,但停止执行取决于一个逻辑条件。例如

x = 1.0;
while x < 1000
   disp(x);
   x = x*2;
endwhile

x 乘以 2,直到其值超过 1000。这里,x < 1000 是循环的条件。只要条件成立(为真),循环就会继续执行。一旦条件不成立,循环就会终止,并执行循环后的第一条指令。

while 循环的一般形式如下

while condition
   ...
endwhile
  1. 编写一个脚本,计算最小的正整数 n,使得 对于某些实数 ab。(也就是说,找到 a 的最小幂,该幂至少为 b。)使用 log 函数被认为作弊。

示例:曼德勃罗特分形

[编辑 | 编辑源代码]

曼德勃罗特集合是另一个分形,它是通过检查复数变大的速度来生成的。对于每个复数 c

  1. 开始。
  2. 找到第一个 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 循环非常相似,因为它们根据给定条件为真还是假来继续执行。但是,whiledo...until 循环之间有一些重要的区别。

  1. while 循环的条件位于循环的开头;
    do...until 循环的条件位于循环的结尾。
  2. while 循环只要条件为真就重复;
    do...until 循环只要条件为假就继续。
  3. while 将执行 0 次或更多次(因为条件位于开头);
    do...until 循环将执行 1 次或更多次(因为条件位于结尾)。

do...until 循环的一般形式是

do
   ...
until condition

练习

[edit | edit source]

编写一个脚本,计算两个正整数的最大公约数(GCD)。您可以使用欧几里得算法来实现。

挑战

[edit | edit source]

编写一个脚本,生成均匀分布的随机数对(a,b)

  1. 在圆盘(下面的第一个图像);
  2. 如下面的第二个图像所示
File:Octave uniform random circle.png File:Octave uniform random challenge.png

breakcontinue 语句

[edit | edit source]

有时需要在循环执行的中间某个位置停止循环,或者继续到 for 循环中的下一个值,而无需为当前值执行循环代码的其余部分。这就是 breakcontinue 语句有用的地方。

以下代码演示了 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 语句可以用于任何循环:forwhiledo...until

continue 语句也从循环内部跳转,但返回到循环的开头,而不是到循环的末尾。在一个

  1. for 循环中,向量中的下一个值将被分配给 for 变量(如果有的话),并使用该值重新启动循环;
  2. while 循环中,将重新测试循环开头的条件,如果条件仍然为真,则继续循环;
  3. 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 语句的 elseifelse 部分是可选的。

以下都是有效的 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(目前已禁用)。


返回Octave 编程教程索引

华夏公益教科书