簡體   English   中英

什么是O(1)-搜索內存有效的數據結構來存儲整數對?

[英]What is an O(1)-search memory-efficient data structure to store pairs of integers?

考慮以下接口:

public interface CoordinateSet {
    boolean contains(int x, int y);
    default boolean contains(Coordinate coord) {
        return contains(coord.x, coord.y);
    }
}

它表示一組二維整數坐標,每個可能的坐標可以在該集合內部( contains返回true )或外部( contains返回false )。

我們可以通過多種方式來實現這樣的接口。 計算效率最高的方法是由數組支持的實現:

public class ArrayCoordinateSet implements CoordinateSet {
    private final boolean[][] coords = new boolean[SIZE][SIZE];
    // ...
    @Override
    public boolean contains(int x, int y) {
        return coords[x][y];
    }
    public void add(int x,  int y) {
        coords[x][y] = true;
    }
    // ...

}

但是,如果SIZE很大,比如說1000,並且只有4個屬於該集合的坐標,就在1000×10000矩形的四個角度中,這意味着絕對大部分的cells空間都被false值。 對於這種稀疏的CoordinateSet,我們最好使用基於HashSetCoordinateSet

public final class Coordinate {
    public final int x;
    public final int y;
    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }
    // .equals() and hashCode()
}
public class HashBasedCoordinateSet implements CoordinateSet {
    private final Set<Coordinate> coords = new HashSet<>();
    @Override
    public boolean contains(int x, int y) {
        return coords.contains(new Coordinate(x, y));
    }
    @Override
    public boolean contains(Coordinate coord) {
         return coords.contains(coord);
    }
    public void add(Coordinate coord) {
        coords.add(coord);
    }
}

但是,對於HashBasedCoordinateSet我們HashBasedCoordinateSet這樣的問題:

for (int x=0; x<1000; x++) {
  for (int y=0; y<1000; y++) {
    hashBasedCoordinateSet.contains(x, y);
  }
}

當我們具有值xy並要檢查hashBasedCoordinateSet.contains(x, y) ,這將需要在每個方法調用處創建一個新對象(因為我們始終需要一個對象在HashSet進行搜索,這還不夠)只是擁有對象的數據)。 這確實是浪費CPU時間(它需要創建所有這些Coordinate對象,然后抓取並收集它們,因為在此代碼上似乎無法進行轉義分析優化)。

所以最后,我的問題是:

存儲稀疏坐標集的數據結構是什么:

  1. 具有O(1) contains(int x, int y)操作;
  2. 有效利用空間(與基於數組的實現不同);
  3. contains(int x, int y)期間不必創建額外的對象嗎?

long是Java中整數大小的兩倍,因此可以在一個long中存儲兩個int。 那呢?

public class CoordinateSet {
    private HashSet<Long> coordinates = new HashSet<>();

    public void add(int x, int y) {
        coordinates.add((x | (long) y << 32));
    }

    public boolean contains(int x, int y) {
        return coordinates.contains((x | (long) y << 32));
    }
}

我很確定contains方法中的long存儲在堆棧中。

當然,不進行評估就進行優化總是很危險的。 您可能應該配置您的應用程序,以查看是否確實存在瓶頸。

您還會產生兩個用例

  1. 在集合中查找單個坐標
  2. 查找給定范圍內屬於集合的所有坐標

通過遍歷集合的迭代器,並過濾掉不需要的迭代器,可以使步驟2效率更高。 這可能會以任意順序返回數據。 而且性能很大程度上取決於數據集的大小。

也許像Guava提供的那樣,一個簡單的表數據結構可以為您提供更好的接口-將X和Y坐標索引為整數,同時為您提供O(1)訪問。

Table<Integer, Integer, Coordinate> index = HashBasedTable.create();

另一個建議是研究位置敏感的哈希。 基本上,您將創建一個新的哈希函數,該函數將XY坐標映射到易於查詢的公共一維空間中。 但這可能超出范圍。

如果要具有O(1)數據結構,則需要具有獨立於要存儲在數據結構中的實際值的查找機制。 做到這一點的唯一方法是枚舉您的值並派生一個公式來計算您擁有的貨幣對的枚舉值,然后為每個枚舉值設置一個是/否值的數組。

例如,如果確保x保證在0到79之間,並且y保證在0到24之間,則可以使用枚舉公式y * 80 + x,對於(10,10)對810。然后,如果為810存儲的值是“是”,則在很大的“是/否”值數組中查找。

因此,如果您堅持使用O(1)算法,則需要空間來保存yes / no值。

您可以使用構成x和y值的位作為鍵來嘗試二叉樹。 例如,如果x和y是32位整數,則樹的總深度為64。因此,您將遍歷x和y的位,最多進行64個決策才能得出包含/不包含的答案。

更新以回應評論:當然,如果您想要O(1),樹不是您通常想到的,但是請記住,原始問題中基於數組的方法只有O(1)達到實現的限制有效內存。 我正在做的是假設整數的位長是固定的實現約束,這通常是一個安全的假設。 換句話說,如果您確實希望contains()調用在恆定時間內運行,則可以對其進行編碼,使其始終執行64個比較操作,然后返回。

誠然,CS教授可能不會接受這種說法。 自從我們刪除作業標簽以來,我一直很難知道有人是想要真實答案還是理論CS答案

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM