All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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