Java中的非对称最近邻居

时间:2011-07-25 09:01:35

标签: java data-structures nearest-neighbor asymmetric

从排序后的地图中,我想检索 n 条目的子集,在指定值 v 之前启动 m 条目。例如,对于键集 k = {0.2,0.3,0.4,0.6,0.8,0.9,1.0}, n = 5, m的查询 = 2, v = 0.5,将返回{0.3,0.4,0.6,0.8,0.9}。是否在Java中支持这样的查询的数据结构的实现,而不必迭代整个(大)集?

我需要什么?插值。我想根据地图中的值插入 v 。但是,我有很多 v 。它们被排序,并且它们之间的间距远小于 k 中的间距。因此,我从地图中取出一系列条目,用它们做一些昂贵的预备计算(例如计算多项式的系数),然后可以快速插入该范围内的另一个值(通过用该值评估多项式)。 / p>

但为什么在 v 之前需要 m 条目? k 中的值通常是等间距的,为了避免插值间隔末端的高振荡的Runge现象,我只需将它们切掉,这意味着我需要在实际之前使用一些节点插值的有效间隔。

这有意义吗?你的建议是什么?

(如果像java.util.TreeMap.ceilingEntry()这样的方法会返回一个迭代器会很有趣,我可以使用它迭代两次。)

3 个答案:

答案 0 :(得分:1)

这比这简单得多:

  1. 使用二进制搜索获取v将在列表中插入的位置,以便它保持排序。
  2. 向左移动m个位置
  3. 将前n个元素放在右边。

    double[] k = new double[] {0.2, 0.3, 0.4, 0.6, 0.8, 0.9, 1.0};
    int n=5;
    int m=2;
    double v=0.5;
    
    int pos = Arrays.binarySearch(k, v);
    if (pos < 0)
        pos = -pos - 1;
    
    while(pos > 0 && k[pos-1]==v)
        --pos;
    
    pos = Math.max(pos-m, 0);
    
    double[] result = Arrays.copyOfRange(k, pos, Math.min(pos+n, k.length));
    

答案 1 :(得分:1)

使用headMap() tailMap()可能是最简单的解决方案。如果担心两次进行相同搜索的开销,则使用列表而不是映射可能是解决方案。我延长了Petar的建议。它现在可以处理键值对,由小子类Pair表示:

public class DataSet {

  // Usage example
  public static void main (String [] args) {

    DataSet nn = new DataSet ();
    nn.add(0.2,1);
    nn.add(0.3,2);
    nn.add(0.4,3);
    nn.add(0.6,4);
    nn.add(0.8,5);
    nn.add(0.9,6);
    nn.add(1.0,7);

    int n = 5;
    int m = 2;
    double v = 0.5;

    ListIterator <Pair> it = nn.iterator(v);
    for (int i=0; i<m; ++i)
      it.previous();      
    for (int i=0; i<n; ++i)
      System.out.append(it.next()+"\n");
  }

  // Implementation
  TreeSet <Pair> set = new TreeSet <Pair> (new PairComparator());
  ArrayList <Pair> list = new ArrayList <Pair> ();
  boolean listUpToDate = false;

  // Add data
  boolean add (double x, double y) {
    listUpToDate = false;
    return set.add(new Pair(x,y));
  }

  // Get iterator at specified position
  ListIterator <Pair> iterator (double v) {
    if (!listUpToDate) {
      list = new ArrayList (set);
      listUpToDate = true;
    }
    int pos = Collections.binarySearch(list,v);
    if (pos < 0)
      pos = -pos - 1;
    return list.listIterator(pos);
  }

  // Helper class
  static class Pair implements Comparable <Double> {
    double x, y;
    Pair (double x, double y) {
      this.x = x; this.y = y;
    }
    public int compareTo (Double d) {
      return Double.valueOf(x).compareTo(d);
    }
    public String toString () {
        return String.format("[%.1f,%.1f]",x,y);
    }
  }

  // Used for sorting
  class PairComparator implements Comparator <Pair> {
    public int compare (Pair n0, Pair n1) {
      return Double.valueOf(n0.x).compareTo(Double.valueOf(n1.x));
    }
  }

}

当然,也可以使用该列表,并确保在调用binarySearch()之前对其进行排序。但是TreeSet除了排序之外还有其优点,它可以防止重复键。

答案 2 :(得分:0)

您可以将条目放在已排序的数组中,并使用Arrays.binarySearch()。

但是如果你必须有像TreeMap这样的NavigableMap,你需要做两次查找来获取headMap()和tailMap()并迭代它们。