8

これはもともと私が仕事で遭遇した問題でしたが、今は自分の好奇心のために解決しようとしているだけです。

可能な限り最も効率的な方法で、int 'a' に int 'b' が含まれているかどうかを調べたいと思います。私はいくつかのコードを書きましたが、何を書いても、それを文字列に解析してから indexOf を使用すると、数学的に行うよりも 2 倍高速です。

メモリは (当然のことながら) 問題ではなく、単に処理速度が速いだけです。

これは私が数学的にそれを行うために書いたコードです:

private static int[] exponents = {10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };

private static boolean findMatch(int a, int b) {
    if (b > a) return false;

    if (a == b) return true;

    int needleLength = getLength(b);

    int exponent = exponents[needleLength];
    int subNum;
    while (a >= 1) {
        subNum = a % exponent;

        if (subNum == b)
            return true;

        a /= 10;
    }
    return false;
}

private static int getLength(int b) {

    int len = 0;

    while (b >= 1) {
        len++;
        b /= 10;
    }

    return len;
}

私が使用している文字列メソッドは次のとおりです。これは、上記の数学的方法よりも優れているようです。

private static boolean findStringMatch(int a, int b) {      
    return String.valueOf(a).indexOf(String.valueOf(b)) != -1;      
}

したがって、これは私の仕事を完了するために実際に必要なわけではありませんが、数学的にそれを行う方法をさらに最適化する方法、またはまったく新しいアプローチを誰かが考えられるかどうか疑問に思っていました. 繰り返しますが、メモリは問題ありません。私はただスピードを求めて撮影しています。

誰かがこれについて提供しなければならないものを見たり聞いたりすることに本当に興味があります.

編集: 含むと言うときは、どこにでもあることを意味します。たとえば、findMatch(1234, 23) == true

編集:このがらくたは読めず、不必要だと言っているすべての人のために:あなたは要点を逃しています。重要なのは、興味深い問題を掘り下げることであり、製品コードで使用するための答えを思いつくことではありません。

4

10 に答える 10

10

問題は数学ではなくテキストであるため、より高速な文字列の方法である必要があります「含む」関係は、数値については何も言っておらず、 10 進数表現についてのみ述べていることに注意してください。

また、書きたい関数が読めなくなることにも注意してください。他の開発者は、あなたが何をしているのか決して理解できません。(ここでどのような問題が発生したかを確認してください。) 一方、文字列バージョンは完全に明確です。

于 2008-10-23T23:50:42.957 に答える
4

これは Kibbee の方針に沿ったものですが、彼が投稿してこれを解決する前に、私はこれに少し興味をそそられました。

long mask ( long n ) { 
    long m   = n % 10;
    long n_d = n;
    long div = 10;
    int  shl = 0;
    while ( n_d >= 10 ) { 
        n_d /= 10;
        long t = n_d % 10;
        m |= ( t << ( shl += 4 ));
    }
    return m;
}

boolean findMatch( int a, int b ) { 
    if ( b < a  ) return false;
    if ( a == b ) return true;

    long m_a = mask( a );    // set up mask O(n)
    long m_b = mask( b );    // set up mask O(m)

    while ( m_a < m_b ) {
        if (( m_a & m_b ) == m_a ) return true;
        m_a <<= 4;  // shift - fast!
        if ( m_a == m_b ) return true;
    }  // O(p)
    return false;
}       

void testContains( int a, int b ) { 
    print( "findMatch( " + a + ", " + b + " )=" + findMatch( a, b ));
}

testContains( 12, 120 );
testContains( 12, 125 );
testContains( 123, 551241238 );
testContains( 131, 1214124 );
testContains( 131, 1314124 );

300 文字では議論するには少なすぎるため、このメインの投稿を Pyrolistical に対応するように編集しています。

OP とは異なり、ネイティブにコンパイルされた indexOf がプリミティブを使用した Java コードよりも高速であることに、それほど驚きはありませんでした。したがって、私の目標は、Java コード全体で何億回も呼び出されるネイティブ メソッドよりも高速だと思われるものを見つけることではありませんでした。

OPは、これが生産上の問題ではなく、怠惰な好奇心の線に沿ったものであることを明らかにしたので、私の答えはその好奇心を解決します. 私の推測では、彼が本番環境で解決しようとしたときは速度が問題だったのですが、怠惰な好奇心として、「このメソッドは何百万回も呼び出されるでしょう」はもはや当てはまりません。彼が 1 人の投稿者に説明しなければならなかったように、それはもはや製品コードとして追求されていないため、複雑さはもはや問題ではありません。

さらに、「551241238」で「123」を見つけることができるページ上の唯一の実装を提供するため、正確さが無関係な懸念でない限り、それを提供します。また、「Java プリミティブを使用して数学的に問題を解決するが、最適化されたネイティブ コードに勝るアルゴリズム」のソリューション スペースはEMPTYになる可能性があります。

さらに、リンゴとリンゴを比較したかどうかは、あなたのコメントからは明らかではありません。機能仕様は f( int, int )-> boolean であり、 f( String, String )-> boolean ( のドメインのようなもの) ではありませんindexOf。したがって、このようなものをテストしない限り (これは私のものよりも優れている可能性があり、私はそれほど驚くことはありません)、追加のオーバーヘッドがその余分な 40% の一部を食い尽くす可能性があります。

boolean findMatch( int a, int b ) { 
    String s_a = "" + a;
    String s_b = "" + b;
    return s_a.indexOf( s_b ) > -1;
}

同じ基本的な手順を実行します。log 10 (a) エンコーディング + log 10 (b) エンコーディング + 実際に一致を見つけます。これも O( n ) であり、nは最大の対数です。

于 2008-10-24T05:28:00.093 に答える
3

私が考えることができる唯一の最適化は、自分で文字列への変換を行い、変換を行うときに数字を (右から左へ) 比較することです。最初に b のすべての桁を変換してから、b の最初の桁 (右から) で一致が見つかるまで、a の右から変換します。b のすべてが一致するか、不一致になるまで比較します。不一致にヒットした場合は、b の最初の桁の一致を開始するポイントに戻り、a に進み、最初からやり直します。

IndexOf は、左側を除いて、基本的に同じバック トラッキング アルゴリズムを実行する必要があります。実際の数値によっては、これはより高速になる場合があります。数字がランダムであれば、すべてを変換する必要がない場合が多いはずなので、そうあるべきだと思います。

于 2008-10-23T23:36:37.180 に答える
2

これは興味深い問題です。String.classの関数の多くは実際にはネイティブであるため、Stringを打ち負かすことは難しい提案です。しかし、ここにいくつかのヘルパーがあります:

ヒント1:単純な整数演算が異なれば、速度も異なります。

サンプルプログラムでの迅速な計算により、次のことが示されました。

% ~ T
* ~ 4T
/ ~ 7T

したがって、乗算またはモジュロを優先して、除算をできるだけ少なくする必要があります。減算、加算、および比較演算子は、これらすべてを水から吹き飛ばす原因となるものは示されていません。また、可能な限り「final」を使用すると、JVMで特定の最適化を実行できます。「getLength」関数の高速化:

private static int getLength(final int b) {        
   int len = 0;
   while (b > exponents[len]) {
       len++;
   }
   return len + 1
}

これにより、機能が約7倍向上します。b>指数の最大値の場合、indexOutOfBounds例外が発生します。それを解決するために、あなたは持つことができます:

private static int getLength(final int b) {        
   int len = 0;
   final int maxLen = exponents.length;
   while (len < maxLen && b > exponents[len]) {
       len++;
   }
   return len + 1;
}

これは少し遅く、bが大きすぎると長さが正しくなくなりますが、例外はスローされません。

ヒント2:不要なオブジェクト/プリミティブの作成とメソッド呼び出しが実行時間に追加されます。

「getLength」は他の場所では呼び出されないと推測しているので、別の関数があると便利かもしれませんが、最適化の観点からは、不要なメソッド呼び出しとオブジェクト「len」の作成が必要です。そのコードは、使用する場所に正しく配置できます。

private static boolean findMatch(int a, final int b) {
        if (b > a) return false;
        if (a == b) return true;
        int needleLength = 0;
        while (b > exponents[len]) {
            needleLength ++;
        }
        needleLength++;

        final int exponent = exponents[needleLength];
        int subNum;
        while (a >= 1 && a <= b) {
                subNum = a % exponent;
                if (subNum == b)
                        return true;
                a /= 10;
        }
        return false;
}

また、下部のwhileループを変更して、「a<=b」も含めるようにしたことに注意してください。私はそれをテストしておらず、反復ごとのペナルティが反復を無駄にしないという事実を上回っているかどうかはわかりません。賢い数学を使って除算を取り除く方法があると確信していますが、今は考えられません。

于 2008-10-24T03:23:16.040 に答える
2

あなたの関数は実際にはかなりうまくいっているように見えますが、小さな改善があります:

private static boolean findMatch(int a, int b) {
        if (b > a) return false;

        if (a == b) return true;

        int needleLength = getLength(b);

        int exponent = exponents[needleLength];
        int subNum;
        while (a > b) {
                subNum = a % exponent;

                if (subNum == b)
                        return true;

                a /= 10;
        }
        return false;
}

aがbよりも小さいというのは価値がないからといって、探し続けるのではありませんか?頑張って、解決策を見つけたら投稿してください!

于 2008-10-23T23:59:16.667 に答える
0

コードのどこでこの関数を使用しているのか聞いてもいいですか?たぶん、それが現在解決している問題を解決する別の方法があり、それははるかに速いでしょう。これは、友人からギターを完全に再調整するように頼まれたときのようなもので、下の弦を1ステップ下げるだけで同等の結果が得られることに気付く前に、それを行いました。

于 2008-10-24T08:30:52.313 に答える
0

これをバイナリで計算する方法はありますか?明らかに、別の文字のバイナリ整数を含む整数のバイナリ値は、10 進法が同じことを意味するわけではありません。ただし、使用できるバイナリのトリックはありますか? 12345 のような数値を 0001 0010 0011 0100 0101 に変換し、ビット シフトを行って 23 (0010 0011) がそこに含まれているかどうかを調べます。文字セットは 10 文字しかないため、2 文字の値を 1 バイトに格納することで計算時間を短縮できます。

編集

このアイデアを少し拡張します。A と B の 2 つの整数があり、A に B が含まれているかどうかを知りたい場合は、最初に 2 つのことを確認します。A が B より小さい場合、A は B を含むことができません。A = B の場合、A は B を含みます。この時点で、文字列に変換できます*。A が B と同じ数の文字数を含む場合、それらが等しい場合を除き、A は B を含みませんが、それらが等しい場合はここにいないため、両方の文字列が同じ長さである場合、a は b を含みません。 . この時点で、A の長さは B よりも長くなります。したがって、この投稿の最初の部分で述べたように、文字列をパック バイナリ値に変換できます。これらの値を整数の配列に格納します。次に、配列内の整数値のビットごとの AND を実行し、結果が A の場合、A には B が含まれます。次に、B の整数の配列を左 4 ビットにシフトします。もう一度比較を行います。B の左側からビットをポップし始めるまで、これを行います。

*前の段落の * は、このステップをスキップできる可能性があることを意味します。文字列をまったく使用せずにこれを行う方法があるかもしれません。最初の段落で説明したパックされたバイナリ表現を取得するために実行できる、凝ったバイナリ トリックがいくつかあるかもしれません。使用できる 2 進数のトリックや、前に説明した整数を 10 進数に変換する簡単な計算が必要です。

于 2008-10-23T23:55:56.893 に答える
0

うーん、質問を完全に誤解しているかもしれませんが……。

// Check if A is inside B lol
bool Contains (int a, int b)
{
    return (a <= b);
}

特定の数列が別の数列内にあるかどうかを知りたい場合を除きます。

その場合、それを文字列に変換する方が、計算して計算するよりも高速です。

于 2008-10-23T23:24:18.983 に答える
0

これは決してあなたの質問に答えるものではありませんが、とにかくアドバイスです:-)

メソッド名findMatchはあまり説明的ではありません。この場合、メソッドContainerBuilder.number(int)を含む を返す static method がありContainerBuilderますcontains。このようにして、コードは次のようになります。

boolean b = number(12345).contains(234);

長い目で見れば、いくつかのアドバイスがあります!

そうそう、「含む」の意味を定義する必要があるとも言いたかった

于 2008-10-23T23:28:54.560 に答える
-1

ご参考までに

http://refactormycode.com/

あなたのために働くことができます。

于 2008-10-24T00:00:59.957 に答える