Java бинарный поиск с использованием пользовательской функции - PullRequest
1 голос
/ 14 апреля 2020

Возможно ли в Java запустить бинарный поиск для функции элементов, а не для самих элементов?

Другими словами, бинарный поиск по f (A [i]), а не по A [i].

Например, рассмотрим этот массив: [1, 3, 4, 5, 9].
Цель состоит в том, чтобы найти первый элемент, квадрат которого больше или равен 20. (В этом случае ответ 5).

Можем ли мы достичь этого в Java с его реализацией binarySearch (без написания нашего собственного бинарного поиска)?

Я видел, что есть версия binarySearch, которая использует компаратор, но я не уверен, гарантируется ли, что первый сравниваемый элемент будет элементом из массива, а второй элемент будет целью?

binarySearch(T[] a, T key, Comparator<? super T> c)

Также обратите внимание, что "квадрат" является лишь примером, который имеет обратный квадрат root. Но в целом предположим, что обратная функция отсутствует. ie мы хотим применить функцию к элементам для двоичного поиска.

Еще одно примечание: предположим, что массив отсортирован, а f (i) увеличивается.

Ответы [ 4 ]

1 голос
/ 15 апреля 2020

Да, это возможно. Согласно документации:

Метод возвращает индекс ключа поиска, если он содержится в списке; в противном случае
(-(insertion point) - 1). Точка вставки определяется как точка, в которой ключ будет вставлен в список: индекс первого элемента больше, чем ключ, или list.size(), если все элементы в списке меньше указанного ключа. Обратите внимание, что это гарантирует, что возвращаемое значение будет> = 0 в том и только в том случае, если ключ найден.

Таким образом, регулируя возвращаемый индекс, вы можете получить следующее значение (или даже предыдущее значение), ближайшее место, где было бы найдено желаемое значение.

Это работает следующим образом:

  • генерирует list значений от 1 до 20. Вызовите затем n
  • создайте лямбду, fn для применения к каждому n.
  • создайте Comparator<Integer> comp, который использует fn для сравнения
  • вызовите двоичную сортировку с помощью list, n и comp
  • используют возвращаемый индекс для поиска ближайшего n для f(n)

Вот пример, который возвращает ближайшее значение к f(n)

    Random r = new Random(23);
        // define f(n)
        Function<Integer, Integer> f = x -> x*x + 2 * x + 3;

        Comparator<Integer> comp =
                (a, b) -> Integer.compare(f.apply(a), f.apply(b));
        for (int i = 0; i < 5; i++) {
            // generate a list of f(n) values.
            List<Integer> list = r.ints(10, 1, 20).distinct()
                    .sorted().boxed()
                    .collect(Collectors.toList());
            List<Integer> fns = list.stream().map(f::apply).collect(Collectors.toList());
            // Choose a random n
            int n = r.nextInt(20);

            System.out.println("fns = " + fns);
            System.out.println("n = " + list);

            // now find nearest f(n) in the list.
            int fn = f.apply(n);
            System.out.println("searching for nearest value to f(" + n
                    + ")   [" + fn + "]");

            int index = Collections.binarySearch(list, n, comp);

            // Now determine the index of the value that
            // meets the requirements.
            if (index >= 0) {
                System.out
                        .println("Exact answer = " + list.get(index));
                System.out.println();
            } else {
                int nearest;
                index = -index - 1;
                // check end cases
                if (index == 0) {
                    nearest = list.get(index);
                } else if (index == list.size()) {
                    nearest = list.get(index - 1);
                } else {
                    // check middle cases
                    int lowerValue = list.get(index - 1);
                    int higherValue = list.get(index);
                    if (n - lowerValue > higherValue - n) {
                        nearest = higherValue;
                    } else {
                        nearest = lowerValue;
                    }
                }
                System.out.println(
                        "Nearest result to " + n + " is " + nearest);
                System.out.println();
            }
        }
    }

Отпечатки:

fn = [18, 51, 66, 83, 102, 123, 171, 291, 363]
n = [3, 6, 7, 8, 9, 10, 12, 16, 18]
searching for nearest value to f(14)   [227]
Nearest result to 14 is 12

fn = [6, 11, 38, 51, 83, 198, 326]
n = [1, 2, 5, 6, 8, 13, 17]
searching for nearest value to f(17)   [326]
Exact answer = 17

fn = [11, 18, 83, 146, 171, 227, 291, 326]
n = [2, 3, 8, 11, 12, 14, 16, 17]
searching for nearest value to f(0)   [3]
Nearest result to 0 is 2

fn = [6, 18, 27, 51, 66, 198, 363]
n = [1, 3, 4, 6, 7, 13, 18]
searching for nearest value to f(1)   [6]
Exact answer = 1

fn = [11, 18, 66, 102, 146, 198, 258, 291, 326]
n = [2, 3, 7, 9, 11, 13, 15, 16, 17]
searching for nearest value to f(0)   [3]
Nearest result to 0 is 2

0 голосов
/ 16 апреля 2020

Я нашел ответ, который немного хитрый (хакерский).

Однако я думаю, учитывая тот факт, что BinarySearch Java возвращает (-(insertion point) - 1), когда элемент не является массивом, это честная игра.

Идея состоит в том, чтобы выделить guish цель, например, отрицая ее, поэтому в функции сравнения, если число отрицательное, мы знаем, что оно является целью (и отрицает ее), и если оно положительное это элемент массива, и мы должны применить функцию.

Это основано на предположении, что в массиве нет отрицательного числа. Если есть отрицательные числа, этот метод не работает.

Вот код для примера в вопросе:

import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Function;

public class BinarySearchFx {

    public static void main(String[] args) {
        Integer[] arr = {1, 3, 4, 5, 9};
        Function<Integer, Integer> f = x -> x*x;
        Comparator<Integer> comparator = (o1, o2) -> {
            o1 = o1 > 0? f.apply(o1) : -o1;
            o2 = o2 > 0? f.apply(o2) : -o2;
            return o1 - o2;
        };

        int result = Arrays.binarySearch(arr, -20, comparator);
        if (result < 0)
            result = -result - 1;
        System.out.printf("index: %d, element: %d", result, arr[result]); // prints index: 3, element: 5
    }
}
0 голосов
/ 14 апреля 2020

Dichotomy Binary Search: вот хороший пример:

http://en.verejava.com/?id=22985888488212

public static int binarySearch(int[] arrays, int searchValue) {
    int low = 0;
    int high = arrays.length - 1;
    int mid = 0;
    while (low <= high) {
        mid = (low + high) / 2;
        if (arrays[mid] == searchValue) {
            return mid;
        } else if (arrays[mid] < searchValue) { 
            low = mid + 1;
        } else if (arrays[mid] > searchValue) {
            high = mid - 1;
        }
    }
    return -1;
}
0 голосов
/ 14 апреля 2020

Да, это возможно, проверьте это:

package control;

import java.util.Comparator;

class Testing {
    int a;
    String b;
    Testing(int a, String b) {
        this.a = a;
        this.b = b;
    }
}

public class FunctionalBinarySearch {

    private static <T> int binarySearch(T[] array, T t, Comparator<? super T> c) {
        int l = 0, r = array.length - 1, m;
        while (l <= r) {
            m = l + (r - l) / 2;
            if (c.compare(array[m], t) == 0) return m;
            if (c.compare(array[m], t) < 0) l = m + 1;
            else if (c.compare(array[m], t) > 0) r = m - 1;
        }
        return -1;
    }

    public static void main(String[] args) {
        Testing[] test = new Testing[]{
                new Testing(1, "a"),
                new Testing(2, "b"),
                new Testing(3, "c"),
                new Testing(4, "d")
        };

        System.out.println(binarySearch(test, new Testing(3, "xyz"),
                //This lambda is applied as a search criteria function
                Comparator.comparingInt(testing -> testing.a)));

        //Uses the string comparator
        System.out.println(binarySearch(test, new Testing(1, "d"),
                Comparator.comparing(testing -> testing.b)));
    }

}

Примечания

  • Этот алгоритм двоичного поиска требует упорядочения входного массива.
  • Для каждого вызова требуется указывать компаратор.
  • Возвращает индекс объекта, если он есть, и -1, если его нет.
  • Компаратор описывает критерий поиска . лямбда, которая имеет доступ к двум объектам и сравнивает атрибуты (если это позволяет модификатор доступа) или функции, объявленные для этих объектов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...