ゆっくりと「プログラミングパール」をかじる [継続的に更新中...]

パート I の基本

第1章 始まり

最初の章では歴史的な問題を紹介します。

ディスクファイルを並べ替える方法は? 具体的には、最大 1,000 万件のレコードを含むファイルを並べ替える場合、各レコードは 7 桁の整数であり、使用できるメモリは 1MB のみです。

この質問の助けを借りて、著者の考えと読者への啓発が引き出されます。

  • 「怠惰な」エンジニアになりましょう。問題に対処するときは、一見解決策がわかっても、すぐにコードを書くのではなく、時間をかけて問題を明確にし、問題モデルを抽象化し、問題の特性と組み合わせて分析し、インスピレーションが湧いたときに始めてください。上記の問題を解く際、著者はまず問題を数学的に定義し、問題内のデータの特性を考慮してベクトル形式で問題を解き、時間計算量と空間計算量が低くなります。この方法は、コーディング効果が向上するだけでなく、状況をより十分に考慮してデバッグ時間を短縮でき、深く考えて実行したという印象がより深くなり、後で同様の問題に遭遇したときに、すぐに解決できます。課題解決のアイデアとなり、実感が蓄積されます。
  • 時間と空間は双方に利益をもたらします確かに、多くの場合、時間と空間の間で妥協することがありますが、この種の妥協は、ある側面を注意深く分析したが改善できない場合に限られ、多くの場合、私たち自身のアルゴリズム能力が原因です。思考能力は限られていますが、独自のアルゴリズムは時間と空間の両方で最適化できるかもしれません。そのためには、現在のアルゴリズムの最適化空間について深く考える必要がある一方で、より良い結果を達成できる完全に異なる方法があるかどうか、または自分のアルゴリズムに対する提案をより優れた人に尋ねることができます。途中で処理する場合は、問題の時間と空間の要件の厳格さと最大許容限界を明確にし、ある程度の側面を合理的に犠牲にするという前提の下で、所定の目標に近づける必要があります。
  • シンプルなデザイン「設計者は、デザインが完璧な基準に達していることを確認します。何も足せないのではなく、何も削れないのです。」 要件を満たすことを前提に、将来につながるシンプルなデザインを心がける拡張とトラブルシューティング、「Da Dao できるだけシンプル」、デザインに必要なのはシンプルさの美しさかもしれません。

——内容の一部はコーディングの小さな世界から抜粋しています

第二章 ああ!アルゴリズム 

第 2 章では、二分探索、基本演算 (逆)、ソートの紹介という 3 つの問題を中心に展開します。

まず最初の質問を見てください

32 ビット整数を含むシーケンシャル ファイルの場合、そのような整数は最大 40 億個しか含めることができず、整数の順序はランダムです。このファイルに存在しない 32 ビット整数を見つけてください。十分なメインメモリがあれば、この問題をどのように解決しますか? 複数の外部一時ファイルにアクセスできるが、使用できるメイン メモリが数百バイトしかない場合、この問題をどのように解決しますか?

 私の考え:

まず、32 ビット整数で表現できる最大数は 2^32=4294967296>4000000000 であるため、ファイルに存在しない整数が存在することは明らかです。

  • 十分なメモリがある場合は、最初の章のビット ベクトルの方法を使用できます。つまり、2^32 ビットを 0 に初期化し、数値が読み取られるたびに添字に対応するビット配列の値を 1 に設定します。最終的な統計値は 0 のままです。 の要素は、これまでに出現したことのない整数です。この方法の時間計算量は O(2^n) で、占有空間は約 O(2^n/8)Bです。n=32 の場合、占有空間は約 2^n/8/2^10/ です。 2^10=512MB。

著者のクーデターをもう一度見てみましょう。中心となるアイデアは二分探索です (私の考えは線形探索に近いです)

2 番目の質問である、著者に直接アクセスする方法は非常に独創的です。余分なスペースは必要なく、いくつかの基本的な操作がうまく活用されています。

 n 要素を持つ 1D ベクトルを i 位置だけ左に回転してください。たとえば、n=8、i=3 と仮定すると、ベクトル abcdefgh が回転されて、ベクトル defghabc が得られます。

void rotate(string str,int i){
    int len = str.length();
    if(len > 0 && i%len!=0){
        reverse(str[0],str[i-1]); // cbadefgh
        reverse(str[i],str[n-1]); // cbahgfed
        reverse(str[0],str[n-1]); // defghabc
    }
    cout<<str<<endl;
}

3番目の質問:

 英単語の辞書が与えられた場合、アナグラムのすべてのセットを見つけます。たとえば、「pots」、「stop」、「tops」は、それぞれ別の単語の文字の順序を変更することによって形成されるため、それぞれのアナグラムです。

 正直、この問題をどうやって始めればいいのかわかりませんし、本を読んでもよくわかりません。コーディングスモールワールド_コードをノックできるもの_CSDNブログ-アンドロイド、アルゴリズム、データ構造ブロガーのおかげで、彼の説明は非常に詳細であり、この問題を理解することができました。以下に彼のメモと私の考えを添付します。

アナグラムの定義を考えてみましょう。アナグラムには同じ文字セットという共通点があります。つまり、単語ごとにロゴを選択し、同じロゴを持つ単語を集めるだけで済みます。フラグはアルファベット順に並べられた単語にすることができます。たとえば、「pots」、「stop」、および「tops」のフラグはすべて「opst」です。

この章からの洞察

- 原理:

  1.並べ替えますソートの最も明白な用途は、順序付けられた出力を生成することです。

  2.二分探索このアルゴリズムは、並べ替えられたリスト内の要素を見つけるのに非常に効率的であり、メモリ内並べ替えまたはディスク 並べ替えに使用できます欠点: テーブル全体を事前に把握し、並べ替える必要があります。

-- アイデア:

  1. 物事は複雑に見えますが、注意深く分析すると、問題が突然非常に単純になることがあります。そして、本当に難しい問題であっても、必ずしも複雑な解決策があるとは限りません。多くの場合、私たちに欠けているのは、問題の霧を晴らし、問題モデルを明確にし、問題の本質を把握する能力です。

  2. 多くのアルゴリズムはいくつかの基本的な操作で構成されているため、私たちがしなければならないことは、一方では独自のアルゴリズムの基盤を拡張し、いくつかの基本的なアルゴリズムのアイデアと高度なデータ構造を習得することですが、他方では、現実に直面する 問題に遭遇したとき、問題の抽象モデルを抽出し、適切な基本操作を見つけ、問題の特性と組み合わせることで、アルゴリズムを改善したり、問題を解決する新しい方法を見つけたりすることができます。賢い方法で問題をよりシンプルに解決します。

 ----------------------------------上記は2022-02-17に更新されました------ -----------------------------

第 3 章 データはプログラム構造を決定する

この章では、アルゴリズムの具体的な内容については説明しませんが、著者は、間違ったデータ構造の使用によって引き起こされるいくつかの問題を列挙し、提案を示します。

著者のヒントの一部を整理し、メモと原文を以下に添付します。

ノート:

オリジナル:

ここでは、自分の出発点を振り返るためのいくつかの原則を示します。

  • 配列を使用して繰り返しコードを書き直します。長くて類似したコードは、多くの場合、最も単純なデータ構造である配列を使用することでより適切に表現できます。

  • 複雑な構造をカプセル化します。非常に複雑なデータ構造が必要な場合は、それらを抽象用語で定義し、操作をクラスとして表現します。

  • 可能な限り高度なツールを使用してください。ハイパーテキスト、名前と値のペア、スプレッドシート、データベース、プログラミング言語などはすべて、特定の問題領域における強力なツールです。

  • プログラムの構造はデータから導き出されます。優れたプログラマーは、コードを書き始める前に、入力、出力、および中間データ構造を完全に理解し、これらの構造に基づいてプログラムを作成します。

この章からの洞察

学部の授業で「プログラム = データ構造 + アルゴリズム」という文を見た記憶があります。データ構造の最適化はコードの最適化において重要な役割を果たします。この章を学習した後、今後のプログラミングで反復性の高いコードに遭遇した場合は、コードの汎用性と互換性を向上させるために配列をさらに使用するよう努めます。

----------------------------------上記は2022-02-22に更新されました------ ----------------------------- 

第 4 章 正しいプログラムの作成

この章では、二分探索を例として、アルゴリズム検証の「3 つのステップ」 (初期化、保守、終了) を説明します。

初期化:ループが初めて実行されるとき、不変条件は true です。
Hold:反復の開始時およびループ本体の実行中に不変式が true の場合、ループ本体の実行時も不変式は True のままです。
終了:ループを終了し、目的の結果を得ることができます。

プログラムを作成した後のテストの重要性は、コーディング自体と同じくらい重要です

思考ロジックの抜け穴やプログラム作成時の手作業によるミスなどにより、プログラムのバグが発生する可能性があり、テストはバグを発見する作業であり、バグが発見され、バグの原因が特定されて初めてバグを完全に解決し、正確性を保証することができます。プログラムの完全性、堅牢性。
プログラマはテスターの役割の一部としても機能し、使用されたアルゴリズムの正しさを証明し(実際には一語一語証明する必要はありません)、包括的なテストケースでプログラムの結果を検証し、自らの手でバグを発見する必要があります。プログラマーはテスターのようにプログラムの指標を 1 つずつ調べる必要はありませんし、その時間もありませんが、テスト前に明らかな論理の抜け穴やパフォーマンスの問題をチェックする方が良いでしょう。実際の作業では、テスト、テスト、バグの提出というプロセスに一定の時間がかかりますし、突然のバグによって現在の作業リズムも崩れることになるため、プログラム開発全体のサイクルをある程度短縮することができます。また、他の人のプログラミング能力に対する評価を高めることもできます。想像してみてください。テスターが時々開発者に、「プログラムの結果のここかしこで何かがおかしい」と言うのを聞いたら、開発者に対する信頼レベルが大幅に低下するに違いありません。 。 

----------------------------------上記は2022-03-03に更新されました------ -----------------------------

第 5 章 プログラミングの豆知識

 

----------------------------------上記は2022-03-11に更新されました------ -----------------------------

パート II パフォーマンス 

第 6 章 プログラムのパフォーマンス分析  

 

----------------------------------上記は2022-03-14に更新されました------ -----------------------------

おすすめ

転載: blog.csdn.net/weixin_43594181/article/details/122978919