[轉載]學程式設計的人不能不看的好文章

轉載自: http://www.vcroad.net/ (棗子原創 [email protected])


做為一名大四的學生,我去面試過不少的公司,有成功的也有失敗的,但是對我來說所有的失敗在某種意義上都是一種成功,特別是我下面寫的這些,寫這篇文章的時候,我已經簽了南京的一家軟體公司,但是想起今年 2 月 21 日我面試蘇州台灣的 IT 公司的經歷聯想到我們現在學習程式設計的一些情況我真的深有感觸,這次面試使我深深的體會到了失敗但也收穫了很多。

我要說的將分成三部分:

  1. 我去面試的具體經過
  2. 由面試想到的
  3. 現今我應該做的

當然這些話很大程度上是我個人的意見,不可能完全得到大家的贊同,所以在某些觀點上如果哪位朋友覺得跟我的有很大出入,請不要介意,也不要對我攻擊,就當我沒有說過,歡迎和我聯繫共同探討這些問題!

1. 面試經過

大約在年前我接到了台灣瑞昱 (Realtek) 蘇州公司的面試通知,通知我 2 月 21 日到蘇州工業園區面試,接到面試後的幾天我把一些專業課程溫習了一遍,特別是 C++ 和資料結構,由於大學幾年裡,我一直鑽研這些方面,加上通過了進階程式設計師的考試,對於一些常用的演算法我差不多也達到了爛熟於胸的地步,當時的感覺是如果問了我這些方面的問題我應該是沒有問題的!

那天我被安排在 4:30 面試,由一位技術人員單獨跟我面試,在問了一些簡單的問題之後他給我出了一道程式設計題目,題目是這樣的 (由於具體面試的題目比較煩瑣,我將其核心思想提取出來分解成了兩個獨立的簡單的問題,有可能問題分解的不當,請大家見諒,實際面試了一個的問題但比其複雜很多,而且涉及一些高等數學轉換):

1) 寫一個函數計算當參數為 n(n 很大) 時的值 1-2+3-4+5-6+7...+n

哼,我的心裡冷笑一聲!沒想到這麼簡單,我有點緊張的心情頓時放鬆起來!於是很快我給出我的解法:

long fn(long n) {
  long temp=0;
  int i,flag=1;
  if(n<=0) {
    printf("error: n must > 0");
    exit(1);
  }
  for(i=1;i<=n;i++) {
    temp=temp+flag*i;
    flag=(-1)*flag;
  }
  return temp;
}

搞定!當我用期待的眼光看著面試官的時候,他微笑著跟我說,執行結果肯定是沒有問題!但當 n 很大的時候我這個程式執行效率很低,在嵌入式系統的開發中,程式的執行效率很重要,能讓 CPU 少執行一條指令都是好的,他讓我看看這個程式還有什麼可以修改的地方,把程式優化一下!

聽了這些話,我的心情當時變的有點沉重,沒想到他的要求很嚴格,之後我對程式進行了嚴格的分析,給出了改進了的方案!

long fn(long n) {
  long temp=0;
  int j=1,i=1,flag=1;
  if(n<=0) {
    printf("error: n must > 0");
    exit(1);
  }
  while(j<=n) {
    temp=temp+i;
    i=-i;
    i>0?i++:i--;
    j++;
  }
  return temp;
}

雖然我不敢保證我這個演算法是最優的,但是比起上一個程式,我將所有涉及到乘法指令的敘述式改為執行加法指令,既達到要題目的要求而且運算時間縮短了很多!而代價僅僅是增加了一個整數變數!

但是我現在的信心已經受了一點打擊,我半信半疑的看著面試官,他還是微笑著跟我說:「不錯,這個程式確實在效率上有的很大的提升!」我心裡一陣暗喜!

但他接著說這個程式仍然不能達到他的要求,要我給出更優的方案!天啊!還有優化!我當時真的有點崩潰了,想了一會後,我請求他給出他的方案!然後他很爽快的給出了他的程式!

long fn(long n) {
  if(n<=0) {
    printf("error: n must > 0");
    exit(1);
  }
  if(0==n%2)
    return (n/2)*(-1);
  else
    return (n/2)*(-1)+n;
}

搞笑,當時我目瞪口呆,沒想到他是這個意思,這麼簡單的程式碼我真的不會寫嗎,但是我為什麼沒有往那方面想呢!

他說的沒有錯,在 n 很大很大的時候這三個程式執行時間的差別簡直是天壤之別!

當我剛想開口說點什麼的時候,他卻先開口了:「不要認為 CPU 運算速度快就把所有的問題都推給它去做,程式設計師應該將程式碼優化再優化,我們自己能做的決不要讓 CPU 做,因為 CPU 是為用戶服務的,不是為我們程式設計師服務的!」

多麼精闢的語言,我已經不想再說什麼了!

接著是第二個問題:

2) 用一種技巧性的設計方法來用一個函數實現兩個函數的功能

如:

fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6!
fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9!

現在用一個函數 fn(int n,int flag) 實現,當 flag 為 0 時,實現 fn1 功能,如果 flag 為 1 時實現 fn2 功能!他的要求還是效率,效率,效率!

說實在話,如果我心情好的話我應該能給出一種比較好的演算法,但我那時真的沒有什麼心思再想了,我在紙上胡亂畫了一些諸如 6!=6*5! 的公式後直截了當的跟他說要他給出他的答案!

面試官也沒有說什麼,給出了他的思路:

定義一個二維陣列 float t[2][5] 存入 [2!,3!,4!,5!,6!],[5!,6!,7!,8!,9!] 然後給出一個迴圈:

for(i=0;i<6;i++) {
  temp=temp+n/t[flag];
}

最後得到計算值!

呵呵,典型的空間換時間的演算法!

這些總共花了 50 分鐘的時間,還有 10 分鐘我就跟他很隨意的聊聊天,聊了一些程式設計以及生活的問題,那時的我已經很放鬆了,因為我知道這次面試結果只有一個:失敗。

5:30 的時候面試官要我等通知,於是我離開了他們公司。這就是面試的整個經過!

2. 由面試想到的

真的是很失敗啊!我記得那天下好大的雨,氣溫也很低,我邊走邊想,從 5:30 一直走到 7:30,全身都濕透了,又冷又餓,但是我只是一直走,腦子裡面充滿了疑惑,我也想讓雨把自己淋醒!看到這裡有些朋友可能覺得那些面試題目不算什麼,如果讓自己做的話肯定能全部答對,我肯定相信你,因為我從未懷疑過中國程式設計師的能力,我認為中國有世界上最好的程式設計師,我也從未認為自己是高手,所以我做不出來不代表中國程式設計師比台灣或者別的地方的程式設計師差,所以我就從我的角度,我的所見所想來談一些感想。

不錯,全世界都有優秀的程式設計師,中國也不例外,但是我疑惑的是:到底中國和台灣或者國外優秀的程式設計師的比例到底是多少?台灣我不知道,中國 100 個程式設計師裡有幾個是優秀的呢?

我根本算不上,從上面的表現就足以說明一切了!是 1 個?5 個?10 個?50 個?這個數字我不敢亂猜,恐遭網友一頓痛罵,那麼我們國內有多少人學習電腦呢?拿我們學校來說,電腦 97 級 4 個班,98 級 5 個班,99 級 10 個班,2000 級 17 個班,人多了,老師怎麼辦?我們學校的做法是讓研究生上課,然後呢?補考一抓一大把,大把大把的補考費落入了學校的口袋,還說現在的學生素質低!

真是好笑,我都不知道學校這麼做是為了什麼,為國內培養大量的程式設計師嗎?學生們能真正學到電腦知識嗎?好了,我敢講,在我們學校學習程式設計學生和優秀程式設計師 (注意我指的是優秀,只會寫幾個糟爛程式的人算不上) 的比例應該是 100:0.1。

在這種比例下雖然我們中國學習程式設計的人鋪天蓋地,但是想想有多少人能真正為中國軟體業發展作出貢獻,有多少人能真正寫出優秀的程式揚名海外!

我從學習程式設計以來,不管是自學還是老師指導,從來都是解決問題就好,寫出程式來就行,我的疑惑是:我們有真正的強調過程式的效率、程式的品質嗎?我們有仔細分析過我們寫的東西,看看有沒有可以改進的地方、有沒有簡單的方法來達到同樣的目的嗎?

我捫心自問,我發現,我從來沒有對我寫出來的程式進行過優化,最多就是進行詳細的測試,然後 Debug,但是這就足夠了嗎?

這些天我偶爾發現我曾經寫過的一個遊戲,那是一年前我剛加入 www.vcroad.net 做為其中一員時候,感覺應該拿點東西出來,然後花了一個星期的時間寫出來的!

程式不算複雜,但是用到了不少資料結構的東西,也用到了一些精彩的演算法,加上 Windows 的介面和遊戲的可玩性,寫完後受到了不少好評,我當時真的很佩服自己!

但是現在看呢:沒有一句註解,好多醜陋的函數名稱,比如:void chushihua(),好多沒有必要的變數,可以用簡單敘述式完成的工作,我使用華麗的演算法,大量使用全域變數…

說不好聽的話,六百多行的程式碼除了能執行之外就是一陀屎!

如果一年前我能聽到一些反面意見的話,大概我能早一點覺悟,但是自從原始碼在網站發佈以來聽到的都是讚美之詞,沒有一個人向我提出程式改進的意見,這又說明了一個什麼問題呢?很值得思考啊!

還有一個疑惑是:我們說的和做的真的一樣嗎?

我在學校的時候曾經受學院指派承辦過一個電腦程式設計大賽,請了一個老師出決賽的題目,主要是一些演算法題目,這個老師可能是我上大學以來唯一敬佩的老師了,從程式除錯到評分,對於每個程式都仔細分析其時間效率和空間效率,然後綜合評分,四十個人的考卷,老師從下午三點一直除錯到晚上十點,在有些寫的精彩的敘述式後還加上批註。

我真是高興能遇到這樣的老師,並且和他做深入的交流。但在事後,卻發生了一件不愉快的事,在比賽中獲得第二名的學生找到了我,說他程式全部除錯成功應該給他滿分,並且應該得第一,我說不過他,最後調出了他的原程式和第一名的原程式對比,不錯,兩個程式都執行的很好,這時,那個同學開口了:「我的程式寫的十分簡捷明瞭,僅僅數行就完成了題目要求,而他的卻寫了一大堆,為什麼給他的分數多過給我的分數。」

我當時很是氣憤,如果不是老師負責的話,那麼現在第一名和第二名的位置真的要互調了!拜託,不是程式的行數越少程式的品質就越高,我記得我跟他大談這方面的道理,最後說服他了!

哈哈,但是我,只能說說而已,我不知道還有多少人一樣,說起來頭頭是道,但心裡卻壓根就從未重視過它!

3. 我打算做的

其實那天我想到的遠不止上面那麼多,但是我不想再說了,因為我猜想看這篇文章的網友大概都有一肚子的感想,一肚子的抱怨,借用這篇文章發洩可不是我想達到的目的,在上面我把自己罵的一文不值也不是妄自菲薄,但是在某些方面我真的做錯了,或者說是偏離了正確方向,現在是矯正方向和重整旗鼓的時候了!就像我前面說過的,我相信中國有世界上最好的程式設計師,我也相信我的水準不會一直保持現狀,我現在就收拾起牢騷真正的實幹起來!

真的很巧,就寫到這裡的時候我在網上偶爾發現了一篇手冊,我不知道這暗示著什麼,但是我想如果我照下面這個基本原則一直踏實做下去,我一定會實現我的理想 - 一名優秀的軟體設計師!

(下面這些文字不是我的原創,是我偶爾在網上發現的,我真的很幸運能看到這些,這篇文章也隨著下面的文字而結束,我真心的希望您能從這篇文章中得到啟發,這篇文章歡迎大家隨意轉載,您可以不寫作者是誰,但是請您寫上 ww.vcroad.net 原創,謝謝您的支持)


程式設計師的高手之路

作者:金蝶中間件公司 CTO 袁紅崗

不知不覺做軟體已經做了十年,有成功的喜悅,也有失敗的痛苦,但總不敢稱自己是高手,因為和我心目中真正的高手們比起來,還差的太遠。世界上並沒有成為高手的捷徑,但一些基本原則是可以遵循的。

1. 紮實的基礎

資料結構、離散數學、編譯原理,這些是所有電腦科學的基礎,如果不掌握他們,很難寫出高水準的程式。據我的觀察,學電腦專業的人比學其他專業的人更能寫出高品質的軟體。程式人人都會寫,但當你發現寫到一定程度很難再提升的時候,就應該想想是不是要回過頭來學學這些最基本的理論。不要一開始就去學 OOP,即使你再精通 OOP,遇到一些基本演算法的時候可能也會束手無策。

2. 豐富的想像力

不要拘泥於固定的思維方式,遇到問題的時候要多想幾種解決問題的方案,試試別人從沒想過的方法。豐富的想像力是建立在豐富的知識的基礎上,除電腦以外,多涉獵其他的學科,比如天文、物理、數學...等等。另外,多看科幻電影也是一個很好的途徑。

3. 最簡單的是最好的

這也許是所有科學都遵循的一條準則,如此複雜的質能互換原理在愛因斯坦眼裡不過是一個簡單得不能再簡單的公式:E=mc2。簡單的方法更容易被人理解,更容易實現,也更容易維護。遇到問題時要優先考慮最簡單的方案,只有簡單方案不能滿足要求時再考慮複雜的方案。

4. 不鑽牛角尖

當你遇到障礙的時候,不妨暫時遠離電腦,看看窗外的風景,聽聽輕音樂,和朋友聊聊天。當我遇到難題的時候會去玩遊戲,而且是那種極暴力的打鬥類遊戲,當負責遊戲的那部分大腦細胞極度亢奮的時候,負責程式設計的那部分大腦細胞就得到了充分的休息。當重新開始工作的時候,我會發現那些難題現在竟然可以迎刃而解。

5. 對答案的渴求

人類自然科學的發展史就是一個渴求得到答案的過程,即使只能知道答案的一小部分也值得我們去付出。只要你堅定信念,一定要找到問題的答案,你才會付出精力去探索,即使最後沒有得到答案,在過程中你也會學到很多東西。

6. 多與別人交流

三人行必有我師,也許在一次和別人不經意的談話中,就可以迸出靈感的火花。多上上網,看看別人對同一問題的看法,會給你很大的啟發。

7. 良好的程式設計風格

注意養成良好的習慣,程式碼的縮進編排、變數的命名規則要始終保持一致。大家都知道如何排除程式碼中錯誤,卻往往忽視了對註解的排錯。註解是程式的一個重要組成部分,它可以使你的程式碼更容易理解,而如果程式碼已經清楚地表達了你的思想,就不必再加註解了,如果註解和程式碼不一致,那就更加糟糕。

8. 韌性和毅力

這也許是「高手」和一般程式設計師最大的區別。A good programming is 99% sweat and 1% coffee。高手們並不是天才,他們是在無數個日日夜夜中磨練出來的。成功能給我們帶來無比的喜悅,但過程卻是無比的枯燥乏味。你不妨做個測試,找個 10000 以內的質數表,把它們全都抄下來,然後再檢查三遍,如果能夠不間斷地完成這一工作,你就可以滿足這一條。所以抄寫也是一種加強記憶的良好方法!

這些是對程式設計師生涯的一點建議,希望能夠給大家有所幫助。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *

  1. 程式設計師本來就必須要想到程式的執行效率,尤其是設計有關數學演算法的問題。
    在BigO(1) 做得完的事情, 就沒有必要花費BigO(n) 去做。1-2+3-4+5-6+7......+n看就知道兩組當一項都是-1, 當然是一道簡化的數學式即可算出,根本就不需要使用迴圈去計算。
    更例如算費氏數列,絕大多數人都使用遞迴計算,但是n帶很大的時候,效率卻差到不行;但是事實上不論n多少, 是有BigO(1)就計算出來的作法。有規則的東西都幾乎可以用BigO(1)的演算法來計算得到結果,就端看設計者的功力和數學好不好而定。

  2. Using Internet Explorer Internet Explorer 6.0 on Windows Windows XP

    long fn(long n) {
    if(n 0);
    exit(1);
    }
    if(0==n%2)
    return (n/2)*(-1);
    else
    return (n/2)*(-1)+n;
    }

    在爬文時看到這篇
    有個小小的錯誤

    if(0==n%2)
    return (n/2)*(-1);
    else
    return (n/2-0.5)*(-1)+n;

    當N為單數時,要捨去

  3. Using Mozilla Firefox Mozilla Firefox 3.6.6 on Windows Windows XP

    我是個初學者,我也有了錯誤的觀念了…我也以為程式碼的簡短就是好…看了版主的這些文章後,才知道並非如此…

  4. Using Mozilla Firefox Mozilla Firefox 3.6.8 on Windows Windows XP

    egg說法有錯,型別為long,本來就會無條件捨去。
    效果等同底下吧~
    if(0==n%2) return (n/2)*(-1);
    else return ((n+1)/2);

    事實上還可以再簡約一點:
    (n/2)*(-1)+(n%2)?n:0;
    這樣就好了~哈
    這都是事後諸葛亮了吧~
    面試一緊張有些就什麼都忘了的說~

    • Using Mozilla Firefox Mozilla Firefox 3.6.8 on Windows Windows XP

      long fn(unsigned long n){
      return (long)n/2*(-1)+((n%2)?n:0);
      }

      型別有考慮進去,根本不需要考慮輸入負數產生的影響。

  5. Using Internet Explorer Internet Explorer 7.0 on Windows Windows XP

    很有趣...我也來賣弄一下

    // using logic instructions.
    inline long fn(long n)
    {
    return n&1 ? ((n+1)>>1):-((n+1)>>1) ;
    }

    // using inline asm.
    long fn(long n)
    {
    __asm{
    mov eax,n ; Load n to AX
    inc eax ; AX = AX + 1.
    shr eax,1 ; AX divides by 2.
    jnc EXIT ; Exit if AX is a odd number.
    neg eax ; AX = -AX ;
    EXIT:
    }
    }

  6. Using Google Chrome Google Chrome 10.0.648.204 on Windows Windows XP

    第二題還有更簡單得解法
    const double xx = 1/2!+1/3!+1/4!+1/5!+1/6!;
    const double yy = 1/5!+1/6!+1/7!+1/8!+1/9!;

    fn(int n,int flag)
    {
    if(flag==0)
    return xx*n;

    if(flag==1)
    return yy*n;

    print ("warring for fn()\n");
    return 0;
    }

  7. Using Google Chrome Google Chrome 12.0.742.100 on Windows Windows 7

    哈~~我不是寫程式的,但是我只是對你留言板有一個好奇。
    我看到Win xp, linux Ubuntu,怎麼還是一堆人都在用XP。

    很好奇這個留言板是你自己開發的嗎?
    挺有趣的。

    因為可以知道使用者的習慣,這支留言板的程式,我覺得還不錯。
    至少這是我目前第一次看到的。

    我最近在學AS3,突然很想學程式,可是看了這篇文章之後...才發要學程式之路,有很一大段的路程需要學習的。

    我不敢想像,這要花多久的時間去學~~畢竟,已經不是學生了,沒有那麼多時間可以耗費在這上面,只能夠利用假日或是下班休息的時候學習。

    我想,寫程式的方式,和想法應該都是一樣的吧!

    想別人沒想過的,做別人沒做過的,才是成功的第一步。

  8. Using Mozilla Firefox Mozilla Firefox 3.6.18 on Windows Windows Vista

    To Fannie:

    瀏覽器及作業系統資訊是用 "Browser Sniff" 這個外掛程式。

  9. 引用通告: 貓居。C'FrCo » Blog Archive » 日記|一篇改變我一生的文字 - 一隻貓,一台thinkpad,走遍世界

  10. Using Internet Explorer Internet Explorer 9.0 on Windows Windows 7

    1 =1
    1-2 =-1
    1-2+3 =2
    1-2+3-4 =-2
    1-2+3-4+5 =3
    1-2+3-4+5-6 =-3
    1-2+3-4+5-6+7 =4
    1-2+3-4+5-6+7-8 =-4

    n為奇數
    1+(-2+3)+(-4+5)+(-6+7)……+ [ -(n-1)+n ]
    n為偶數
    (1-2)+(3-4)+(5-6)+(7-8)……+ [ (n-1)-n ]

    so?
    樓上都想太多

  11. Using Internet Explorer Internet Explorer 9.0 on Windows Windows 7

    我不想去探討上面的演算法的優與劣, 看了這篇文章, 覺得有些意思, 提出我在這個領域將近二十年的經驗給大家分享, 其實很多技術說穿得真的一文錢都不值, 像是作者提出這位面試官的解答, 如果你真要考慮效能不是要利用 &&, ||, <> 等位元運算不是更有效率? 這樣的答案太多了, 但若我是主考官, 我在意的恐怕不是這位被面試者"現在"的能力, 而是"未來"的潛力, 就像作者在面試的前後不是就被點通了? 又比如身分證驗證程式, 你可以看到網路上流傳著各種版本的演算法, 你很可能看到一種演算法, 會驚呼, "捷克, 這太神奇了", 若干年後, 會發現之前那不過是駝垃圾, 程式的領域太多了, 有處理CPU, 硬體, 也有重視資料庫的處理, 也有在意使用者的操作邏輯與親和性........一個稱職的程式設計師有一個不可或缺的能力, 自我成長, 因為如果這位面試官想用的是好的工程師, 他該在意的不是這個答案的對與錯, 而是他能理解的程度, 以及學習的能力與速度, 這些都不是在面試的一個小時中可以得到答案的, 需要時間, 正在帶一個新人工程師, 他還有三個月的時間要決定他的去留, 想看看到底怎樣的人適合當工程師, 這篇文章給了我一些啟發, 感謝了.

  12. 引用通告: 學程式設計的人不能不看的好文章 | interflyblog

  13. Using Google Chrome Google Chrome 36.0.1985.125 on Windows Windows 7

    照這篇文章所言,第一題中第二個方法應該比第一個快,可是用第一個方法跑出來的時間少於第二個方法,秒數差了將近1倍~求解~~~~

    我用的編譯器是VS 2012

  14. Using Google Chrome Google Chrome 37.0.2062.120 on Windows Windows 7

    正在學習程式的我,希望在最快的速度能看懂你這兩題面試題目,並且能做出更進一步提升效能的方法。來與大家討論~請大家祝福我吧!~

  15. Using Google Chrome Google Chrome 43.0.2357.124 on Windows Windows XP

    我還以為基礎打好 包括了 離散數學 微積分 工程數學 線性代數這類...
    因為我一直自認為受的不是正規教育 數學不好 所以寫不出好程式是理所當然的

  16. Using Google Chrome Google Chrome 33.0.0.0 on Android Android 4.4.4

    這篇的前面部分是袁紅崗的著作

    但是裡面內容所說的是臺灣的程式設計人員,而非大陸的

    原創部分頂多是後面說的部分

    資料結構跟演算法本來就是程式設計人員精進必要的技能

    • Using Mozilla Firefox Mozilla Firefox 38.0 on Windows Windows NT

      這篇文章的前半部是「棗子」的文章,後半部才是「袁紅崗」的文章。
      原文就是「棗子」在後面轉載「袁紅崗」的文章,我是原文轉載整篇文章,替換成台灣的用語。

  17. 引用通告: [轉載]學程式設計的人不能不看的好文章 | Never Nop Tech 軟體技術交流

  18. Using Google Chrome Google Chrome 46.0.2486.0 on Windows Windows NT

    我老公也是程式設計師
    我看他工作時多多少少有時會有些沒有辦法完成的程式
    然後就要開會如何解決

  19. Using Google Chrome Google Chrome 49.0.2623.110 on Windows Windows NT

    answer = (-1) * (n>>1) + (((n&1)>0)? n : 0);

  20. Using Google Chrome Google Chrome 49.0.2623.110 on Windows Windows NT

    (6!) = 720
    (9!) = 362880

    fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6!
    = n(1 + 6 + 6*5 + 6*5*4 + 6*5*4*3) / (6!)= n*517/720

    fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9!
    = n(1 + 9 + 9*8 + 9*8*7 + 9*8*7*6) / (9!)= n*3610/362880

    if(flag)
    return fn2;
    else
    return fn1;

    用float接就直接弄個const float去存
    用int接就要運算

  21. 引用通告: [轉載]學程式設計的人不能不看的好文章 – 國間的宏觀

  22. 引用通告: 不良的編寫程式習慣(引用的文章) – 聚興面對麵 炒麵,豬血湯 潭子店