Algorithm 在集合中查找最小值/最大值时使用类型的最小值/最大值

Algorithm 在集合中查找最小值/最大值时使用类型的最小值/最大值,algorithm,theory,Algorithm,Theory,起初,我基本上写了一篇文章,结尾有一个问题,所以我要把它压缩成这样:哪一个更好(这里真的很挑剔) (A) int-min=someArray[0][0]; for(int i=0;i

起初,我基本上写了一篇文章,结尾有一个问题,所以我要把它压缩成这样:哪一个更好(这里真的很挑剔)

(A)

int-min=someArray[0][0];
for(int i=0;i
-或-

(B)

int min=int.MAX\u值;
for(int i=0;i
我认为b更快,通过将
min
初始化为常量值而不是使用索引器来保存一两条指令。它也感觉不那么冗余-无需将
someArray[0][0]
与自身进行比较

作为一种算法,哪一种更好/更有效

编辑:假设数组不为null且不为空。

EDIT2:修复了几个粗心的错误。

B更好;如果某个数组碰巧为空,则会出现运行时错误;但是A和B都可能有问题,因为如果某个数组为null(并且在前面的代码行中没有检查),A和B都会抛出异常。

这两种算法都是正确的(当然,假设数组是非空的)。我认为版本A更通用,因为对于某些类型(特别是字符串),可能没有定义好的最大值

这些算法等价的原因与一个叫做a的酷数学对象有关。为了激发半格,max的一些很酷的性质恰好成立:

  • max是幂等的,所以将它应用于相同的值两次会返回原始值:max(x,x)=x
  • max是可交换的,所以对其参数应用它的顺序无关紧要:max(x,y)=max(y,x)
  • max是关联的,因此当取三个或更多值中的最大值时,元素的分组方式无关紧要:max(max(x,y),z)=max(x,max(y,z))
  • 这些法律也适用于最低限度,以及许多其他结构。例如,如果有树结构,“最小上界”操作符也满足这些约束。类似地,如果您有一组集合和集合并集或交集,您会发现这些约束也适用

    如果您有一组元素(例如,整数、字符串等)和使用上述三个属性(幂等性、交换性和结合性)在其上定义的某个二元运算符,那么您找到了一个称为半格的结构。然后,二进制运算符被称为满足运算符(有时根据上下文称为联接运算符)

    半格之所以有用,是因为如果您有一个从半格绘制的(有限)元素集合,并且想要计算它们的相交,您可以使用如下循环:

    Element e = data[0];
    for (i in data[1 .. n])
        e = meet(e, data[i])
    
    这样做的原因是因为满足操作符是可交换的和关联的,所以我们可以按照我们想要的任何顺序在元素之间应用满足。当我们按顺序遍历数组元素时,一次应用一个元素,因此产生的值与我们先洗牌数组元素或按相反顺序迭代时产生的值相同。在您的例子中,满足运算符是“max”或“min”由于它们满足上述满足运算符的定律,上述代码将正确计算最大值或最小值

    为了回答您最初的问题,我们需要更多的术语。您很好奇,将最小值的初始猜测值初始化为可能的最大整数是否更好或更安全。这样做的原因是我们有一个很酷的特性

    min(int.MAX_VALUE, x) = min(x, int.MAX_VALUE) = x
    
    换句话说,如果您计算
    int.MAX_值
    和任何其他值的满足度,则返回第二个值。用数学术语来说,这是因为
    int.MAX_VALUE
    是meet半格的顶部元素。更正式地说,meet半格的顶元素是满足以下条件的元素(表示为⊤)

    相遇(⊤,x)=相遇(x,⊤)=x

    如果使用max而不是min,那么顶部元素将是
    int.min\u VALUE
    ,因为

    max(int.MIN_VALUE, x) = max(x, int.MIN_VALUE) = x
    
    因为将“满足”操作符应用于⊤任何其他元素都会产生另一个元素,如果你有一个带有定义良好的顶部元素的相遇半格,你可以重写上面的代码,计算所有元素的相遇,如下所示

    Element e = Element.TOP;
    for (i in data[0 .. n])
        e = meet(e, data[i])
    
    这是因为在第一次迭代之后,
    e
    被设置为
    meet(e,数据[0])=meet(Element.TOP,数据[0])=data[0]
    ,迭代照常进行。因此,在您最初的问题中,使用两个循环中的哪一个并不重要;只要定义了至少一个元素,它们就会产生相同的值

    也就是说,并非所有半格都有顶元素。例如,考虑满足MIX运算符定义为

    的所有字符串集合。
    meet(x, y) = x if x lexicographically precedes y
               = y otherwise
    
    例如,
    meet(“a”、“ab”)=“a”
    meet(“dog”、“cat”)=“cat”
    ,等等。在这种情况下,没有字符串s满足属性meet(s,x)=meet(x,s)=x,因此半格没有top元素。在这种情况下,您不可能使用代码的第二个版本,因为没有可以初始化初始值的top元素

    但是,有一种非常可爱的技术可以用来伪装这种情况,实际上这种技术在实践中确实被使用了一些。给定一个没有顶元素的半格,您可以通过引入新元素⊤并任意定义满足(⊤,x)=满足(x,⊤)来创建一个具有顶元素的新半格=x。换句话说,此元素是经过精心设计的顶级元素,除此之外没有任何意义

    在代码中,您可以通过编写

    bool found = false;
    Element e;
    
    for (i in data[0 .. n]) {
        if (!found) {
            found = true;
            e = i;
        } else {
            e = meet(e, i);
        }
    }
    
    此代码通过找到外部布尔值
    来工作
    
    meet(x, y) = x if x lexicographically precedes y
               = y otherwise
    
    bool found = false;
    Element e;
    
    for (i in data[0 .. n]) {
        if (!found) {
            found = true;
            e = i;
        } else {
            e = meet(e, i);
        }
    }
    
    int min = someArray[0][0];