smile.neighbor.LinearSearch Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2010 Haifeng Li
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package smile.neighbor;
import java.util.List;
import smile.math.distance.Distance;
import smile.sort.HeapSelect;
/**
* Brute force linear nearest neighbor search. This simplest solution computes
* the distance from the query point to every other point in the database,
* keeping track of the "best so far". There are no search data structures to
* maintain, so linear search has no space complexity beyond the storage of
* the database. Although it is very simple, naive search outperforms space
* partitioning approaches (e.g. K-D trees) on higher dimensional spaces.
*
* @param the type of data objects.
*
* @author Haifeng Li
*/
public class LinearSearch implements NearestNeighborSearch, KNNSearch, RNNSearch {
/**
* The dataset of search space.
*/
private T[] data;
/**
* The distance function used to determine nearest neighbors.
*/
private Distance distance;
/**
* Whether to exclude query object self from the neighborhood.
*/
private boolean identicalExcluded = true;
/**
* Constructor. By default, query object self will be excluded from search.
*/
public LinearSearch(T[] dataset, Distance distance) {
this.data = dataset;
this.distance = distance;
}
@Override
public String toString() {
return String.format("Linear Search (%s)", distance);
}
/**
* Set if exclude query object self from the neighborhood.
*/
public LinearSearch setIdenticalExcluded(boolean excluded) {
identicalExcluded = excluded;
return this;
}
/**
* Get whether if query object self be excluded from the neighborhood.
*/
public boolean isIdenticalExcluded() {
return identicalExcluded;
}
@Override
public Neighbor nearest(T q) {
T neighbor = null;
int index = -1;
double dist = Double.MAX_VALUE;
for (int i = 0; i < data.length; i++) {
if (q == data[i] && identicalExcluded) {
continue;
}
double d = distance.d(q, data[i]);
if (d < dist) {
neighbor = data[i];
index = i;
dist = d;
}
}
return new SimpleNeighbor(neighbor, index, dist);
}
@Override
public Neighbor[] knn(T q, int k) {
if (k <= 0) {
throw new IllegalArgumentException("Invalid k: " + k);
}
if (k > data.length) {
throw new IllegalArgumentException("Neighbor array length is larger than the dataset size");
}
SimpleNeighbor neighbor = new SimpleNeighbor(null, 0, Double.MAX_VALUE);
@SuppressWarnings("unchecked")
SimpleNeighbor[] neighbors = (SimpleNeighbor[]) java.lang.reflect.Array.newInstance(neighbor.getClass(), k);
HeapSelect> heap = new HeapSelect>(neighbors);
for (int i = 0; i < k; i++) {
heap.add(neighbor);
neighbor = new SimpleNeighbor(null, 0, Double.MAX_VALUE);
}
for (int i = 0; i < data.length; i++) {
if (q == data[i] && identicalExcluded) {
continue;
}
double dist = distance.d(q, data[i]);
Neighbor datum = heap.peek();
if (dist < datum.distance) {
datum.distance = dist;
datum.index = i;
datum.key = data[i];
datum.value = data[i];
heap.heapify();
}
}
heap.sort();
return neighbors;
}
@Override
public void range(T q, double radius, List> neighbors) {
if (radius <= 0.0) {
throw new IllegalArgumentException("Invalid radius: " + radius);
}
for (int i = 0; i < data.length; i++) {
if (q == data[i] && identicalExcluded) {
continue;
}
double d = distance.d(q, data[i]);
if (d <= radius) {
neighbors.add(new SimpleNeighbor(data[i], i, d));
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy