快速查找列表中的每对值

时间:2015-02-05 06:35:12

标签: delphi delphi-xe2

例如,我从一些文件中检索了随机入口点和哈希

EP   |  Hash
25432|545676343 
25732|344284432 
93632|9432763432 
45432|194363432 
35433|345676325
15434|445676337 
35439|745676343
55437|243276342
85532|476263821
85532|156743832 
85532|626343633
85531|626343633

我们说清单非常庞大。

希望将所有数据放入内存,因为它们只是Cardinal / Integer数据类型。

如果我想找到EP = 85532和Hash = 626343633,那么快速(est)方式是什么。我不认为for loop就是答案。

注意:

  • 如果仅找到EP,将计算并搜索哈希值。
  • 没有重复数据
  • 可以对数据进行排序

感谢。

5 个答案:

答案 0 :(得分:1)

据我所知,你在Delphi中没有哈希表。你当然可以轻松写一个,但你也可以只使用一个tDictonary

看看这个并看看它是否有意义:

procedure TForm1.FormCreate(Sender: TObject);
var
  List: TDictionary<TPair<Integer, Cardinal>, Integer>;
begin

  //Dummy data
  List := TDictionary<TPair<Integer, Cardinal>, Integer>.Create;

  List.Add(TPair<Integer, Cardinal>.Create(25432, 545676343), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(25732, 344284432), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(93632, 9432763432), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(45432, 194363432), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(35433, 345676325), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(15434, 445676337), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(35439, 745676343), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(55437, 243276342), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(85532, 476263821), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(85532, 156743832), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(85532, 626343633), List.Count);
  List.Add(TPair<Integer, Cardinal>.Create(85531, 626343634), List.Count);

  //check if exists
  List.ContainsKey(TPair<Integer, Cardinal>.Create(85531, 626343634));

  //Free data
  FreeAndNil(List);
end;

答案 1 :(得分:1)

这是一个包含字典和对象的示例,如果需要,它可以存储和构建哈希值。

program so_28337613;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections,
  System.Generics.Defaults;

type
  // data object
  THasher = class
  private
    FEP: Integer;
    FHasHash: Boolean;
    FHash: Cardinal;
    function GetHash: Cardinal;
  protected
    procedure BuildHash( out AHash: Cardinal );
  public
    constructor Create( const EP: Integer ); overload;
    constructor Create( const EP: Integer; const Hash: Cardinal ); overload;
    property EP: Integer read FEP;
    property Hash: Cardinal read GetHash;
  end;

  { THasher }

procedure THasher.BuildHash( out AHash: Cardinal );
begin
  Writeln( 'DEBUG: Building Hash' );
  AHash := FEP;
end;

constructor THasher.Create( const EP: Integer );
begin
  inherited Create;
  FEP := EP;
end;

constructor THasher.Create( const EP: Integer; const Hash: Cardinal );
begin
  Create( EP );
  FHash := Hash;
  FHasHash := True;
end;

function THasher.GetHash: Cardinal;
begin
  if not FHasHash
  then
    begin
      BuildHash( FHash );
      FHasHash := True;
    end;
  Result := FHash;
end;

procedure Test;
var
  LHashDict: TObjectDictionary<THasher, Boolean>;
  LSearchFor: THasher;
begin
  LSearchFor := nil;
  LHashDict := nil;
  try
    LHashDict := TObjectDictionary<THasher, Boolean>.Create(
      {Ownerships} [doOwnsKeys],
      {AEqualityComparer} TEqualityComparer<THasher>.Construct(
        {EqualityComparison} (
            function( const L, R: THasher ): Boolean
      begin
        Writeln( 'DEBUG: Compare' );
        Result := ( L.EP = R.EP ) and ( L.Hash = R.Hash );
      end ),
    {Hasher} (
      function( const I: THasher ): Integer
      begin
        Result := I.EP;
      end ) ) );

    // Add known hashes

    LHashDict.Add( THasher.Create( 1, 45 ), True );
    LHashDict.Add( THasher.Create( 2, 56 ), True );
    LHashDict.Add( THasher.Create( 3, 76 ), True );
    LHashDict.Add( THasher.Create( 4, 34 ), True );
    LHashDict.Add( THasher.Create( 5, 5 ), True );
    LHashDict.Add( THasher.Create( 6, 23 ), True );
    LHashDict.Add( THasher.Create( 7, 78 ), True );
    LHashDict.Add( THasher.Create( 8, 89 ), True );

    // Looking for an object with now unknown hash
    LSearchFor := THasher.Create( 5 );

    if LHashDict.ContainsKey( LSearchFor )
    then
      Writeln( 'GOTCHA' );

  finally
    LHashDict.Free;
  end;
end;

begin
  try
    Test;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  Readln;

end.

当调试输出状态时,只有一个比较和一个哈希构建。

答案 2 :(得分:0)

除非您的数据具有比当前可观察的结构更多的结构(它似乎是无序的),并且您希望仅执行一次查找,否则您将无法击败线性搜索,即使它具有O( n)复杂性。所有其他选项至少具有第一次搜索的复杂性。

如果订购了数据,那么您可以使用二进制搜索有效地搜索多个项目。如果数据没有被订购,那么它是一个O(n log n)操作来订购它,这显然是昂贵的。但是,一旦订购,则二进制搜索为O(log n)。

另一种选择是填充字典。标准的Delphi字典有O(1)查找。然而,再次形成字典是昂贵的。但是如果你可以在排序和构建字典之间做出选择,那就选择后者,因为它应该更快地构建和执行查找。

总结:

  • 要执行单个查找,或者执行非常少量的查找,请使用线性搜索。
  • 要执行多次查找,请使用字典。

从表面上看,人们会认为EP是你词典的关键。但是你似乎有多对具有相同的EP。所以我猜你需要一个复杂的价值结构,其中包含与一个特定EP密钥相关的所有信息。

答案 3 :(得分:0)

虽然我的下一个建议不会为您提供最佳性能,但它很容易实现,并且仍能提供相当好的性能。

现在,在数据搜索中获得性能的最简单方法是将数据拆分为有组织的组。

根据您声称主搜索键是EP值并且EP值似乎是五位数的事实,我建议创建100个组(单独的数组)。

这些数组中的每一个都会以这样的方式存储部分数据:
首先排列EP值介于0和1000之间的所有项目 第二个数组所有EP值在1001和2000之间的项目 ...

这将允许使用启发式方法来减少您需要迭代的项目数量,只需要确定特定项目属于哪个组,然后只迭代该特定组中的项目。您只需将EP值除以1000就可以做到这一点。

这大大减少了您需要迭代的项目数量,并且不要求您按照二进制搜索的要求对所有项目进行完美排序。

此外,如果可能存在大量具有相同EP值的不同项目,您可能需要创建单独的数组来存储具有相同EP值的多个项目,以减少内存使用量。

因此,例如,具有用于存储的单独数组让我们说100个具有EP值25759的项目将仅需要32位用于对阵列的引用以及100倍32位(整数的大小),其总计3232位或404字节。登记/> 但是将这些项目成对存储将需要基本部分的32位32位和整数部分的32位,总计为6400位或800字节。

答案 4 :(得分:0)

假设您可以为您需要的数据项目中添加一个数字,那么这可能是一种方法。我无法想象检索可能会更快但这是以记忆为代价的......

unit EZStore;

interface

const
  MAX_HASHES = 5;
  MAX_EPS = 10000000;

type
  THashArray = Array[0..MAX_HASHES -1] of Int64;

  TEZStore = class(TObject)
  private
    FData : Array[0..MAX_EPS - 1] of THashArray;
  public
    procedure Initialise();
    procedure Store(const AEP : Integer; const AHash : Int64);
    function Retrieve(const AEP : Integer) : THashArray;
  end;

implementation

uses
  SysUtils;

procedure TEZStore.Initialise;
begin
  FillChar(FData, MAX_HASHES * MAX_EPS, 0);
end;

function TEZStore.Retrieve(const AEP: Integer): THashArray;
begin
  Result := FData[AEP];
end;

procedure TEZStore.Store(const AEP: Integer; const AHash: Int64);
var
  ThisHashArray : THashArray;
  i : integer;
begin
  i := 0;
  ThisHashArray := FData[AEP];

  while(FData[AEP][i] <> 0) do begin
    Inc(i);
    if (i > MAX_HASHES - 1) then
      raise Exception.Create(Format('The maximum of %d hashes per entry point has been exceeded', [MAX_HASHES]));
  end;

  FData[AEP][i] := AHash;
end;

end.