1. ホーム
  2. java

[解決済み] Javaで整数のlog base 2を計算する方法は?

2022-05-02 16:31:42

質問

以下の関数を使って、整数のlog base 2を計算しています。

public static int log2(int n){
    if(n <= 0) throw new IllegalArgumentException();
    return 31 - Integer.numberOfLeadingZeros(n);
}

最適なパフォーマンスを発揮するか?

そのためのJ2SE API関数をご存知の方はいらっしゃいませんか?

UPD1 意外なことに、浮動小数点演算は整数演算より速いようです。

UPD2 ご指摘を受け、より詳細な調査を行うことになりました。

UPD3 私の整数演算関数は Math.log(n)/Math.log(2) より10倍高速です。

どのように解決するのですか?

整数演算の補助に浮動小数点を使おうと考えている人は、注意が必要です。

私は普段、可能な限りFPの計算をしないようにしています。

浮動小数点演算は厳密ではありません。何が起こるかを確実に知ることはできません。 (int)(Math.log(65536)/Math.log(2)) と評価されます。 例えば Math.ceil(Math.log(1<<29) / Math.log(2)) は私のPCでは30ですが、数学的には正確に29になるはずです。xの値で (int)(Math.log(x)/Math.log(2)) は失敗しますが(32個の "dangerous" 値しかないため)、どのPCでも同じように動作するということではありません。

ここでの通常のトリックは、丸めるときに "epsilon" を使うことです。例えば (int)(Math.log(x)/Math.log(2)+1e-10) は失敗しないはずです。この "epsilon" を選択するのは些細な作業ではありません。

より一般的なタスクを使って、より多くのデモンストレーションを行います。 int log(int x, int base) :

テスト用のコードです。

static int pow(int base, int power) {
    int result = 1;
    for (int i = 0; i < power; i++)
        result *= base;
    return result;
}

private static void test(int base, int pow) {
    int x = pow(base, pow);
    if (pow != log(x, base))
        System.out.println(String.format("error at %d^%d", base, pow));
    if(pow!=0 && (pow-1) != log(x-1, base))
        System.out.println(String.format("error at %d^%d-1", base, pow));
}

public static void main(String[] args) {
    for (int base = 2; base < 500; base++) {
        int maxPow = (int) (Math.log(Integer.MAX_VALUE) / Math.log(base));
        for (int pow = 0; pow <= maxPow; pow++) {
            test(base, pow);
        }
    }
}

対数の最も素直な実装を用いれば

static int log(int x, int base)
{
    return (int) (Math.log(x) / Math.log(base));
}

が表示されます。

error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...

エラーを完全になくすには、1e-11と1e-14の間のイプシロンを追加する必要がありました。 テストする前にこのことを伝えることができたでしょうか? 間違いなく無理でした。