Java相当于python中的bisect

发布于 2024-09-03 19:20:59 字数 336 浏览 12 评论 0原文

Java 中是否有与 Python 的 bisect 模块 等效的模块?使用 Python 的二分法,您可以按方向进行数组二分法。例如 bisect.bisect_left 的作用是:

找到列表中项目的正确插入点以保持排序顺序。参数 lo 和 hi 可用于指定应考虑的列表子集;默认情况下使用整个列表。

我知道我也可以通过二分搜索手动执行此操作,但我想知道是否已经有一个库或集合可以执行此操作。

Is there an equivalent in Java for Python's bisect module? With Python's bisect you can do array bisection with directions. For instance bisect.bisect_left does:

Locate the proper insertion point for item in list to maintain sorted order. The parameters lo and hi may be used to specify a subset of the list which should be considered; by default the entire list is used.

I know I can do this manually with a binary search too, but I was wondering if there is already a library or collection doing this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一笔一画续写前缘 2024-09-10 19:20:59

您有两个选择:

You have two options:

又怨 2024-09-10 19:20:59

到目前为止(Java 8),它仍然缺失,所以您仍然必须自己制作。这是我的:

public static int bisect_right(int[] A, int x) {
    return bisect_right(A, x, 0, A.length);
}

public static int bisect_right(int[] A, int x, int lo, int hi) {
    int N = A.length;
    if (N == 0) {
        return 0;
    }
    if (x < A[lo]) {
        return lo;
    }
    if (x > A[hi - 1]) {
        return hi;
    }
    for (;;) {
        if (lo + 1 == hi) {
            return lo + 1;
        }
        int mi = (hi + lo) / 2;
        if (x < A[mi]) {
            hi = mi;
        } else {
            lo = mi;
        }
    }
}

public static int bisect_left(int[] A, int x) {
    return bisect_left(A, x, 0, A.length);
}

public static int bisect_left(int[] A, int x, int lo, int hi) {
    int N = A.length;
    if (N == 0) {
        return 0;
    }
    if (x < A[lo]) {
        return lo;
    }
    if (x > A[hi - 1]) {
        return hi;
    }
    for (;;) {
        if (lo + 1 == hi) {
            return x == A[lo] ? lo : (lo + 1);
        }
        int mi = (hi + lo) / 2;
        if (x <= A[mi]) {
            hi = mi;
        } else {
            lo = mi;
        }
    }
}

经过测试(X 是我存储我打算重用的静态方法的类):

@Test
public void bisect_right() {
    System.out.println("bisect_rienter code hereght");
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, X.bisect_right(A, -1));
    assertEquals(1, X.bisect_right(A, 0));
    assertEquals(6, X.bisect_right(A, 2));
    assertEquals(8, X.bisect_right(A, 3));
    assertEquals(8, X.bisect_right(A, 4));
    assertEquals(9, X.bisect_right(A, 5));
    assertEquals(10, X.bisect_right(A, 6));
    assertEquals(10, X.bisect_right(A, 7));
}

@Test
public void bisect_left() {
    System.out.println("bisect_left");
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, X.bisect_left(A, -1));
    assertEquals(0, X.bisect_left(A, 0));
    assertEquals(2, X.bisect_left(A, 2));
    assertEquals(6, X.bisect_left(A, 3));
    assertEquals(8, X.bisect_left(A, 4));
    assertEquals(8, X.bisect_left(A, 5));
    assertEquals(9, X.bisect_left(A, 6));
    assertEquals(10, X.bisect_left(A, 7));
}

To this date (Java 8), this is still missing, so you must still make your own. Here's mine:

public static int bisect_right(int[] A, int x) {
    return bisect_right(A, x, 0, A.length);
}

public static int bisect_right(int[] A, int x, int lo, int hi) {
    int N = A.length;
    if (N == 0) {
        return 0;
    }
    if (x < A[lo]) {
        return lo;
    }
    if (x > A[hi - 1]) {
        return hi;
    }
    for (;;) {
        if (lo + 1 == hi) {
            return lo + 1;
        }
        int mi = (hi + lo) / 2;
        if (x < A[mi]) {
            hi = mi;
        } else {
            lo = mi;
        }
    }
}

public static int bisect_left(int[] A, int x) {
    return bisect_left(A, x, 0, A.length);
}

public static int bisect_left(int[] A, int x, int lo, int hi) {
    int N = A.length;
    if (N == 0) {
        return 0;
    }
    if (x < A[lo]) {
        return lo;
    }
    if (x > A[hi - 1]) {
        return hi;
    }
    for (;;) {
        if (lo + 1 == hi) {
            return x == A[lo] ? lo : (lo + 1);
        }
        int mi = (hi + lo) / 2;
        if (x <= A[mi]) {
            hi = mi;
        } else {
            lo = mi;
        }
    }
}

Tested with (X being the class where I store static methods that I intend to reuse):

@Test
public void bisect_right() {
    System.out.println("bisect_rienter code hereght");
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, X.bisect_right(A, -1));
    assertEquals(1, X.bisect_right(A, 0));
    assertEquals(6, X.bisect_right(A, 2));
    assertEquals(8, X.bisect_right(A, 3));
    assertEquals(8, X.bisect_right(A, 4));
    assertEquals(9, X.bisect_right(A, 5));
    assertEquals(10, X.bisect_right(A, 6));
    assertEquals(10, X.bisect_right(A, 7));
}

@Test
public void bisect_left() {
    System.out.println("bisect_left");
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, X.bisect_left(A, -1));
    assertEquals(0, X.bisect_left(A, 0));
    assertEquals(2, X.bisect_left(A, 2));
    assertEquals(6, X.bisect_left(A, 3));
    assertEquals(8, X.bisect_left(A, 4));
    assertEquals(8, X.bisect_left(A, 5));
    assertEquals(9, X.bisect_left(A, 6));
    assertEquals(10, X.bisect_left(A, 7));
}
_失温 2024-09-10 19:20:59

为了完整起见,这里有一个小 java 函数,它将 Arrays.binarySearch 的输出转换为接近 bisect_left 的输出。我显然错过了一些东西,但这足以解决简单的情况。

public static int bisectLeft(double[] a, double key) {
    int idx = Math.min(a.length, Math.abs(Arrays.binarySearch(a, key)));
    while (idx > 0 && a[idx - 1] >= key) idx--;
    return idx;
}

Just for completeness, here's a little java function that turns the output from Arrays.binarySearch into something close to the output from bisect_left. I'm obviously missing things, but this does the job for the simple case.

public static int bisectLeft(double[] a, double key) {
    int idx = Math.min(a.length, Math.abs(Arrays.binarySearch(a, key)));
    while (idx > 0 && a[idx - 1] >= key) idx--;
    return idx;
}
倚栏听风 2024-09-10 19:20:59

为什么不快速移植经过尝试和测试 Python 代码本身?例如,以下是 bisect_right 的 Java 端口:

public static int bisect_right(double[] A, double x) {
  return bisect_right(A, x, 0, A.length);
}

private static int bisect_right(double[] A, double x, int lo, int hi) {
  while (lo < hi) {
    int mid = (lo+hi)/2; 
    if (x < A[mid]) hi = mid; 
    else lo = mid+1;
  }
  return lo; 
}

Why not do a quick port of the tried and tested Python code itself? For example, here's a Java port for bisect_right:

public static int bisect_right(double[] A, double x) {
  return bisect_right(A, x, 0, A.length);
}

private static int bisect_right(double[] A, double x, int lo, int hi) {
  while (lo < hi) {
    int mid = (lo+hi)/2; 
    if (x < A[mid]) hi = mid; 
    else lo = mid+1;
  }
  return lo; 
}
看轻我的陪伴 2024-09-10 19:20:59

基于java.util.Arrays.binarySearch 文档

这里我使用 long[] 数组的示例,
但可以调整代码以利用任何受支持的类型。

int bisectRight(long[] arr, long key) {
    int index = Arrays.binarySearch(arr, key);
    return Math.abs(index + 1);
}

注意:java API 的限制,来自 javadoc 的以下句子:

If the array contains multiple elements with the specified value,
there is no guarantee which one will be found

事实上,我已经使用不同元素的排序数组对此进行了测试。
我的用例是范围分组,其中 arr 是一组不同的时间戳,指示间隔的开始时间。

Based on the java.util.Arrays.binarySearch documentation

Here I use the example for a long[] array,
but one can adapt the code to utilize any of the supported types.

int bisectRight(long[] arr, long key) {
    int index = Arrays.binarySearch(arr, key);
    return Math.abs(index + 1);
}

Note: Limitation on the java API, by the following sentence from javadoc:

If the array contains multiple elements with the specified value,
there is no guarantee which one will be found

Indeed, I've tested that with sorted array of distinct elements.
My use-case was for range grouping, where arr an array of distinct timestamps that indicate the start time of an interval.

哥,最终变帅啦 2024-09-10 19:20:59

你需要自己定义,这是我的:

bisect.bisect_left

public static int bisectLeft(int[] nums, int target) {
    int i = 0;
    int j = nums.length - 1;
    while (i <= j) {
        int m = i + (j-i) / 2;
        if (nums[m] >= target) {
            j = m - 1;
        } else {
            i = m + 1;
        }
    }
    return i;
}

bisect.bisect_right

public static int bisectRight(int[] nums, int target) {
    int i = 0;
    int j = nums.length - 1;
    while (i <= j) {
        int m = i + (j-i) / 2;
        if (nums[m] <= target) {
            i = m + 1;
        } else {
            j = m - 1;
        }
    }
    return j+1;
}

You need to define on your own, here's mine:

bisect.bisect_left

public static int bisectLeft(int[] nums, int target) {
    int i = 0;
    int j = nums.length - 1;
    while (i <= j) {
        int m = i + (j-i) / 2;
        if (nums[m] >= target) {
            j = m - 1;
        } else {
            i = m + 1;
        }
    }
    return i;
}

bisect.bisect_right

public static int bisectRight(int[] nums, int target) {
    int i = 0;
    int j = nums.length - 1;
    while (i <= j) {
        int m = i + (j-i) / 2;
        if (nums[m] <= target) {
            i = m + 1;
        } else {
            j = m - 1;
        }
    }
    return j+1;
}
坠似风落 2024-09-10 19:20:59

源自@Profiterole的答案,这里是一个通用变体,它使用 int->boolean 函数而不是数组。它找到谓词发生变化的第一个索引。

public class Bisect {

    /**
     * Look for the last index i in [min, max] such that f(i) is false.
     *
     * @param function monotonous function going from false to true in the [min, max] interval
     */
    public static int bisectLeft(Function<Integer, Boolean> function, int min, int max) {
        if (max == min) {
            return max;
        }
        if (function.apply(min)) {
            return min;
        }
        if (!function.apply(max)) {
            return max;
        }
        while (true) {
            if (min + 1 == max) {
                return min;
            }
            int middle = (max + min) / 2;
            if (function.apply(middle)) {
                max = middle;
            } else {
                min = middle;
            }
        }
    }

    /**
     * Look for the first index i in [min, max] such that f(i) is true.
     *
     * @param function monotonous function going from false to true in the [min, max] interval
     */
    public static int bisectRight(Function<Integer, Boolean> function, int min, int max) {
        if (max == min) {
            return max;
        }
        if (function.apply(min)) {
            return min;
        }
        if (!function.apply(max)) {
            return max;
        }
        while (true) {
            if (min + 1 == max) {
                return max;
            }
            int middle = (max + min) / 2;
            if (function.apply(middle)) {
                max = middle;
            } else {
                min = middle;
            }
        }
    }
}

例如,要查找数组中的插入点,该函数会将插入的值与数组的值进行比较:

@Test
public void bisect_right() {
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, bisectRight(f(A, -1), 0, A.length));
    assertEquals(1, bisectRight(f(A, 0), 0, A.length));
    assertEquals(6, bisectRight(f(A, 2), 0, A.length));
    assertEquals(8, bisectRight(f(A, 3), 0, A.length));
    assertEquals(8, bisectRight(f(A, 4), 0, A.length));
    assertEquals(9, bisectRight(f(A, 5), 0, A.length));
    assertEquals(10, bisectRight(f(A, 6), 0, A.length));
    assertEquals(10, bisectRight(f(A, 7), 0, A.length));
}

public Function<Integer, Boolean> f(int[] A, int x) {
    return n -> (n >= A.length || A[n] > x);
}

Derived from @Profiterole's answer, here is a generalized variant that works with an int->boolean function instead of an array. It finds the first index where the predicate changes.

public class Bisect {

    /**
     * Look for the last index i in [min, max] such that f(i) is false.
     *
     * @param function monotonous function going from false to true in the [min, max] interval
     */
    public static int bisectLeft(Function<Integer, Boolean> function, int min, int max) {
        if (max == min) {
            return max;
        }
        if (function.apply(min)) {
            return min;
        }
        if (!function.apply(max)) {
            return max;
        }
        while (true) {
            if (min + 1 == max) {
                return min;
            }
            int middle = (max + min) / 2;
            if (function.apply(middle)) {
                max = middle;
            } else {
                min = middle;
            }
        }
    }

    /**
     * Look for the first index i in [min, max] such that f(i) is true.
     *
     * @param function monotonous function going from false to true in the [min, max] interval
     */
    public static int bisectRight(Function<Integer, Boolean> function, int min, int max) {
        if (max == min) {
            return max;
        }
        if (function.apply(min)) {
            return min;
        }
        if (!function.apply(max)) {
            return max;
        }
        while (true) {
            if (min + 1 == max) {
                return max;
            }
            int middle = (max + min) / 2;
            if (function.apply(middle)) {
                max = middle;
            } else {
                min = middle;
            }
        }
    }
}

For example, to find the insertion point in an array, the function compares the value inserted with the values of the array:

@Test
public void bisect_right() {
    int[] A = new int[]{0, 1, 2, 2, 2, 2, 3, 3, 5, 6};
    assertEquals(0, bisectRight(f(A, -1), 0, A.length));
    assertEquals(1, bisectRight(f(A, 0), 0, A.length));
    assertEquals(6, bisectRight(f(A, 2), 0, A.length));
    assertEquals(8, bisectRight(f(A, 3), 0, A.length));
    assertEquals(8, bisectRight(f(A, 4), 0, A.length));
    assertEquals(9, bisectRight(f(A, 5), 0, A.length));
    assertEquals(10, bisectRight(f(A, 6), 0, A.length));
    assertEquals(10, bisectRight(f(A, 7), 0, A.length));
}

public Function<Integer, Boolean> f(int[] A, int x) {
    return n -> (n >= A.length || A[n] > x);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文