プログラマーは学ぶ必要があります:高速電力アルゴリズム

先ほど、Bステーションのアルゴリズムチュートリアルにメッセージを

残した友達がいました質問がある場合や、何か説明したい場合は、個人のBステーションまたはパブリックアカウント(xmg_mj)にメッセージ残すことができます。最善を尽くします。能力、時間をかけて記事を書き、みんなに返信する動画を録画してみてください。

高速パワーについて

実際、高速パワーに関連する問題は、アルゴリズム競争(NOI、ACMなど)に参加する小さなパートナーが習得しなければならない小さな基本的なコンテンツです。もちろん、アルゴリズムのコンテストに参加する予定がない場合でも、個人的にはプログラマーである限り高速パワーアルゴリズムを習得する必要があると個人的に感じています。

『The Art of Computer Programming』では高速パワーアルゴリズムについて説明されていますが、この本の英語名はThe Art of Computer Programming(略してTAOCP)です。

TAOCPはDonald Ervin Knuthのから生まれましたシニアクヌースは、コンピューター分野で多くの成果を上げてきた有名な科学者です。彼は有名なKMPアルゴリズムの発明者の1人です。1974年に彼は「コンピューター分野のノーベル賞」:チューリング賞を受賞しました(彼は36歳でした)。現在、TAOCPは第1巻、第2巻、第3巻、および第4巻を発行しており、計画によれば、第4巻、第5巻、第6巻、および第7巻は出版されていません。初版は1968年に初刊され、今年はシニアクヌースが82歳で、105歳までに傑作を完成させる予定と言われています。

TAOCPについて、マイクロソフトの創設者であるビルゲイツ氏はかつて

あなたが本当に優れたプログラマだと思うなら...(Knuthの)Art of Computer Programmingを読んでください...すべてを読むことができたら、ぜひ履歴書を送ってください。

おそらく意味:自分が非常に優れたプログラマだと思うなら、KnuthのTNUCPを読むべきです;あなたがすべてを読むことができれば、私に直接履歴書を送ることができます。それはシニアクヌースの言葉がより鋭いと言われています:理解していない場合はプログラマーにならないでください!しかし、TAOCPは初心者には読みにくいため、この本の例はすべてKnuth独自のMIXアセンブリ言語を使用しています。

この記事の前にリマインダーを読んでください

時間をかけて今日、古典的な高速パワーアルゴリズムを説明する記事を書いてください。ただし、この記事を完全に理解するには、いくつかの前提条件があります。

  • アルゴリズムの2つの基本的な概念に精通している:時間の複雑さ、空間の複雑さ
    • これら2つの概念をまったく聞いていない場合は、アルゴリズムベースが完全に 0であり、冗談ではありません。
    • 複雑さをパブリックアカウントに送信して、関連するチュートリアル取得できます
  • 2進および10進変換に慣れている
    • これに慣れていない場合は、プログラミングの基礎を実際に満たす必要があります。
    • 16進数をパブリックアカウントに送信して、関連するチュートリアル取得できます
  • 一般的なビット操作に慣れている
    • n&1の結果は、nの最下位バイナリビットの値であり、nのパリティを判断するためにも使用できます。
    • 正の整数n / 2を検索し、ビット演算で置き換えることができます:n >> 1
    • 上記の操作の原理がわからない場合は、ビット操作をパブリックアカウントに送信して、関連するチュートリアル取得できます。

パワーとは?

ご存じのように、xのn乗は、n xsの乗算であるxのn乗を指します。たとえば、2の累乗は2 * 2 * 2 * 2です。

説明を簡略化するために、xの後ろのnのべき乗はx ^ nに簡略化されます(この記事の^はビットごとのXORを意味するものではありません)

それでは、プログラミングを通じて力を求める方法は?xとnだけが整数であり、nが0以上であると仮定すると、最も簡単な考え方は次のとおりです(ここで使用されているプログラミング言語はJavaですが、Javaの特別な構文は含まれていません。したがって、Javaを使用したことがない場合でも、理解できる)

int power(int x, int n) {
    int result = 1;
    while (n-- > 0) {
        result *= x;
    }
    return result;
}

明らかに、この方法の時間の複雑さはO(n)であり、空間の複雑さはO(1)です。

高速パワーとは何ですか?

いわゆる高速電力とは、より効率的な(時間の複雑さが低い)方法を使用して電力を見つけることであり、時間の複雑さはO(logn)に最適化できます2つの解決策があります:再帰的、非再帰的

再帰的

上図の式によれば、以下のコードを書くことは難しくありません

int fastPower(int x, int n) {
    if (n == 0) return 1;
    int result = fastPower(x, n >> 1);
    result *= result;
    return (n & 1) == 0 ? result : result * x;
}

この方法の時間と空間の複雑さはどちらもO(logn)です。

この方法の複雑さを分析するには?

アルゴリズムが弱い場合は、大まかな分析の代わりに特定の値を使用できます。たとえば、nが16の場合、メソッドの再帰呼び出しプロセスを次の図に示します。

呼び出されるたびにnのサイズが半分になることを確認することは難しくないため、時間とスペースの複雑さはどちらもO(logn)です。

アルゴリズムが優れている場合、より専門的な方法を使用してその複雑さを分析できます(特定のアルゴリズムの基礎がないと、理解できない場合があります)。

  • これは実際には分割統治戦略を適用するための典型的なアルゴリズムです
  • データサイズがnの場合、T(n)が時間の複雑さであると仮定すると、再帰式を導出することは難しくありません。T(n)= T(n / 2)+ O(1)
  • 最後に、再帰+ マスター定理(マスター定理)に従って、T(n)= O(logn)という結論を直接引き出すことができます。

非再帰的

3 ^ 21を例にして、非再帰的なコードを記述する方法を分析してみましょう。

まず、21のバイナリ形式は10101です。

次の結論を出すことは難しくありません

  • 3 ^ n(nは2、4、8、16)は3 ^ 1で乗算できます
  • 各3 ^ nには対応するバイナリビットがあります
    • 3 ^ 1は1のバイナリビット値に対応し、これは実際に10101バイナリの最後のビットです
    • 3 ^ 2は0のバイナリ値に対応します。これは実際に1010バイナリの最後のビットです
    • 3 ^ 4は1のバイナリ値に対応し、これは実際にバイナリ101の最後のビットです
    • 3 ^ 8は0のバイナリ値に対応します。これは実際にバイナリ10の最後のビットです
    • 3 ^ 16は、実際にバイナリ1の最後のビットである1のバイナリ値に対応します。
  • 3 ^ nが0のバイナリ値に対応する場合、最終結果を乗算する必要はありません
    • たとえば、3 ^(8 * 0)、3 ^(2 * 0
    • 最終値が3 ^ 0、つまり1であるため
  • 3 ^ nがバイナリ値1対応する場合、それを乗算して最終結果にする必要があります
    • たとえば、3 ^(16 * 1)、3 ^(4 * 1)、3 ^(1 * 1

したがって、上記の結論に基づいて、次の問題解決手順を要約できます。

  • 3 ^ 1を使用して、3 ^ nを累積し続けます(nは2、4、8、16です)
  • 3 ^ nが累積されるたびに、対応するバイナリ値が1または0であるかどうかをチェックして、最終結果に乗算するかどうかを決定します
int fastPower(int x, int n) {
    int result = 1;
    while (n != 0) {
        if ((n & 1) == 1) {
            result *= x;
        }
        x *= x;
        n >>= 1;
    }
    return result;
}

3と21を置き換えると、fastPower(3、21)の実行フローは次のようになります。

ラウンド1のwhileループ

  • 行4コード
    • nの2進数は10101(10進数は21)
    • x = 3 ^ 1、対応するバイナリビットの値は1(nの最後のバイナリビット)
    • したがって、5行目のコード実行する必要があります。最終結果にxを乗算します。
    • 結果= 3 ^ 1
  • 7行目のコード
    • x =(3 ^ 1)*(3 ^ 1)= 3 ^ 2
  • 行8コード
    • nは1ビット右にシフトされ、その2進数は1010になります(対応する10進法は何ですか?重要ではありません!!!)

ラウンド2 whileループ

  • 行4コード
    • nのバイナリは1010です
    • x = 3 ^ 2、対応するバイナリビットの値は0(nの最後のバイナリビット)
    • したがって、コードの5行目実行する必要はありません。最終結果にxを乗算する必要はありません。
    • 結果= 3 ^ 1
  • 7行目のコード
    • x =(3 ^ 2)*(3 ^ 2)= 3 ^ 4
  • 行8コード
    • nは1ビット右にシフトされ、その2進数は101になります(対応する10進法は何ですか?重要ではありません!!!)

ラウンド3 whileループ

  • 行4コード
    • nのバイナリは101です
    • x = 3 ^ 4、対応するバイナリビットの値は1(nの最後のバイナリビット)
    • したがって、5行目のコード実行する必要があります。最終結果にxを乗算します。
    • 結果=(3 ^ 1)*(3 ^ 4)
  • 7行目のコード
    • x =(3 ^ 4)*(3 ^ 4)=(3 ^ 8)
  • 行8コード
    • nは1ビット右にシフトされ、その2進数は10になります(対応する10進法は何ですか?重要ではありません!!!)

ラウンド4 whileループ

  • 行4コード
    • nのバイナリは10です
    • x = 3 ^ 8、対応するバイナリビットの値は0(nの最後のバイナリビット)
    • したがって、コードの5行目実行する必要はありません。最終結果にxを乗算する必要はありません。
    • 結果=(3 ^ 1)*(3 ^ 4)
  • 7行目のコード
    • x =(3 ^ 8)*(3 ^ 8)= 3 ^ 16
  • 行8コード
    • nが1ビット右にシフトされ、その2進数が1になります(対応する10進法は何ですか?重要ではありません!!!)

ループ5のwhileループ

  • 行4コード
    • nのバイナリは1です
    • x = 3 ^ 16、対応するバイナリビットの値は1(nの最後のバイナリビット)
    • したがって、5行目のコード実行する必要があります。最終結果にxを乗算します。
    • 結果=(3 ^ 1)*(3 ^ 4)*(3 ^ 16)
  • 7行目のコード
    • x =(3 ^ 16)*(3 ^ 16)= 3 ^ 32
  • 行8コード
    • nを1ビット右にシフトすると、そのバイナリは0になります

最後

  • n = 0なので、whileループを終了します
  • 最終結果=(3 ^ 1)*(3 ^ 4)*(3 ^ 16)
  • 複雑さの分析
    • whileループの本体が実行されるたびに、n >> = 1はnの値を半分にします。
    • したがって、時間の複雑さ:O(logn)、空間の複雑さ:O(1)

リートコード

質問50. Leetcode Pow(x、n)今日説明した高速パワーアルゴリズムを使用してください。以下は私のコード実装です

// 递归
public double myPow(double x, int n) {
    if (n == 0) return 1;
    if (n == -1) return 1 / x;
    double half = myPow(x, n >> 1);
    half *= half;
    return ((n & 1) == 1) ? half * x : half;
}

// 非递归
public double myPow(double x, int n) {
    long y = (n < 0) ? -((long) n) : n;
    double result = 1.0;
    while (y > 0) {
        if ((y & 1) == 1) {
            result *= x;
        }
        x *= x;
        y >>= 1;
    }
    return (n < 0) ? (1 / result) : result;
}

思い出す必要があるのは

  • ここで使用するプログラミング言語はJavaですが、おなじみのプログラミング言語に応じて文法の詳細を調整できます。
  • Leetcodeのnは負の数になる可能性があるため、上記のコードは負の数の場合に処理を行います

より高速な電力関連の質問

時間は限られています。この記事では、これについて最初に説明します。友達に電源関連の簡単な質問を2つ残してください。時間があれば、勉強に行くことができます。

  • 行列を使用してフィボナッチ数列をすばやく指数化する
  • xのy乗係数zの結果を見つけるアルゴリズムを設計してください:(x ^ y)%z
    • xとyの両方が大きな整数であると仮定します(yは0以上、zは0以外)

特に私に何か書いてもらいたい場合は、提案を残すこともできます。ありがとうございます。ようこそ注意

おすすめ

転載: www.cnblogs.com/mjios/p/12690097.html