“uniquetol”究竟是什么功能?

8
介绍自 R2015a 起引入的 uniquetol 函数,可计算“容差内唯一元素”。具体来说,

C = uniquetol(A,tol) 使用公差 tol 返回在 A 中唯一的元素。

但是,在给定公差的情况下找到唯一元素的问题有多个解决方案。 那么实际上哪种才是正确的呢?
我们来看两个例子:
  1. A = [3 5 7 9],绝对容差为 2.5。输出可以是 [3 7] 或者 [5 9]。这两种解决方案都符合要求。

  2. 对于绝对容差为 2.5A = [1 3 5 7 9],输出可以是 [1 5 9] 或者 [3 7]。因此,甚至输出中元素的数量也可能会有所不同。

请参见这个讨论,了解困扰问题核心的传递性问题。
那么,uniquetol 是如何工作的? 它在众多现有解决方案中产生了哪个输出?

为什么要使用“逆向工程”标签? - arrowd
1
@arrowd 因为他对该函数进行了逆向工程。 - Ander Biguri
@arrowd 老实说,我不完全确定它是否适用于这里。但我认为它是适用的,因为描述中提到“反向工程是通过分析其功能和操作来发现人造[...]对象或系统的技术原理的过程”。 - Luis Mendo
1个回答

8
为了简化,我考虑使用 uniquetol单输出、双输入 版本。
 C = uniquetol(A, tol);

其中第一个输入是一个double类型的向量A。特别地,这意味着:

  • uniquetol'ByRows'选项未被使用。
  • 第一个输入是一个向量。如果不是向量,则uniquetol会隐式地线性化为列,与通常情况下相同。
  • 第二个输入,用于定义容差的,被解释如下:

    如果abs(u-v) <= tol*max(abs(A(:))),则两个值uv在容差范围内。

    也就是说,默认情况下指定的容差是相对的。在比较中使用的实际容差是通过将最大绝对值在A中进行缩放得到的。

考虑到这些因素,uniquetol使用的方法似乎是:

  1. A 进行排序。
  2. 选择已排序的 A 的第一个条目,并将其设置为 参考值(稍后需要更新此值)。
  3. 将参考值写入输出 C
  4. 跳过已排序的 A 的后续条目,直到找到不在参考值容限内的条目。当找到该条目时,将其作为新的参考值,并返回步骤 3。

当然,我并不是说这就是 uniquetol内部实现。但是 输出 看起来是相同的。因此,这与 uniquetol 所做的是 功能等效的

以下的代码实现了上述方法(效率低下的代码,仅用于说明问题)。

% Inputs A, tol
% Output C
tol_scaled = tol*max(abs(A(:))); % scale tolerance
C = []; % initiallize output. Will be extended
ref = NaN; % initiallize reference value to NaN. This will immediately cause
           % A(1) to become the new reference
for a = sort(A(:)).';
    if ~(a-ref <= tol_scaled)
        ref = a;
        C(end+1) = ref;
    end
end

为了验证这一点,让我们生成一些随机数据,并比较uniquetol和上述代码的输出结果:
clear
N = 1e3; % number of realizations
S = 1e5; % maximum input size
for n = 1:N;
    % Generate inputs:
    s = randi(S); % input size
    A = (2*rand(1,S)-1) / rand; % random input of length S; positive and 
                                % negative values; random scaling
    tol = .1*rand; % random tolerance (relative). Change value .1 as desired

    % Compute output:
    tol_scaled = tol*max(abs(A(:))); % scale tolerance
    C = []; % initiallize output. Will be extended
    ref = NaN; % initiallize reference value to NaN. This will immediately cause
               % A(1) to become the new reference 
    for a = sort(A(:)).';
        if ~(a-ref <= tol_scaled)
            ref = a;
            C(end+1) = ref;
        end
    end

    % Check if output is equal to that of uniquetol:
    assert(isequal(C, uniquetol(A, tol)))
end

在我所有的测试中,这个程序都没有失败过断言。
因此,uniquetol 似乎会对输入进行排序,选择第一个条目,并且尽可能地跳过其它条目。
对于问题中的两个示例,输出如下所示。请注意,第二个输入被指定为 2.5/9,其中 9 是第一个输入的最大值,以实现绝对容差为 2.5
>> uniquetol([1 3 5 7 9], 2.5/9)
ans =
     1     5     9
>> uniquetol([3 5 7 9], 2.5/9)
ans =
     3     7

1
那么,对于问题中的示例,输出是什么?(+1) - Ander Biguri
1
@AnderBiguri 好的,我会包含那个。它基本上选择它找到的第一个值,然后跳过值,直到不能再跳为止。所以:uniquetol([3 5 7 9], 2.5/9) 给出 [3 7]uniquetol([1 3 5 7 9], 2.5/9) 给出 [1 5 9]。请注意所需的 /9,因此 2.5 是_绝对_公差。 - Luis Mendo
有趣!我会输出每组等价值的平均值或中位数,我认为这样更公平。 - Cris Luengo
@CrisLuengo 但是你是否仍然会像uniquetol一样定义_一组等价值_?这种方式似乎存在很大的武断性。(感谢您的投票!) - Luis Mendo
也许迭代过程可以导致更稳定的结果? - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接