最近、こっそりLeetCodeの簡単な質問を維持するために毎日ブラシの簡単な質問が簡単にACを見つけるのに、感じるが、その解決策のすべてを理解するために、だけでなく、知識の幅を広げ、新たな知識の多くを学ぶことがあれば。
この記事では、創造の考え方から来ている:LeetCode問題69. xの平方根
説明タイトル効果(ここで見ることができ、リンクをジャンプする必要はありません):非負の整数を考えると、X、およびリターンは、コンピューティング要件は、xは(丸い)の平方根を。例えば、4入力、出力2、8入力、出力2(2.82842平方根...... 8は、戻り型が整数であるため、小数部分は廃棄されます)。すなわち、所与の\(X \) 、我々は計算する必要が(\ \ lfloor \ SQRT {X} \ rfloor)を\。
当然よりその二乗値以上の最初まで、0から横断する最も簡単で直感的な方法\(Xは\)の数である\(N- \) 、次いで\(N-1 \)の答えです。いずれかのための\(X \) 、平方根は丸めである必要があり、その後([0、X] \ \ ) 間隔で、次の通り:
int sqrt(int x)
{
if (x == 0)
return x;
int ans = 1;
while (ans <= x / ans)
ans++;
return ans - 1;
}
ここで注意すべき点が2つあります。
- コードライン6、
while
オーバーフローを回避するために決意条件。大型の確率は、次のように書くかもしれないwhile (ans * ans <= x)
、より自然な、より直感的、しかしであるans
、時間値の多くはans * ans
結果が超えることができるint
最大の範囲表現のタイプを。例えば、我々は、計算する必要が\(Xを\)丸めの平方根である(ある\(N- \) 、すなわち、\(\ lfloor \ SQRT {X} \ = N-rfloor \) )、アルゴリズムはだろうans
に横切ります最初の正方形を超える\(X \)の値、すなわち、\は、(N + 1 \)停止しました。場合\(X \)の値であるint
ときに、タイプを表すことができる最大値ans
横断が(N + 1 \)\、計算ans * ans
超え結果int
タイプの範囲を示しました。 - 以来
while
判定のサイクル、我々は代わりに、乗算の分割を使用し、したがってans
もはや0からトラバースすることができない(そうでなければ、ゼロによる除算につながります)。この目的のために、我々はアルゴリズムの開始時に一人で処理することができます\(X = 0 \)の状況、その後、聞かせてans
1つのトラバーサルから始まります。
簡単な質問のように、このような暴力はACの自然な単純なアルゴリズムです。その低い効率(必要横断する\(O(\ SQRT {N })\) 回)90msのより多くのC ++言語ランタイム平均を使用して、唯一の約5%速いユーザよりLeetCodeで時間効率、。バイナリ探索法やニュートン法:したがって、この記事では、2つの効率的なアルゴリズムを提供します。
1.バイナリ検索
あなたは素晴らしい可能性を解決するために基づいて暴力について考え続ける場合は、バイナリ検索によって解決思っているだろう。
はい、暴力を解決するための戦略を考えて、私たちは、インターバル中にある\([0、X] \ ) ソリューションの検索ではなく、ドメインを検索する\([0、x]の\は ) 自然に発注され、自然にあなたはバイナリ検索の代わりにリニアを使用することができます大幅に検索効率を向上させるために検索します。
さらに、私たちは、検索範囲を絞り込むことができます。直感は非負の整数のために、米国のことを告げる\(X \) 、\(\ SQRT {X} \ ) を超えるべきではない(X / 2 \)\(例えば、\(\ = SQRT {25}。5 \)、未満(25/2 = 12.5 \ \) )。私たちは、それを証明することができます。
\ [\ {整列}開始&\テキスト{セット}、Y = \ FRAC {X} {2} - \ SQRT {X}、\テキスト{}、Y ^ \プライム= \ FRAC {1} {2} - \ FRAC {1} {2 \ SQRT {X}}、\\ [2EX]&\テキスト{そう} y ^ \プライム= 0、\テキスト{利用できる停滞}、X = 1、\\ [2EX]&\テキスト{もし} X> 1 \ {}テキスト、Y ^ \プライム> 0、\テキスト{即ち} X> 1 \ {}テキスト、Y = \ FRAC {X} {2} - \ SQRT {X } \テキスト{単調に増加する値}、\\ [2EX]&\テキストが{起動されるように、X}> 1 \テキスト{}、\ lfloor \ FRAC {X} {2} \ rfloor - \ lfloor \ SQRT {X} \ rfloor \テキスト{単調に増加する値}、\\ [2EX]&\テキスト{と理由}、X = 2 \ {}テキスト、\ lfloor \ FRAC {X} {2} \ rfloor - \ lfloor \ SQRT {X} \ rfloor = 0、\\ [2EX]&\テキスト{そう} X \ GEQ 2 \ {}テキスト、\ lfloor \ FRAC {X} {2} \ rfloor - \ lfloor \ SQRT {X} \ rfloor \ GEQ 0、\テキスト{即ち} X \ GEQ 2 \ {場合}テキスト、\ lfloor \ FRAC {X} {2} \ rfloor \ GEQ \ lfloor \ SQRT {X} \ rfloor&\テキスト{(QED)} \端{整列} \]
要求場合証明することによって、我々は、それを見ることができる\(X \)は以上\(2 \) 、sqrt(x)
のための探索空間\([1、X / 2] \)のために、(X <2 \)\ケース、我々は、特別な治療に(ここでは結論できる:とき\(X \ GEQ 1 \)場合、\(\ lfloor \ X FRAC} {2} {\ + rfloor 1 \ GEQ \ lfloor \ SQRT {X。 } \ rfloor \) 、次いで、別々に処理されたX <1 \)(\)の場合。コード:
int sqrt(int x)
{
if (x < 2) // 处理特殊情况
return x;
int left = 1, right = x / 2;
while (left <= right) {
# 避免溢出,相当于 mid = (left + right) / 2
int mid = left + ((right - left) >> 1);
if (mid == x / mid)
return mid;
else if (mid > x / mid)
right = mid - 1;
else
left = mid + 1;
}
return right;
}
ここでは、最後の戻り値理由を説明しますright
。バイナリサーチのために、それはサーチスペースに縮小していきleft == right
、この場合には、(自身が手動でアナログができ、はるかにここで繰り返され、二分探索に)mid
、left
そしてright
値が3に等しいです(mid = (left + right) / 2
)。後退既知のバイナリ検索の検索範囲の条件、left
(又はmid
)、左側に小さい(\ \ rfloor \ lfloor \ SQRT {X})\、 right
(またはmid
より右の値より大きい)\ lfloor \ SQRT(\ X} {\ rfloor \) 、この時刻(AT while
最後のサイクル)を決定するmid
の正方形x
サイズを、3つのケースがあります。
mid == x / mid
。直接循環戻りにおけるmid
値。mid > x / mid
。この場合、以降mid
左辺の値が小さい(\ lfloor \ SQRT {X} \ rfloor \)\、およびmid
値がより大きい(X \)\、次いで、mid-1
それが答えです。状況に応じて支店、実行はright = mid - 1
、我々が見ることができるright
どのような値の値が返されるべきです。この場合、サイクルの終わりには、返されるべきですright
。mid <= x / mid
。この場合mid
、left
及びright
それが計算される答え(より右の値より大きい(\ \ lfloor \ SQRT {X} \ rfloor \) )。分岐条件、実行left = mid + 1
、終了サイクル。このとき、mid
及びright
計算結果の値。
統合は、上記3つの場合には、見ることができるwhile
サイクルが完了すると、right
記憶された値は、結果を計算しなければなりません。
そして暴力の前に解決を見つけるためにバイナリ思考を用いて比較sqrt(x)
を介してのみループを\(O(\ LG {\ FRAC {X} {2}})\) 倍、複雑な空間程度\(O(1)\ )。
2.ニュートン - ラプソン反復法
ニュートン-歌の代わりにストレートな思考にメソッドを使用して(ニュートン法と呼ばれる)ラプソン反復法では、だけでなく、平方根計算を解くために、解決の関数です。もちろん、そこに多くのピットがありますが、進化を解決するため、ニュートン法が安全である機能を解決するためにニュートン法を使用して。:この方法では、あなたは、リンクを参照して、詳細を知りたい、高い数学のいくつかの知識を持っている必要がありますどのように説明するために、プレーン、ニュートン反復法では、進化のために?数値解析?- MAの学生の答え
理解するためのシンプルな、あなたは画像を参照することができます。
任意の負でない整数を与えられた(N- \)\、我々が知りたい\(X = \ lfloor \ N-SQRT {} \ rfloorを\) 、我々が計算する関数に相当する\(F(X)= X ^ 2 - N \)ルート。まず、我々は推測与える必要が\(X_0を\)なるかもしれない、\(X_0 = \ FRAC {X} + {2}。1 \)、その後、(最初の測定証明を参照)(F(X_0)\ \ )の正接の関数として正接(X \)\反復後の値である交差軸\(X_1 \) 。場合\(X_1 \)結果が得られない場合、反復がで、継続される\(F(X_1)\)タンジェントの関数として接線と\(X \)軸の交点、すなわち第2の反復の後に、値\(X_2 \) 。そしてように、まで(x_nに関する= \ lfloor \ N-SQRT {} \ rfloor \)\。
今、私たちは、反復を導き出します。ため\(X_I \)関数の値である、\(F(X_I)\)次に、ポイントの\((X_I、F(X_I))\) 、接線の式を貸与することができます。
\ [\ {ALIGN}&Y開始 - F(X_I)= F(X_I)^ \プライム(X - X_I)\\ [2EX] \が意味&Y - (X_I ^ 2 - N)= 2x_i(X - X_I)を\ \ [2EX] \意味&Y + X_I ^ 2 + N = 2x_ix \端{ALIGN} \]
そしてため(X_ {I + 1} \ \) の接線である(X \)\それように、軸の交点\(Y = 0 \)は:、得ることができます
\ [X_ {I + 1} =(X_I + N / X_I)/ 2 \]
今、私たちは、反復に基づいてコードを書くことができます。
int sqrt(int x)
{
// 避免除零错误,单独处理 x = 0 的情况
if (x == 0)
return x;
int t = x / 2 + 1;
while (t > x / t)
t = (t + x / t) / 2;
return t;
}
アルゴリズムが正しいことを確認するために、我々はいくつかの追加の証拠を必要としています。まず第一に、それは減少し、反復単調を証明しています:
\ [X_ {I + 1} - X_I = \左\ lfloor \ FRAC {1} {2}(X_I + \ FRAC {N} {X_I})\ rfloor \右 - X_I = \ \ lfloor \ FRAC {1を残し} {2}(\ FRAC {N} {X_I} - X_I)rfloor \] \ \右
見られる間隔で\([\ SQRT {X} 、+ \ inftyの)\) 、\(1 + X_ {I} - 。X_I <0 \) 。
その後、我々は、反復が収束することができることを証明しなければならないの\(\ lfloor \ SQRT {N } \ rfloor \) の:
\ [X_ {I + 1} = \左\ lfloor \ FRAC {1} {2} \左(X_I + \左\ lfloor \ FRAC {N} {X_I} \右\ rfloor \右)\右\ rfloor = \左\ lfloor \ FRAC {1} {2}(X_I + \ FRAC {N} {X_I})\右\ rfloor \ GEQ \左\ lfloor \ FRAC {1} {2} \倍2 \回\ SQRT { X_I \ CDOT \ FRAC {N} {X_I}} \右\ rfloor = \ lfloor \ SQRT {N} \ rfloor \]
このため、while
サイクルの終わりには、我々は正しい答えを得ることができます。
求めているニュートンの法則についてsqrt(x)
の時間の複雑さを、著者はまた、明確ではない、シェアを喜ば子供用の靴の理解は〜があります。しかし、データだけでなく、実際の試験を照会することによって、私たちは、ニュートン法の時間効率はバイナリサーチアルゴリズムよりも優れて見ることができます。