【问题标题】:How to get the highest value from a Delphi set?如何从 Delphi 集中获得最高值?
【发布时间】:2009-05-06 22:56:02
【问题描述】:

有没有办法提取集合中的最高(或最低)值?例如,如果我有以下“字节集”:

[0, 1, 2, 4, 5, 28, 199]

我可以在上面运行任何函数并返回 199 作为结果吗?

编辑:有一个明显的暴力解决方案涉及 for..in 循环。如果可能的话,我想找到比这更好的方法。

【问题讨论】:

    标签: algorithm delphi set


    【解决方案1】:

    循环确实是形式上正确的方法。

    type
      TSetType = set of TEnumType;
    
    function HighestMember(const s: TSetType): TEnumType;
    begin
      for Result := High(Result) downto Low(Result) do
        if Result in s then
          exit;
      raise Exception.Create('empty sets have no highest member');
    end;
    

    任何其他类型的解决方案都需要类型转换或汇编程序,这两者都会迫使您失去类型安全性——可以这么说,它们会“脱离语言”。

    如果你能保证你的集合不超过32个可能的元素,那么集合可以用一个普通的整数覆盖,你的问题就相当于询问一个32位整数中最高位集合的位置.这是以前在这里问过的,差不多:

    如果您对集合类型没有 32 个元素的限制,那么您有 Delphi 的 256 个元素的限制,并且任何位旋转解决方案都需要处理 32-字节 输入.

    【讨论】:

    • 一个 256-max-count 循环来查找最高位集不是蛮力。对 DoD 的攻击或试图解密 RSA 是蛮力的。这是最好的解决方案。编写代码首先是为了可读性,其次是速度(并且只有在您发现真正的瓶颈时)。
    • 那种依赖于作为位集的集合的内部实现不是吗?否则我宁愿循环遍历集合的元素,而不是遍历所有可能的值...
    • Smasher,除了检查每个可能的值之外,没有办法枚举集合的内容。在内部,这就是“for-in”循环的工作方式。我用代码演示的方法不依赖于任何内部表示,只依赖于集合是有限的。任何位旋转的解决方案都依赖于内部表示,但这是一个非常安全的假设:德里集从未改变;它们和 Turbo Pascal 中的一样。
    【解决方案2】:

    集合是无序的,所以你不会比循环更好。如果您经常查找集合的最小/最大值,请使用heap data structure,它提供 O(1) 查找来获取最小/最大值和 O(log n) 查找任何其他值。

    【讨论】:

      【解决方案3】:

      这里有一个更快的版本,它使用了字节集内部结构的知识。

      type
        TByteSet = set of Byte;
      
      function HighestElement(const ByteSet: TByteSet): Byte;
      type
        TSetBytes = array[0..SizeOf(TByteSet) - 1] of Byte;
      var
        I, J: Integer;
        B: Byte;
        SetBytes: TSetBytes;
      begin
        if ByteSet <> [] then
        begin
          SetBytes := TSetBytes(ByteSet);
          // Start at the top and work down, one byte at a time
          for I := SizeOf(TByteSet) - 1 downto 0 do
          begin
            // Any bits set here
            B := SetBytes[I];
            if B <> 0 then
            begin
              Result := I * 8;
              for J := 0 to 7 do
                if (B shr J) and 1 <> 0 then
                begin
                  Result := Result + J;
                  Exit;
                end;
            end;
          end;
        end else
          // No elements set
      end;
      

      您可以将集合的类型 TByteSet 更改为几乎任何集合类型,并且该函数应该仍然有效。只需将函数 decl 和 body 中的 TByteSet 替换为您的集合类型即可。如果使用一组 AnsiChar 或一组枚举类型,您还可以修改它以返回实际的元素类型。要获得最小值,请将 for 循环的“I”更改为“0 to SizeOf(TByteSet) - 1”,将“J”循环中的 if 测试更改为“if (B shl J) and $80 0”

      【讨论】:

      • 嗯,除了“平台”和“已弃用”之外,我们还需要及时的“endianunsafe”指令:-)
      【解决方案4】:

      几乎相同,但更短:

      type
        TByteSet = set of Byte;
      
      function MaxSet(S: TByteSet): Byte;
      var
        CardArr: Array [0..7] of Cardinal absolute S;
        i: Byte;
      begin
        i := 7;
        while (i > 0) AND (CardArr[i] = 0) do
          Dec(i);
        Result := i + Floor(Log2(CardArr[i]));
      end;
      

      【讨论】:

        【解决方案5】:

        最高和最低,不使用数学单位:

        type
          TCharSet = set of Char;
        
        function MaxOfSet(aSet: TCharSet):Char;
        var
          Data:array[0..SizeOf(TCharSet)-1] of Byte absolute aSet;
          i,r:Byte;
        begin
          if aSet<>[] then begin
             i:=SizeOf(TCharSet)-1;
             while (i>0) and (Data[i]=0) do
                Dec(i);
             r:=i*8;
             i:=Data[i];
             while (i and $80)=0 do begin
                i:=i shl 1;
                Dec(r)
             end;
             Result:=Chr(r+7)
          end
          else
             raise Exception.Create('Unable to extract max value from an empty set');
        end;
        
        function MinOfSet(aSet: TCharSet):Char;
        var
          Data:array[0..SizeOf(TCharSet)-1] of Byte absolute aSet;
          i,r:Byte;
        begin
          if aSet<>[] then begin
             i:=0;
             while (i<SizeOf(TCharSet)-1) and (Data[i]=0) do
                Inc(i);
             r:=i*8;
             i:=Data[i];
             while (i and 1)=0 do begin
                i:=i shr 1;
                Inc(r)
             end;
             Result:=Chr(r)
          end
          else
             raise Exception.Create('Unable to extract min value from an empty set');
        end;
        

        【讨论】:

        • 为您的答案添加一些描述。这将比仅仅一段代码更有帮助。
        猜你喜欢
        • 1970-01-01
        • 2014-02-23
        • 1970-01-01
        • 1970-01-01
        • 2020-09-16
        • 1970-01-01
        • 2021-11-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多