關於學習數據結構與演算法的一些疑惑?
本人正在學習數據結構,書是Mark Allen Weiss寫的數據結構(C++版本),學習遇到了這麼幾個問題
1,數學證明很難看懂,前面一些簡單的數據結構和演算法的證明還能看懂,到了中部之後,就基本看不懂了,看到就思考一下,思考不懂就直接看結論,可能是我自己數學功底比較薄弱的原因吧
2,關於一個演算法的正確性,有寫地方光看書上證明正確性只能理解個半斤八兩,直接看實現並且人肉跑演算法後,也算是知道一個演算法大概是怎麼樣的一個流程以及確定這個演算法是正確的(不正確的演算法還能寫在書上嗎!),可是我還是感覺有哪裡不足,還不算是完全理解了這個演算法,卻又說不上是哪裡不足,求分析幫助
3,書上的源碼都自己敲了一遍,做練習的時候還是要邊翻書邊做,感覺提升很慢
https://courses.engr.illinois.edu/cs225/
Search · cs225 · GitHub
Mark Allen Weiss的數據結構(C++版本)挺不錯的。我手頭買了一本第三版(最新是第四版,相差無幾)。我們學校的數據結構用的就是這本書當 optional 教材。個人感覺比 Robert Sedgewick 的那本《演算法》(java 版本)寫得好很多(不過那版的演算法圖多)
上面給了鏈接,是 UIUC 的 CS225的教案和 code。github 上有的包含答案……至於對不對我就不知道了,你自己試試看跑跑看。
我個人建議:
1,數學證明很難看懂,前面一些簡單的數據結構和演算法的證明還能看懂,到了中部之後,就基本看不懂了,看到就思考一下,思考不懂就直接看結論,可能是我自己數學功底比較薄弱的原因吧
2,關於一個演算法的正確性,有寫地方光看書上證明正確性只能理解個半斤八兩,直接看實現並且人肉跑演算法後,也算是知道一個演算法大概是怎麼樣的一個流程以及確定這個演算法是正確的(不正確的演算法還能寫在書上嗎!),可是我還是感覺有哪裡不足,還不算是完全理解了這個演算法,卻又說不上是哪裡不足,求分析幫助
3,書上的源碼都自己敲了一遍,做練習的時候還是要邊翻書邊做,感覺提升很慢
1:複雜度還是比較好證明的,基本思路是數學歸納法,有時候的確需要數學功底,但是如果目標是讓你判斷:A 和 B 方法再某個情況下哪個比較優秀的話,並不一定需要嚴格的數學證明,求 worst case 啥的很容易。這個可以慢慢來,也可以結合那本 Java 的演算法
2:這部分內容我覺得超出數據結構這方面的要求了。比如讓你證明為什麼 huffman 是正確的,能不能有多重 implementation ……這就很難了,因為本身 huffman 就是 paper 證明的;除非徹底去啃 paper,不然還是在看二手貨,在使用結論。
3:把我給你的鏈接裡面的 lab 寫了吧。mp 也可以寫。先自己寫,不行再看 git 上的答案。多寫。多寫。多寫。首先分清是演算法的證明分為:複雜度、正確性、收斂性等。複雜度原則上必須掌握(Haskell之類的除外。。。),正確性看情況, 下面分3點看:面試中;開發中;「形式化「地。
另外注意,有些演算法性是很難證明的(比如數學建模中的那幾個高級演算法。。不點名了),有些甚至至今未解決。
1. 在面試中,被問道演算法正確性,用自然語言講講就夠了,常用的方法包括:循環不變式、反證法、數學歸納法等等,可參考《演算法導論》。下面舉個例子:
問題:求數組中個數超過一半的數
演算法描述:兩兩比較數組中的數,若相同,留下一個,若不同,丟棄這兩個數。直到數組長度為1,留下的數就是個數超過一半的數;如果最後留下2個不同的數,選其中之一掃描一遍統計其出現的次數。。。
複雜度:攤還分析,每次都至少可以去掉一個數,最多需要n次,所以複雜度O(n)
正確性:因為超過一半,所以刪掉一個相同的數和兩個不同的數,原先超過一半的數現在仍然超過一半。
2. 實際開發中,主要用各種測試方法。當然100%覆蓋是不太可能的, 在有些情況下,可以知道某些case是等價類,從而可以減少測試量。各種測試用例自動生成、覆蓋率檢測等工具也從來不缺。 測試在工業、學術界是不斷發展的。
3. 比較形式化的。搜a verified implementation of,certified software,model checking, hoare logic...有真相。。
理論:Hoare Logic,Curry–Howard correspondence等,更本質的是數理邏輯的Proof Theory。
工具:有基於constructive type theory的Coq,也有用Higher Order Logic的Isabele等等。
Require Import List.
Require Import ZArith.
Open Scope Z_scope.
(******** Implementation of the insertion insertion_sort algorithm ********)
(* Inserts the specified element z into the lst at the correct position. *)
Fixpoint insert (z:Z) (l : list Z) {struct l} : list Z :=
match l with
| nil =&> z::nil
| head::tail =&> if Z_le_gt_dec z head then z::head::tail else head::(insert z tail)
end.
(* The insertion_sort function. *)
Fixpoint insertion_sort (l : list Z) {struct l} : (list Z) :=
match l with
| nil =&> nil
| head::tail =&> insert head (insertion_sort tail)
end.
(************** Helper methods ***********)
(* Inductive check for when a list is sorted. *)
Inductive sorted : list Z -&> Prop :=
| sorted0 : sorted nil
| sorted1: forall z:Z, sorted (z::nil)
| sorted2: forall (z1 z2 : Z) (l : list Z),
z1 &<= z2 -&> sorted (z2::l) -&> sorted (z1::z2::l).
(* Insertion sort hints for sorted list. *)
Hint Resolve sorted0 sorted1 sorted2 : insertion_sort.
(* Counts the number of occurrences of the element z in list l. *)
Fixpoint count (z:Z) (l : list Z) {struct l} : nat :=
match l with
| nil =&> O
| (head::tail) =&> if Z_eq_dec z head then S (count z tail) else count z tail
end.
(* Definition of equality for two lists. Two lists are equal if they contain the same elements (are permuted). *)
Definition is_equal (l l" : list Z) :=
forall z : Z, count z l = count z l".
(* Definition of reflexive for a list. *)
Lemma is_equal_refl : forall l : list Z, is_equal l l.
Proof.
unfold is_equal.
trivial.
Qed.
(* Definition of simetry for two lists that have been permuted. *)
Lemma is_equal_syme : forall l l" : list Z, is_equal l l" -&> is_equal l" l.
Proof.
unfold is_equal.
auto.
Qed.
(* Definition of transitivity for three lists. *)
Lemma is_equal_trans : forall l l" l"" : list Z,
is_equal l l" -&> is_equal l" l"" -&> is_equal l l"".
Proof.
intros.
unfold is_equal in *.
intro; eapply trans_eq; eauto.
Qed.
(* Definition of equality for two lists that have been extended with a value. *)
Lemma is_equal_cons : forall (z : Z) (l l" : list Z),
is_equal l l" -&> is_equal (z::l) (z::l").
Proof.
intros z l l" H z".
simpl.
case (Z_eq_dec z" z).
auto.
auto.
Qed.
(* Definition of equality for two lists that have been extended with two value in permutated order. *)
Lemma is_equal_perm : forall (x y : Z) (l l" : list Z),
is_equal l l" -&> is_equal (x::y::l) (y::x::l").
Proof.
intros x y l l" H z; simpl.
case (Z_eq_dec z x); case (Z_eq_dec z y); simpl; case (H z); auto.
Qed.
(* Insertion sort hints for the equality of permuted lists. *)
Hint Resolve is_equal_cons is_equal_refl is_equal_perm : insertion_sort.
(********** Proofs of correctness **********)
(* Lemma that states that applying the inser method on an already sorted list stays sorted. *)
Lemma sorted_insert : forall (l : list Z) (x : Z),
sorted l -&> sorted (insert x l).
Proof.
intros l x H; elim H.
simpl; auto with insertion_sort.
intro z; simpl.
case (Z_le_gt_dec x z); simpl; auto with insertion_sort zarith.
simpl.
intros z1 z2; case (Z_le_gt_dec x z2); simpl; intros;
case (Z_le_gt_dec x z1); simpl; auto with insertion_sort zarith.
Qed.
(* Lemma that states the insert method preserves all of the elements. *)
Lemma insert_preserves : forall (l : list Z) (x : Z),
is_equal (x::l) (insert x l).
Proof.
induction l as [ | a l0 H]; simpl.
auto with insertion_sort.
intros x; case (Z_le_gt_dec x a); simpl.
auto with insertion_sort.
intro; apply is_equal_trans with (a :: x :: l0); auto with insertion_sort.
Qed.
(* Lemma that states that applying the insertion sort on an already sorted list stays sorted. *)
Lemma sorted_insertion_sort : forall (l : list Z), sorted (insertion_sort l).
Proof.
induction l.
auto with insertion_sort.
simpl.
apply sorted_insert.
assumption.
Qed.
(* Lemma that states that insertion sort algorithm preserves all of the elements. *)
Lemma insertion_sort_preserves: forall (l : list Z), (is_equal l (insertion_sort l)).
Proof.
induction l.
auto with insertion_sort.
simpl.
eauto using is_equal_trans, insert_preserves with insertion_sort.
Qed.
具體數學是本好書
看證明吃力的話就先跳過。網上找些博客,一般會發現一些不涉及數學但是講演算法原理的文章。差不多明白了再回頭看數學證明。
多練多想就是了,知識一上來有些不能輕易理解是正常現象。
2和3就像大家說的,還是剛開始學,不熟。用多了就熟了。1的話比較複雜,影響因素比較多。要不你具體舉個例子?哪個演算法的數學證明看不懂,證明貼上來,從哪裡開始不懂的,哪個術語不知道什麼含義,這樣討論一輪下來就知道自己欠缺在哪裡了。
多練就好。證明可跳過。隨著對cs理解的加深,這些都會自然解決。
比如我剛學快排的時候無法獨立寫出,就那麼放著,後來有次想寫就自己寫出來了。
可以嘗試配合公開課學習,coursera上的演算法,或學堂在線的數據結構都不錯,看完自己實現一下,實現時關鍵地方插入不變式斷言可有助於理解和debug。geeksforgeeks上也有不錯講解。
題主可以將演算法的證明放在下一階段的學習。初期把各種數據結構概念弄熟,能夠用來模擬解決一些實際演算法問題即可。 後期可以去看看演算法的證明,那方面的東西目前我沒有涉及。
推薦閱讀:
※在這種情況下,我應該如何努力才能如願成為一名計算機大神?
※Python 適合初學編程的人學嗎?
※優秀的程序員都應該常接觸電腦,但為什麼有些牛 X 程序員卻沒有近視?
※如果出現《我是程序員》這樣的節目,你覺得有什麼看點?
※C++ 如何進階?