MATLAB的冒号运算符如何工作? [英] How does MATLAB's colon operator work?

查看:137
本文介绍了MATLAB的冒号运算符如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Sam Roberts的答案

As noted in this answer by Sam Roberts and this other answer by gnovice, MATLAB's colon operator (start:step:stop) creates a vector of values in a different way that linspace does. In particular, Sam Roberts states:

冒号运算符将增量添加到起点,并从终点减去减值以到达中间点.这样,可以确保输出矢量尽可能对称.

The colon operator adds increments to the starting point, and subtracts decrements from the end point to reach a middle point. In this way, it ensures that the output vector is as symmetric as possible.

但是,The MathWorks的官方文档已从其站点中删除.

However, offical documentation about this from The MathWorks has been deleted from their site.

如果Sam的描述正确,那么步长中的错误就不是对称的吗?

If Sam's description is correct, wouldn't the errors in the step sizes be symmetric?

>> step = 1/3;
>> C = 0:step:5;
>> diff(C) - step
ans =
   1.0e-15 *
  Columns 1 through 10
         0         0    0.0555   -0.0555   -0.0555    0.1665   -0.2776    0.6106   -0.2776    0.1665
  Columns 11 through 15
    0.1665   -0.2776   -0.2776    0.6106   -0.2776

有关冒号运算符的有趣注意事项:

Interesting things to note about the colon operator:

  • 其值取决于其长度:

  • Its values depend on its length:

>> step = 1/3;
>> C = 0:step:5;
>> X = 0:step:3;
>> C(1:10) - X
ans =
   1.0e-15 *
         0         0         0         0         0   -0.2220         0   -0.4441    0.4441         0

  • 如果四舍五入,它可以生成重复的值:

  • It can generate repeated values if they are rounded:

    >> E = 1-eps : eps/4 : 1+eps;
    >> E-1
    ans =
       1.0e-15 *
       -0.2220   -0.2220   -0.1110         0         0         0         0    0.2220    0.2220
    

  • 最后一个值有一定的公差,如果步长在端点的正上方创建一个值,则仍将使用该最终值:

  • There is a tolerance for the last value, if the step size creates a value just above the end, this end value is still used:

    >> A = 0 : step : 5-2*eps(5)
    A =
      Columns 1 through 10
             0    0.3333    0.6667    1.0000    1.3333    1.6667    2.0000    2.3333    2.6667    3.0000
      Columns 11 through 16
        3.3333    3.6667    4.0000    4.3333    4.6667    5.0000
    >> A(end) == 5 - 2*eps(5)
    ans =
      logical
       1
    >> step*15 - 5
    ans =
         0
    

  • 推荐答案

    Sam的答案所引用的已删除页面是仍.幸运的是,即使是附加的M文件colonop也在那里.而且似乎该函数仍然与MATLAB的功能匹配(我在R2017a上):

    The deleted page referred to by Sam's answer is still archived by the Way Back Machine. Luckily, even the attached M-file colonop is there too. And it seems that this function still matches what MATLAB does (I'm on R2017a):

    >> all(0:step:5 == colonop(0,step,5))
    ans =
      logical
       1
    >> all(-pi:pi/21:pi == colonop(-pi,pi/21,pi))
    ans =
      logical
       1
    

    我将在此处复制该函数在一般情况下的作用(有一些捷径可用于生成整数向量和处理特殊情况).我用更有意义的变量替换了函数的变量名.输入是startstepstop.

    I'll replicate here what the function does for the general case (there are some shortcuts for generating integer vectors and handling special cases). I'm replacing the function's variable names with more meaningful ones. The inputs are start, step and stop.

    首先,它计算startstop之间有多少步.如果最后一步超出stop超过一个容限,则不会采取该步骤:

    First it computes how many steps there are in between start and stop. If the last step exceeds stop by more than a tolerance, it is not taken:

    n = round((stop-start)/step);
    tol = 2.0*eps*max(abs(start),abs(stop));
    sig = sign(step);
    if sig*(start+n*step - stop) > tol
      n = n - 1;
    end
    

    这解释了问题中提到的最后一个观察结果.

    This explains the last observation mentioned in the question.

    接下来,它会计算最后一个元素的值,并确保它不超过stop值,即使在上一次计算中允许它超过该值也是如此.

    Next, it computes the value of the last element, and makes sure that it does not exceed the stop value, even if it allowed to go past it in the previous computation.

    last = start + n*step;
    if sig*(last-stop) > -tol
       last = stop;
    end
    

    这就是为什么问题中向量A中的lasat值实际上以stop值作为最后一个值的原因.

    This is why the lasat value in the vector A in the question actually has the stop value as the last value.

    接下来,它按照广告说明将输出数组分为两部分:数组的左半部分和右半部分分别填充:

    Next, it computes the output array in two parts, as advertised: the left and right halves of the array are filled independently:

    out = zeros(1,n+1);
    k = 0:floor(n/2);
    out(1+k) = start + k*step;
    out(n+1-k) = last - k*step;
    

    请注意,它们不是通过递增来填充的,而是通过计算整数数组并将其乘以步长来填充的,就像linspace一样.这说明了问题中关于数组E的观察.区别在于,通过从last值中减去这些值来填充数组的右半部分.

    Note that they are not filled by incrementing, but by computing an integer array and multiplying it by the step size, just like linspace does. This exaplains the observation about array E in the question. The difference is that the right half of the array is filled by subtracting those values from the last value.

    最后一步,对于奇数大小的数组,分别计算中间值以确保其恰好位于两个端点的中间:

    As a final step, for odd-sized arrays, the middle value is computed separately to ensure it lies exactly half-way the two end points:

    if mod(n,2) == 0
       out(n/2+1) = (start+last)/2;
    end
    

    完整功能colonop复制在底部.

    请注意,分别填充数组的左侧和右侧并不意味着步长的错误应该是完全对称的.这些误差由舍入误差给出.但这确实会有所不同,就像问题中的数组A一样,步距不能精确到达stop点.在这种情况下,在数组的中间而不是在结尾处采用稍短的步长:

    Note that filling the left and right side of the array separately does not mean that the errors in step sizes should be perfectly symmetric. These errors are given by roundoff errors. But it does make a difference where the stop point is not reached exactly by the step size, as in the case of array A in the question. In this case, the slightly shorter step size is taken in the middle of the array, rather than at the end:

    >> step=1/3;
    >> A = 0 : step : 5-2*eps(5);
    >> A/step-(0:15)
    ans =
       1.0e-14 *
      Columns 1 through 10
             0         0         0         0         0         0         0   -0.0888   -0.4441   -0.5329
      Columns 11 through 16
       -0.3553   -0.3553   -0.5329   -0.5329   -0.3553   -0.5329
    

    但是,即使在精确地达到stop点的情况下,中间也会累积一些其他错误.以问题中的数组C为例. linspace:

    But even in the case where the stop point is reached exactly, some additional error accumulates in the middle. Take for example the array C in the question. This error accumulation does not happen with linspace:

    C = 0:1/3:5;
    lims = eps(C);
    subplot(2,1,1)
    plot(diff(C)-1/3,'o-')
    hold on
    plot(lims,'k:')
    plot(-lims,'k:')
    plot([1,15],[0,0],'k:')
    ylabel('error')
    title('0:1/3:5')
    L = linspace(0,5,16);
    subplot(2,1,2)
    plot(diff(L)-1/3,'x-')
    hold on
    plot(lims,'k:')
    plot(-lims,'k:')
    plot([1,15],[0,0],'k:')
    title('linspace(0,5,16)')
    ylabel('error')
    

    colonop:

    colonop:

    function out = colonop(start,step,stop)
    % COLONOP  Demonstrate how the built-in a:d:b is constructed.
    %
    %   v = colonop(a,b) constructs v = a:1:b.
    %   v = colonop(a,d,b) constructs v = a:d:b.
    %
    %   v = a:d:b is not constructed using repeated addition.  If the
    %   textual representation of d in the source code cannot be
    %   exactly represented in binary floating point, then repeated
    %   addition will appear to have accumlated roundoff error.  In
    %   some cases, d may be so small that the floating point number
    %   nearest a+d is actually a.  Here are two imporant examples.
    %
    %   v = 1-eps : eps/4 : 1+eps is the nine floating point numbers
    %   closest to v = 1 + (-4:1:4)*eps/4.  Since the spacing of the
    %   floating point numbers between 1-eps and 1 is eps/2 and the
    %   spacing between 1 and 1+eps is eps,
    %   v = [1-eps 1-eps 1-eps/2 1 1 1 1 1+eps 1+eps].
    %
    %   Even though 0.01 is not exactly represented in binary,
    %   v = -1 : 0.01 : 1 consists of 201 floating points numbers
    %   centered symmetrically about zero.
    %
    %   Ideally, in exact arithmetic, for b > a and d > 0,
    %   v = a:d:b should be the vector of length n+1 generated by
    %   v = a + (0:n)*d where n = floor((b-a)/d).
    %   In floating point arithmetic, the delicate computatations
    %   are the value of n, the value of the right hand end point,
    %   c = a+n*d, and symmetry about the mid-point.
    
    if nargin < 3
        stop = step;
        step = 1;
    end
    
    tol = 2.0*eps*max(abs(start),abs(stop));
    sig = sign(step);
    
    % Exceptional cases.
    
    if ~isfinite(start) || ~isfinite(step) || ~isfinite(stop)
       out = NaN;
       return
    elseif step == 0 || start < stop && step < 0 || stop < start && step > 0
       % Result is empty.
       out = zeros(1,0);
       return
    end
    
    % n = number of intervals = length(v) - 1.
    
    if start == floor(start) && step == 1
       % Consecutive integers.
       n = floor(stop) - start;
    elseif start == floor(start) && step == floor(step)
       % Integers with spacing > 1.
       q = floor(start/step);
       r = start - q*step;
       n = floor((stop-r)/step) - q;
    else
       % General case.
       n = round((stop-start)/step);
       if sig*(start+n*step - stop) > tol
          n = n - 1;
       end
    end
    
    % last = right hand end point.
    
    last = start + n*step;
    if sig*(last-stop) > -tol
       last = stop;
    end
    
    % out should be symmetric about the mid-point.
    
    out = zeros(1,n+1);
    k = 0:floor(n/2);
    out(1+k) = start + k*step;
    out(n+1-k) = last - k*step;
    if mod(n,2) == 0
       out(n/2+1) = (start+last)/2;
    end
    

    这篇关于MATLAB的冒号运算符如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆