映射两组元素

时间:2011-05-27 15:52:30

标签: delphi map indexing set lookup

有一组索引为[0,n]的元素。在任何时候,来自A组的大多数m个元素可以是活动的(在使用中),具有0 <= m <= n的明显条件。我想索引本地数组中的那些活动元素。程序执行时可以动态停用元素,我希望在激活新元素时可以重用其索引。

我想以最有效的方式将元素索引映射到它的本地索引,因为我正在使用本地索引来快速查找活动元素数据。

普通散列函数(元素索引==局部索引)和暴力强制通过关联列表的可能解决方案不能很好地扩展到大n。

数据结构的动态扩展/缩小是一个明显的优势。

谢谢

1 个答案:

答案 0 :(得分:2)

  

我想以最有效的方式将元素索引映射到它的本地索引

没有最快的代码(tanstatfc) - Michael Abrash

您的问题有很多解决方案 第一步是选择数据结构和散列函数。

数据结构可以是:

  1. 普通数组+直接哈希
  2. 将起始指针保存到链接列表+哈希链接到起始元素的数组
  3. 二叉树,其中哈希表示树的分支
  4. 我要到此为止。
  5. 1平原阵列

    这是最简单的解决方案。如果您的分配是blobby (这意味着它们在空间中聚集在一起),这甚至可能是一个很好的解决方案。
    以下是它的工作原理:

    1. 你占据了很大一部分虚拟内存。你可以申请2GB的内存(即使在1GB的机器上),因为它只是虚拟的,只有你实际使用它才会被提交。
    2. 将块拆分为4KB块,或其倍数(x86处理器使用4KB块)并使索引数组开始指定是否已提交4K块。
    3. 如果您需要编写,请检查索引是否已提交页面,如果尚未提交,请提交。
    4. 写信给列表。
    5. 如果需要阅读,请检查索引,如果页面尚未提交,则没有命中,返回false,否则读取条目。
    6. 每个条目可以容纳2GB / 4bytes =此结构中有5亿个条目 这最适用于分组在一起的群集中的数据 如果索引是随机的,这将是低效的。

      这是Delphi伪代码:

      使用虚拟内存的直接列表的示例代码

      type
        TElement = record
          Data: string; //really a pointer to string in Delphi
          function PutInList(IndexNr: integer): PElement;
          constructor CreateFromList(Index: integer);
        end;
      
        PElement = ^TElement;
        TElements = array[0..0] of TElement;
        PElements = ^TElements;
      
      const
        ArraySize = (1024 * 1024 * 1024 * 2); //2GB
        BlockSize = 4096;
        NumberOfBlocks = (ArraySize) div (BlockSize); //2GB in 4KB blocks
        BitsPerInt32 = 32;
        IndexCount = NumberOfBlocks div BitsPerInt32;
      
      var
        IndexBlock: array[0..IndexCount-1]; //1 bit for each block = 64KB.
      
      var
        Elements: PElements;
      
      function ClaimVirtualBlock: PElements; 
      begin
        FillChar(IndexBlock, #0, SizeOf(IndexBlock)); //Zero init indexblock
        Result:= PElements(VirtualAlloc(nil, ArraySize, MEM_RESERVE, PAGE_READWRITE));
      end;
      
      function GetAddress(Index: integer): PElement; inline;
      var
        DestAddress: PElement;
        BlockIndex: Integer;
        BlockDWord: integer;
        BlockMask: integer;
        BlockNotAllocated: boolean;
      begin
        //Create a new element in our list
        Integer(DestAddress):= Index * SizeOf(TElement); 
        BlockIndex:= Integer(DestAddress) div 4096;
        BlockMask:= 1 shl (BlockIndex mod 32);
        BlockIndex:= BlockIndex div 32;
        BlockNotAllocated:= (IndexBlock[BlockIndex] and BlockMask) <> 0;
        if BlockNotAllocated then begin
          IndexBlock[BlockIndex]:= IndexBlock[BlockIndex] or BlockMask;
          if VirtualAlloc(DestAddress, BlockSize, MEM_COMMIT, PAGE_READWRITE) = nil then
            raise EOutOfMemoryError.Create('Out of physical memory');
        end;
        Result:= DestAddress;
      end;
      
      
      function TElements.PutInList(IndexNr: integer): PElement;
      begin
        Result:= GetAddress(IndexNr);
        Result^.Data:= Self.Data;
      end;
      
      constructor TElements.CreateFromList(Index: integer);
      var
        ListElement: PElement;
      begin
        ListElement:= GetAddress(Index);
        Self.IndexNr:= Index;
        Self.Data:= ListElement.Data;
      end;
      

      2包含链表的数组

      1. 使用指向链接列表的指针创建一个数组。
      2. 散列索引,这指向您的数组项。
      3. 浏览链接列表,直至找到正确的项目。
      4. 用于插入项目:执行步骤1和2,然后在开头插入项目。
      5. 这对于具有非常随机索引的数据最有效,几乎没有碰撞变化 成功取决于你的哈希函数,它需要尽可能多地选择不同的数组条目,collisions太多,你只会一直走同一个链表。

        链接列表数组的示例代码

        type  
          PElement = ^TElement;
          TElement = record
            Index: integer;
            Data: string;
            Next: PElement;
            procedure PutInList;
            constructor CreateFromList(AIndex: integer);
          end;
        
        const
          LargePrimeNumber = 100003;
        
        var
          StartArray: array[0..LargePrimeNumber-1] of PElement;
        
        procedure InitList;
        begin
          FillChar(StartArray, #0, SizeOf(StartArray));
        end;
        
        function IndexToArrayHash(AnIndex: integer): integer; inline;
        begin
          Result:= AnIndex mod LargePrimeNumber;
        end;
        
        procedure TElement.PutInList;
        var
          ArrayIndex: integer;
        begin
          ArrayIndex:= IndexToArrayHash(Self.Index);
          Self.Next:= StartArray[ArrayIndex];
          StartArray[ArrayIndex]:= @Self;
        end;
        
        constructor CreateFromList(AIndex: integer);  
        var
          ArrayIndex: integer;
          Element: PElement;
        begin
          ArrayIndex:= IndexToArrayHash(AIndex);
          Element:= StartArray[ArrayIndex];
          while (Element <> nil) and (Element.Index <> AIndex) do begin
            Element:= Element^.Next;
          end; {while}
          if (Element <> nil) then begin
            Self.Index:= Element^.Index;
            Self.Data:= Element^.Data;
            Self.Next:= nil;
          end;
        end;
        

        3二叉树使用索引中的位来遍历树

        1. 使用根
        2. 创建一个空树
        3. 如果我们要插入一个项目,请使用索引中的位来遍历树(0 =左侧分支,1 =右侧分支)。
        4. 虽然遍历树会在下面添加排名较高的索引,但在较高的索引之上插入排名较低的索引。 (沉重的物品沉到底部)。
        5. 使用二叉树的示例代码

          type
            PElement = ^TElement;
            TElement = record
              Left, Right: PElement;
              Index: integer;
              Data: string;
              procedure PutInList;
            end;
          
          function GetFromList(AIndex: integer): PElement;
          
          var
            Root: TElement;
          
          const
            GoLeft: false;
            GoRight: true;
          
          procedure InitRoot;
          begin
            FillChar(Root, #0, SizeOf(Root));
          end;
          
          function TElement.PutInlist;
          var
            CurrentElement: PElement;
            PrevElement:= PElement;
            Depth: integer;
            Direction: boolean;
          begin
            PrevElement:= nil;
            CurrentElement:= @Root;
            Depth:= 0;
            //Walk the tree
            while (CurrentElement <> nil) and (CurrentElement.Index < Index) do begin
              PrevElement:= CurrentElement;
              Direction:= Odd(Index shr Depth);
              case Direction of
                GoLeft: CurrentElement:= CurrentElement^.Right;
                GoRight: CurrentElement:= CurrentElement^.Left;
              end; {case}
              Inc(Depth);
            end; {while}
          
            //Insert the item here
            case Direction of 
              GoLeft: PrevItem^.Left:= @Self;
              GoRight: PrevItem.Right:= @Self;
            end;
          
            //What to do with the currentItem.
            if Assigned(CurrentItem) then begin
              Direction:= Odd(CurrentItem^.Index shr Depth);
              case Direction of
                GoLeft: begin
                  Self.Left:= CurrentItem;
                  Self.Right:= CurrentItem^.Right;
                end; {GoLeft}
                GoRight: begin
                  Self.Right:= CurrentItem;
                  Left:= CurrentItem^.Left;
                end; {GoRight}
              end; {case}
            end; {if}
          end;
          
          function TElement.GetFromList(AIndex: integer): PElement;
          var
            CurrentElement: PElement;
            Depth: integer;
            Direction: boolean;
          begin
            CurrentElement:= @Root;
            Depth:= 0;
            //Walk the tree
            while (CurrentElement <> nil) and (CurrentElement.Index < Index) do begin
              Direction:= Odd(Index shr Depth);
              case Direction of
                GoLeft: CurrentElement:= CurrentElement^.Right;
                GoRight: CurrentElement:= CurrentElement^.Left;
              end; {case}
              Inc(Depth);
            end; {while}
            if Assigned(CurrentElement) and (CurrentElement^.Index = AIndex) then begin
              Result:= CurrentElement;
            end
            else Result:= nil;
          end;
          

          推荐阅读:
          Knuth:TAOCP I:基本算法,第2章ISBN 0-201-03809-9
          Cormen,Leiserson&amp; Rivest:算法导论,第三章数据结构ISBN 0-07-013143-0