数学,离一个程序员有多近?
作者:小傅哥
博客:https://bugstack.cn
沉淀、分享、成長,讓自己和他人都能有所收獲!😄
一、前言
數(shù)學(xué)離程序員有多近?
ifelse也好、for循環(huán)也罷,代碼可以說就是對數(shù)學(xué)邏輯的具體實現(xiàn)。所以敲代碼的程序員幾乎就離不開數(shù)學(xué),難易不同而已。
那數(shù)學(xué)不好就寫不了代碼嗎😳?不,一樣可以寫代碼,可以寫出更多的CRUD出來。那你不要總覺得是產(chǎn)品需求簡單所以你的實現(xiàn)過程才變成了增刪改查,往往也是因為你還不具備可擴展、易維護、高性能的代碼實現(xiàn)方案落地能力,才使得你小小年紀(jì)寫出了更多的CRUD!
與一錐子買賣的小作坊相比,大廠和超級大廠更會注重數(shù)學(xué)能力。
2004年,在硅谷的交通動脈 101 公路上突然出現(xiàn)一塊巨大的廣告牌,上面是一道數(shù)學(xué)題:{e 的連續(xù)數(shù)字中最先出現(xiàn)的 10 位質(zhì)數(shù)}.com。
廣告:這里的 e 是數(shù)學(xué)常數(shù),自然對數(shù)的底數(shù),無限不循環(huán)小數(shù)。這道題的意思就是,找出 e 中最先出現(xiàn)的 10 位質(zhì)數(shù),然后可以得出一個網(wǎng)址。進入這個網(wǎng)址會看到 Google 為你出的第二道數(shù)學(xué)題,成功解鎖這步 Google 會告訴你,我們或許是”志同道合“的人,你可以將簡歷發(fā)到這個郵箱,我們一起做點改變世界的事情。
計算 e 值可以通過泰勒公式推導(dǎo)出來:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推導(dǎo)計算過程還包括埃拉托色尼篩選法(the Sieve of Eratosthenes)、線性篩選法的使用。感興趣的小伙伴可以用代碼實現(xiàn)下。
二、把代碼寫好的四步
業(yè)務(wù)提需求、產(chǎn)品定方案、研發(fā)做實現(xiàn)。最終這個系統(tǒng)開發(fā)的怎么樣是由三方共同決定的!
- 地基挖的不好,樓就蓋不高
- 磚頭擺放不巧,樓就容易倒
- 水電走線不妙,樓就危險了
- 格局設(shè)計不行,樓就賣不掉
這里的地基、磚頭、水電、格局,對應(yīng)的就是,數(shù)據(jù)結(jié)構(gòu)、算法邏輯、設(shè)計模式、系統(tǒng)架構(gòu)。從下到上相互依賴、相互配合,只有這一層做好,下一層才好做!
- 數(shù)據(jù)結(jié)構(gòu):高矮胖瘦、長寬扁細(xì),數(shù)據(jù)的存放方式,是一套程序開發(fā)的核心基礎(chǔ)。不合理的設(shè)計往往是從數(shù)據(jù)結(jié)構(gòu)開始,哪怕你僅僅是使用數(shù)據(jù)庫存放業(yè)務(wù)信息,也一樣會影響到將來各類數(shù)據(jù)的查詢、匯總等實現(xiàn)邏輯的難易。
- 算法邏輯:是對數(shù)據(jù)結(jié)構(gòu)的使用,合適的數(shù)據(jù)結(jié)構(gòu)會讓算法實現(xiàn)過程降低時間復(fù)雜度。可能你現(xiàn)在的多層for循環(huán)在合適的算法過程下,能被優(yōu)化為更簡單的方式獲取數(shù)據(jù)。注意:算法邏輯實現(xiàn),并不一定就是排序、歸并,還有你實際業(yè)務(wù)的處理流程。
- 設(shè)計模式:可以這么說,不使用設(shè)計模式你一樣能寫代碼。但你愿意看到滿屏幕的ifelse判斷調(diào)用,還是喜歡像膏藥一樣的代碼,粘貼來復(fù)制去?那么設(shè)計模式這套通用場景的解決方案,就是為你剔除掉代碼實現(xiàn)過程中的惡心部分,讓整套程序更加易維護、易擴展。就是開發(fā)完一個月,你看它你還認(rèn)識!
- 系統(tǒng)架構(gòu):描述的是三層MVC,還是四層DDD。我對這個的理解就是家里的三居還是四局格局,MVC是我們經(jīng)常用的大家都熟悉,DDD無非就是家里多了個書房,把各自屬于哪一個屋子的擺件規(guī)整到各自屋子里。那么亂放是什么效果呢,就是自動洗屁屁馬桶🚽給按到廚房了,再貴也格楞子! 好,那么我們在延展下,如果你的衛(wèi)生間沒有流出下水道咋辦?是不這個地方的數(shù)據(jù)結(jié)構(gòu)就是設(shè)計缺失的,而到后面再想擴展就難了吧!
所以,研發(fā)在承接業(yè)務(wù)需求、實現(xiàn)產(chǎn)品方案的時候。壓根就不只是在一個房子的三居或者四居格局里,開始隨意碼磚。
沒有合理的數(shù)據(jù)結(jié)構(gòu)、沒有優(yōu)化的算法邏輯、沒有運用的設(shè)計模式,最終都會影響到整個系統(tǒng)架構(gòu)變得臃腫不堪,調(diào)用混亂。在以后附加、迭代、新增的需求下,會讓整個系統(tǒng)問題不斷的放大,當(dāng)你想用重構(gòu)時,就有著千絲萬縷般調(diào)用關(guān)系。 重構(gòu)就不如重寫了!
三、for循環(huán)沒算法快
在《編程之美》一書中,有這樣一道題。求:1n中,1出現(xiàn)的次數(shù)。比如:110,1出現(xiàn)了兩次。
1. for 循環(huán)實現(xiàn)
long startTime = System.currentTimeMillis(); int count = 0; for (int i = 1; i <= 10000000; i++) {String str = String.valueOf(i);for (int j = 0; j < str.length(); j++) {if (str.charAt(j) == 49) {count++;}} } System.out.println("1的個數(shù):" + count); System.out.println("計算耗時:" + (System.currentTimeMillis() - startTime) + "毫秒");使用 for 循環(huán)的實現(xiàn)過程很好理解,就是往死了循環(huán)。之后把循環(huán)到的數(shù)字按照字符串拆解,判斷每一位是不是數(shù)字,是就+1。這個過程很簡單,但是時間復(fù)雜很高。
2. 算法邏輯實現(xiàn)
如圖 20-3 所示,其實我們能發(fā)現(xiàn)這個1的個數(shù)在100、1000、10000中是有規(guī)則的循環(huán)出現(xiàn)的。11、12、13、14或者21、31、41、51,以及單個的1出現(xiàn)。最終可以得出通用公式:abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...,abcd代表位數(shù)。另外在實現(xiàn)的過程還需要考慮比如不足100等情況,例如98、1232等。
實現(xiàn)過程
long startTime = System.currentTimeMillis(); int num = 10000000, saveNum = 1, countNum = 0, lastNum = 0; int copyNum = num; while (num != 0) {lastNum = num % 10;num /= 10;if (lastNum == 0) {// 如果是0那么正好是少了一次所以num不加1了countNum += num * saveNum;} else if (lastNum == 1) {// 如果是1說明當(dāng)前數(shù)內(nèi)少了一次所以num不加1,而且當(dāng)前1所在位置// 有1的個數(shù),就是去除當(dāng)前1最高位,剩下位數(shù),的個數(shù)。countNum += num * saveNum + copyNum % saveNum + 1;} else {// 如果非1非0.直接用公式計算// abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...countNum += (num + 1) * saveNum;}saveNum *= 10; } System.out.println("1的個數(shù):" + countNum); System.out.println("計算耗時:" + (System.currentTimeMillis() - startTime) + "毫秒");在《編程之美》一書中還不只這一種算法,感興趣的小伙伴可以查閱。但自己折騰實現(xiàn)后的興奮感更強哦!
3. 耗時曲線對比
按照兩種不同方式的實現(xiàn)邏輯,我們來計算1000、10000、10000到一個億,求1出現(xiàn)的次數(shù),看看兩種方式的耗時曲線。
- for循環(huán)隨著數(shù)量的不斷增大后,已經(jīng)趨近于無法使用了。
- 算法邏輯依靠的計算公式,所以無論增加多少基本都會在1~2毫秒內(nèi)計算完成。
那么,你的代碼中是否也有類似的地方。如果使用算法邏輯配合適合的數(shù)據(jù)結(jié)構(gòu),是否可以替代一些for循環(huán)的計算方式,來使整個實現(xiàn)過程的時間復(fù)雜度降低。
四、Java中的算法運用
在 Java 的 JDK 實現(xiàn)中有很多數(shù)學(xué)知識的運用,包括數(shù)組、鏈表、紅黑樹的數(shù)據(jù)結(jié)構(gòu)以及相應(yīng)的實現(xiàn)類ArrayList、Linkedlist、HashMap等。當(dāng)你深入的了解這些類的實現(xiàn)后,會發(fā)現(xiàn)它們其實就是使用代碼來實現(xiàn)數(shù)學(xué)邏輯而已。就像你使用數(shù)學(xué)公式來計算數(shù)學(xué)題一樣
接下來小傅哥就給你介紹幾個隱藏在我們代碼中的數(shù)學(xué)知識。
1. HashMap的擾動函數(shù)
未使用擾動函數(shù)
已使用擾動函數(shù)
擾動函數(shù)公式
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }- 描述:以上這段代碼是HashMap中用于獲取hash值的擾動函數(shù)實現(xiàn)代碼。HashMap通過哈希值與桶定位坐標(biāo) 那么直接獲取哈希值就好了,這里為什么要做一次擾動呢?
- 作用:為了證明擾動函數(shù)的作用,這里選取了10萬單詞計算哈希值分布在128個格子里。之后把這128個格子中的數(shù)據(jù)做圖表展示。從實現(xiàn)數(shù)據(jù)可以看到,在使用擾動函數(shù)后,曲線更加平穩(wěn)了。那么,也就是擾動后哈希碰撞會更小。
- 用途:當(dāng)你有需要把數(shù)據(jù)散列分散到不同格子或者空間時,又不希望有太嚴(yán)重的碰撞,那么使用擾動函數(shù)就非常有必要了。比如你做的一個數(shù)據(jù)庫路由,在分庫分表時也是盡可能的要做到散列的。
2. 斐波那契(Fibonacci)散列法
- 描述:在 ThreadLocal 類中的數(shù)據(jù)存放,使用的是斐波那契(Fibonacci)散列法 + 開放尋址。之所以使用斐波那契數(shù)列,是為了讓數(shù)據(jù)更加散列,減少哈希碰撞。具體來自數(shù)學(xué)公式的計算求值,公式:f(k) = ((k * 2654435769) >> X) << Y對于常見的32位整數(shù)而言,也就是 f(k) = (k * 2654435769) >> 28
- 作用:與 HashMap 相比,ThreadLocal的數(shù)據(jù)結(jié)構(gòu)只有數(shù)組,并沒有鏈表和紅黑樹部分。而且經(jīng)過我們測試驗證,斐波那契散列的效果更好,也更適合 ThreadLocal。
- 用途:如果你的代碼邏輯中需要存儲類似 ThreadLocal 的數(shù)據(jù)結(jié)構(gòu),又不想有嚴(yán)重哈希碰撞,那么就可以使用 斐波那契(Fibonacci)散列法。其實除此之外還有,除法散列法、平方散列法、隨機數(shù)法等。
3. 梅森旋轉(zhuǎn)算法(Mersenne twister)
// Initializes mt[N] with a simple integer seed. This method is // required as part of the Mersenne Twister algorithm but need // not be made public. private final void setSeed(int seed) {// Annoying runtime check for initialisation of internal data// caused by java.util.Random invoking setSeed() during init.// This is unavoidable because no fields in our instance will// have been initialised at this point, not even if the code// were placed at the declaration of the member variable.if (mt == null) mt = new int[N];// ---- Begin Mersenne Twister Algorithm ----mt[0] = seed;for (mti = 1; mti < N; mti++) {mt[mti] = (MAGIC_FACTOR1 * (mt[mti-1] ^ (mt[mti-1] >>> 30)) + mti);}// ---- End Mersenne Twister Algorithm ---- }梅森旋轉(zhuǎn)算法(Mersenne twister)是一個偽隨機數(shù)發(fā)生算法。由松本真和西村拓士在1997年開發(fā),基于有限二進制字段上的矩陣線性遞歸。可以快速產(chǎn)生高質(zhì)量的偽隨機數(shù),修正了古典隨機數(shù)發(fā)生算法的很多缺陷。 最為廣泛使用Mersenne Twister的一種變體是MT19937,可以產(chǎn)生32位整數(shù)序列。
- 描述:梅森旋轉(zhuǎn)算法分為三個階段,獲得基礎(chǔ)的梅森旋轉(zhuǎn)鏈、對于旋轉(zhuǎn)鏈進行旋轉(zhuǎn)算法、對于旋轉(zhuǎn)算法所得的結(jié)果進行處理。
- 用途:梅森旋轉(zhuǎn)算法是R、Python、Ruby、IDL、Free Pascal、PHP、Maple、Matlab、GNU多重精度運算庫和GSL的默認(rèn)偽隨機數(shù)產(chǎn)生器。從C++11開始,C++也可以使用這種算法。在Boost C++,Glib和NAG數(shù)值庫中,作為插件提供。
五、程序員數(shù)學(xué)入門
與接觸到一個有難度的知識點學(xué)起來辛苦相比,是自己不知道自己不會什么!就像上學(xué)時候老師說,你不會的就問我。我不會啥?我從哪問?一樣一樣的!
代碼是對數(shù)學(xué)邏輯的實現(xiàn),簡單的邏輯調(diào)用關(guān)系是很容易看明白的。但還有那部分你可能不知道的數(shù)學(xué)邏輯時,就很難看懂了。比如:擾動函數(shù)、負(fù)載因子、斐波那契(Fibonacci)等,這些知識點的學(xué)習(xí)都需要對數(shù)學(xué)知識進行驗證,否則也就學(xué)個概念,背個理論。
書到用時方恨少,在下還是個寶寶!
那如果你想深入的學(xué)習(xí)下程序員應(yīng)該會的數(shù)學(xué),推薦給你一位科技博主 Jeremy Kun 花了4年時間,寫成一本書 《程序員數(shù)學(xué)入門》。
這本書為程序員提供了大量精簡后數(shù)學(xué)知識,包括:多項式、集合、圖論、群論、微積分和線性代數(shù)等。同時在wiki部分還包括了抽象代數(shù)、離散數(shù)學(xué)、傅里葉分析和拓?fù)鋵W(xué)等。
作者表示,如果你本科學(xué)過一些數(shù)學(xué)知識,那么本書還是挺適合你的,不會有什么難度。書中的前三章是基礎(chǔ)數(shù)學(xué)內(nèi)容,往后的難度依次遞增。
- 書籍獲取:關(guān)注公眾號:bugstack蟲洞棧,回復(fù):程序員數(shù)學(xué),下載這本書
- 在線Wiki:https://jeremykun.com/primers/
六、總結(jié)
- Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. https://www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD498.html
- 單純的只會數(shù)學(xué)寫不了代碼,能寫代碼的不懂?dāng)?shù)學(xué)只能是CRUD碼農(nóng)。數(shù)學(xué)知識幫助你設(shè)計數(shù)據(jù)結(jié)構(gòu)和實現(xiàn)算法邏輯,代碼能力幫你駕馭設(shè)計模式和架構(gòu)模型。多方面的知識結(jié)合和使用才是碼農(nóng)和工程師的主要區(qū)別,也是是否擁有核心競爭力的關(guān)鍵點。
- 學(xué)習(xí)知識有時候看不到前面的路有多遠(yuǎn),但哪怕是個泥坑,只要你不停的蠕動、折騰、翻滾,也能抓出一條泥鰍。知識的路上是發(fā)現(xiàn)知識的快樂,還學(xué)會知識的成就感,不斷的促使你前行。
七、系列推薦
- 互聯(lián)網(wǎng)大廠,線上研發(fā)事故總結(jié)!
- 碼德,這不就是產(chǎn)品給我留的數(shù)學(xué)作業(yè)!
- HashMap核心知識,擾動函數(shù)、負(fù)載因子、擴容鏈表拆分,深度學(xué)習(xí)
- Netty實戰(zhàn),1比1仿桌面版微信聊天
總結(jié)
以上是生活随笔為你收集整理的数学,离一个程序员有多近?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大话设计模式(更新ing...)
- 下一篇: WIn 10 企业版激活