簡體   English   中英

Java相當於python中的bisect

[英]Java's equivalent to bisect in python

Python 的bisect 模塊在 Java 中是否有等價物? 使用 Python 的二分法,您可以按方向對數組進行二分法。 例如bisect.bisect_left做:

為列表中的項目找到正確的插入點以維護排序順序。 參數 lo 和 hi 可用於指定應考慮的列表子集; 默認情況下使用整個列表。

我知道我也可以通過二進制搜索手動執行此操作,但我想知道是否已經有一個庫或集合在執行此操作。

您有兩種選擇:

到目前為止(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));
}

為了完整性,這里有一個小函數, Arrays.binarySearch的輸出Arrays.binarySearch為接近bisect_left輸出的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;
}

基於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一組不同的時間戳,指示間隔的開始時間。

為什么不快速端口經過試驗和測試的 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; 
}

你需要自己定義,這是我的:

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;
}

源自@Profiterole 的回答,這是一個通用變體,它使用 int->boolean function 而不是數組。 它找到謂詞更改的第一個索引。

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;
            }
        }
    }
}

例如,要找到數組中的插入點,function 會將插入的值與數組的值進行比較:

@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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM