首頁>Club>
3
回覆列表
  • 1 # 那年那些事er

    從上大學第一天開始接觸程式設計,老師便給我們講過各式各樣的演算法。從各種查詢、排序,到遞迴、貪心等演算法,大一的時候一直在和這些演算法搏鬥。直到工作後,為了應付面試,仍不得不回過頭去啃演算法書或者去刷一些演算法習題,才能夠拾回一些上學時的記憶。為什麼演算法就這麼難以記住呢?或者說,為何計算機的演算法不能更直觀一些呢?

    因為計算機的演算法就是反人性的,從本質上來說,這是計算機的思維方式和人腦思維方式的區別而造成的。

    人腦思維的機制至今沒有一個確定的理論,暫時認為是化學物質和電訊號的作用。雖然沒有科學的解釋,但是我們每個人都有一顆大腦,我們每個人都可以感受到自己的思維方式。

    而計算機則是人類創造的,從設計之初它便不是以模擬人腦為目的,因此它有其獨特的工作方式,只有理解了計算機的工作方式,才可以學會以它的方式去思考, 才可以寫出最適合計算機執行的程式程式碼。

    在排序陣列中尋找特定數字 —— 人腦 vs 計算機 round 1

    我們透過一個具體的例子,來說明人腦和計算機的思維方式不同,假設我們想要從一個已經排好序的陣列中找出一個特定的數字。

    已知排序好的陣列是1 2 3 5 7 13 34 67 90 127 308,我們希望找到是否13這個數在陣列內。

    人腦是如何去完成任務的呢?

    人腦處理這樣的問題幾乎是“作弊”的,我們可以一目十行,我們在眼鏡一掃視的情況下就發現了13,所以如果我問自己我是如何找到13的,我只能說我“看見”了。

    而計算機是如何來完成這個任務呢?

    最簡單也是最笨的演算法就是從陣列開始一個一個的讀入陣列,我相信每個學習過程式設計基礎的同學都可以寫出類似下面的程式碼。

    計算機需要從陣列的第一個元素開始,一個一個的去查當前的陣列的元素,和13相比,看看是不是相等。為了找出13這個數,計算機要做6次迴圈操作,而人幾乎是瞬間就看到了答案。

    為何計算機解決問題的方式這麼“笨”呢?我們先得從計算機的工作原理說起。

    CPU的工作方式

    CPU作為計算機的最核心的部件,也是演算法的主要運載體。

    CPU並不會像人一樣思考,它只懂得一些基本的指令。每一個CPU都有其指令集,指令集是儲存在CPU內部,對CPU運算進行指導和最佳化的硬程式。通俗一點說,指令集就是CPU的所有思維方式。比如常見的指令集中都會有ADD指令,這個指令可以將兩個暫存器中的值相加,並將儲存到另一個暫存器中;與此相對應的也會有SUB指令,用於將兩個暫存器值相減。如果你去查閱各種CPU指令集的手冊,會發現基本上都會包含基本的加減乘除指令,以及向記憶體中存、取資料的指令。而常見的CPU指令集,最多也就是幾百條指令。也就是說CPU只會這幾百個命令。

    而人腦相對於CPU,有強大的記憶和聯想能力,比如你看到1+1,就想到2,看到紅燈,就會想到停下來,看到門,就知道去開門把手,這些都是你不假思索可以立刻反映出來的東西。

    所以,CPU會的東西(指令)比人少多了,那CPU豈不是很笨?沒錯,CPU就是很笨,但是CPU的優點也是人腦所無法比擬的:

    雖然CPU只會幹簡單的事情(幾百種指令),但是它可以在固定的時間(指令執行時間)內保證正確的運算出正確的結果。而人腦不可能保證在固定的時間內一定產生“同樣”的思維結果。現代化的CPU工藝可以在一秒鐘內執行百萬次以上的指令,而人腦的思維速度則比不上,我們一個“念頭”最短也需要零點零幾秒的反應時間。

    綜上所述,CPU是一個既笨又快的傢伙。

    計算機儲存

    計算機的常見儲存有暫存器、快取記憶體、記憶體、硬碟等。

    暫存器就相當於人腦中立刻可以想起來的東西,CPU所做的一切運算都是針對於在暫存器中的資料進行的。暫存器儲存了計算機當前要做什麼計算(指令暫存器),要計算的資料(資料暫存器),計算到哪一步了(段暫存器)等資訊。無論是最早的有暫存器的CPU還是最新最強的的CPU,它們的暫存器數量最多也只有幾十個(特殊情況有幾百個),也就是說CPU同一時刻能夠立刻使用過的資訊也就是這幾十個數字。

    記憶體則是計算機的主力儲存設施,它可以儲存執行中的程式的資訊,記憶體相當於圖書館的書架,CPU需要用某一段記憶體中的資料是,需要透過LOAD指令,同時附上一個書架編號(記憶體地址),然後記憶體控制器可以將對應的地址的資料透過匯流排傳輸給CPU,CPU則將載入的結果放入暫存器中使用。記憶體存取的速度遠小於暫存器,但是訪問分佈在記憶體各個區間的資料的速度基本是相等的。

    由於大部分時候CPU需要讀取連續的一段記憶體來進行運算,因此通常CPU會有快取記憶體將最近使用過的記憶體整塊快取起來,而使得CPU不必每執行一步就需要去讀一次記憶體。快取記憶體的速度介於暫存器和記憶體之間,但遠高於記憶體。快取記憶體的大小一般在幾兆到十幾兆之間。

    硬碟屬於外部儲存,老式的機械硬碟中會有一個可轉的磁頭,在讀取磁碟檔案的時候需要將磁頭轉到對應的位置,磁碟的速度遠低於記憶體,並且如果磁碟的磁頭如果停留在某個位置時,隨機磁碟上不同位置的資訊,會受到磁頭運動的物理速度限制而出現速度不均等的情況。新式的固態硬碟採用了和記憶體相似的儲存介質,在隨機訪問的效能上提升很大。

    所以,計算機有一顆只能記得一點點事情的小腦袋(暫存器),但是能夠擁有相對較大的快速記憶(快取),擁有遠超過人類的知識儲備(記憶體),並且還隨身攜帶了巨大的移動圖書館(硬碟),所以從儲存上來看,計算機像是一個有先天缺陷的雨人(Rain Man)。

    所以,我們來分析一下round 1中為何計算機到底做了怎樣的操作?

    首先我們看我們函式的定義

    在呼叫函式的底層實現中,引數是被分配到兩個暫存器中。這個函式,在被呼叫時,第一個引數的值會被載入到暫存器(r1), 的第二個引數,傳入CPU的時候就只是在記憶體中的地址資訊,被儲存在另一個暫存器(r2)。

    而在第四行時,CPU需要做三件事才可以完成這工作:

    透過ADD指令,根據array的地址(r2)和i(r4)的數字,計算需要讀取的記憶體地址透過LOAD指令將記憶體地址對應的數載入到暫存器(r3)透過CMP指令比較num(r1)和r3的值,結果儲存在結果儲存器中

    而根據操作3的結果,如果結果不相等,則CPU需要將迴圈計數器加上1存入暫存器r4,再次進行上面的計算。所不同的是,第二到第N次的步驟二會比第一次要快很多,因為整個陣列的內容已經被快取記憶體所捕獲。

    所以,我們可以看出為何計算機在解決這個問題上顯得如此愚笨:

    計算機的輸入收到限制。計算機一次只能讀入單個值(有快取記憶體的幫助這並不太糟糕),且在暫存器中放有限的幾個值,而人類可以透過視覺等一次性讀入多個值儲存在腦海中。計算機的指令有限制,只能支援基本的運算指令。而人腦可以有豐富的指令,比如直接透過一堆剛剛看到的數字中視覺模式匹配出13這個數字。在排序陣列中尋找特定數字 —— 人腦 vs 計算機 round 2

    計算機在上一輪和人腦的PK中敗下陣來,然而這並不是很公平,因為陣列的數量只有短短的幾個,而計算機可以儲存的上限遠不止於如此。於是我們開始第二次的比拼。 這次我們將輸入擴大

    1 2 3 5 7 13 34 67 90 127 308 502 ... 2341245 ... (100萬個

    查詢的數變成了2341245。

    這次人腦和計算機的表現又如何呢?

    對於一個普通人,我們假設這100萬個數字是列印在一本字典裡的,那麼他如何找出100萬個有序陣列中的某個數字呢?

    這時人類引以自豪的“一目十行”的能力已經微乎其微,當數字的位數增大時,且不說一眼比較一個數字是否和目標數字相同已經困難,即使真的有一目十行的本事,在100萬這樣的數字面前也是微乎其微。

    於是乎,我們老老實實的去從頭到尾比較數字,一頁一頁的翻開,去看當前的頁中有沒有數字,沒有的話就去翻下一頁。

    這個思路是不是很熟悉?沒錯,這就是計算機的思維,和我們上一節中所描述的計算機編碼幾乎是一樣的,除了人可以一眼多看幾個資料外。

    然而,人類在比較大數是否相等的速度,以及翻字典的速度可遠遠比不上計算機去讀完這100萬個數的速度,同樣是“笨鳥”,計算機每秒百萬次的運算能力幾乎可以在瞬間就完成這樣的任務。

    也就是說,在大規模輸入的情況下,人腦的思維方式“退化”成和計算機近似,但是被計算機壓倒性的效能優勢給擊敗。

    在排序陣列中尋找特定數字 —— 人腦 vs 計算機 round 3

    在第二輪中,人腦敗給了計算機,但這樣的比拼無疑於兩隻笨鳥比誰更快。有沒有聰明一些的方法呢?

    沒錯,我們學過二分查詢(Binary Search)的演算法可以派上用場了。

    步驟一:有這麼有一本列印了100萬個數字的字典擺在我們的面前,我們不知道要找的數字會在哪裡,那麼我們先折半開啟字典(不用那麼精確也沒關係),看當前頁的第一個數字和最後一個數字,我們要找的數字是否在這個範圍內,如果在那麼我們可以繼續在當前頁找這個數字。

    步驟二:如果當前頁的第一個數字還是比我們要找的數字大,那麼我們可以將字典的後半部分撕了(因為我們要找的數字不可能在後半部分了),繼續上面的步驟。

    步驟三:如果當前頁的最後一個數字比我們要找的數字小,那麼我們可以將字典的前半部分撕了(理由同上),繼續步驟一。

    這樣我們會講這本字典越撕越薄,最壞的情況下我們會撕到最後一頁,這一頁要麼有這個數字,要麼沒有這個數字,但是我們保證按照上面的步驟進行我們不會錯過任何可能含有這個數字一頁。

    這個邏輯和計算機演算法中的二分查詢原理是一樣的,我們來看看實際的演算法程式碼是如何實現的

    我們可以看出,和人類的思維方式比,計算機不會翻“一頁”,它只會翻看一個數字,但是其他的思維方式是一模一樣的。利用這樣的演算法,人類雖然從結果上還是比計算機要慢,但是雙方都找到了最適合的方法,達到自我效率的最大提升。

    在排序陣列中尋找特定數字 —— 更多的思考

    那麼我們回過頭來看,為什麼我要假設這100萬數字列印在字典上呢?因為字典和計算機記憶體的模型很像。

    計算機可以透過記憶體地址來直接訪問記憶體,這一點和透過字典的頁碼來翻到某一頁,這一點是近似的。

    在計算機編碼中我們可以知道陣列的長度,而透過折半的方法找到中間的數,字典有厚度,我們可以透過厚度減半來找到中間的頁碼,這一點也是相似的。

    試想一樣,如果100萬的數字不是列印在字典,而是印在一條公路上,我們是否還可以用上一節的演算法來人肉二分查詢?答案是不可以,因為跑到公路的一半會消耗你很多的體力,如果採用二分法查詢比起round 1中的最笨辦法只會讓你耗費更多的體力。因為公路這一儲存的概念,對應的便不是記憶體的模型,而是磁帶(Tape)的模型,那麼對於這樣的模型,我相信不論是人或者是計算機, 都需要調整演算法,來達到最高的效率。

    總結

    透過以上的例子,我們可以看到,計算機的演算法反人性,是因為計算機不是一個“正常人”,它有自己的缺陷,也有自己的長處。很多時候我們覺的演算法不直觀,不是因為我們的思維能力比計算機差,而恰恰是因為作為人類我們同時接觸的資訊太多,所會的東西也太多而阻塞了我們的思維。那麼這種時候,不妨將自己“墮落”成一臺“鼠目寸光”和“所知甚少”的計算機,這時可能會有更清晰的思路。

  • 2 # 晨風依舊

    計算機思維建立的基礎是計算機處理的能力及其侷限性,不管是由人還是機器來執行。計算機方法和模型使我們有勇氣去解決問題,設計出無論哪個個人都無法獨立擔綱的系統。計算機思維面對著有關機器智慧的不解之謎:人做什麼比計算機強?計算機什麼比人好?最根本的問題是:什麼是可以計算機做的?今天,我們對這樣的問題仍然一知半解。計算機思維是每個人的基本技能,不只屬於計算機科學家。在閱讀,書寫和算術之外,應該把計算機科學加入每個兒童的分析能力培養。和出版社促進了3個R(閱讀,書寫和算術Reading, Writing & Arithmetic)的傳播相類似,計算機和使用電腦促進了計算機思維的傳播。計算機思維採納計算機科學的基本理念,可運用於問題的解決,系統設計和理解人類行為。計算機思維包含了一定範圍內的思維工具,反映出計算機科學領域的廣泛性。在解決一個問題時,我們會問:這有多難?怎樣做是最佳的方法?計算機思維站在堅實的理論地基上給予這樣的問題精確的答案。問題的難度要說取決於機器的能力-用來解決問題的計算工具。要考慮機器的指令,資源的約束和執行環境。為了有效率地解決問題,我們也許要進而問道,貌似的解決方案是不是最好的呢,我們可以隨機化優勢嗎,是否允許主動錯誤或者被動錯誤。計算機思維透過簡化,嵌入,轉換或者模擬,將看來困難的問題轉化為可以解決的問題。計算機思維是遞迴思維,並行處理。它將程式碼譯為資料,又將資料譯成程式碼。它用維度分析的泛化進行型別檢查。承認異化的優缺點。給某個人或物多個名字。它同時意識到間接定址和程式呼叫的代價和用處。它不只用正確程度和效率來評判一個程式,還判斷美感,系統設計的簡潔和優雅。計算機思維利用抽象和分解來對付複雜的大型任務或者來設計複雜的大型系統。它使你遠離擔憂。它挑出合適的代表性的問題或者給問題的相關方面建模使問題易於處理。它使用不變數來概要地或者陳述性地描述系統行為。它確信我們無需理解系統的每個細節就可以安全地使用,修改或者影響一個大型複雜的系統。它設想多個不同的使用者建立不同的模組,為了設想的未來的使用進行預載入或快取。計算機思維都以最糟糕的情形來考慮預防,保護和復原,方法可以是冗餘,容錯和糾錯。 它採取呼叫高壓封鎖,死鎖或者約定介面的方法。它還學習在發生同步相遇時避免競爭的情形。計算機思維使用啟發式推理找到解決之道。它在不確定的情況下進行計劃,學習和安排。它是搜尋,搜尋,再搜尋,找到一長列的網頁,贏得遊戲的攻略或是一個反例。它是使用大量的資料來提高計算的速度。它是在時間和空間中,在處理能力和儲存容量中找到平衡。來看這些生活中的事例:您女兒早上去上學,她把這一天要用的東西放到揹包裡;這就是預載入和快取。當您的兒子弄丟了他的手套,你建議他到經過的地方找;這是回溯。到什麼時候下您會自己買一套而不再租用滑雪用具呢?這是聯機演算法。在超市排哪條隊伍?這是伺服器系統的效能建模。為什麼停電時電話還可以用?這是失敗的獨立性和設計的冗餘。那麼如何進行用來分辨計算機和人的完全自動化的圖靈測試,即CAPTCHAS,人類模擬?;這是利用解決人工智慧的難題來給計算機代理商做宣傳的。計算機思維將植根於每個人的生活當中,那時演算法,前置條件等詞彙將成為每個人的詞彙, 非決定論和垃圾收集不再是計算機科學家使用的含義;人們將會從上往下來畫一棵樹。我們目睹了計算機思維對其他學科的影響。例如,機器學習改變了統計學。統計的學習正用於考察問題的規模, 以資料的大小和角度的方式,這在幾年前還是不能想像的。各種組織的統計部門都在招聘計算機科學家。計算機學校包圍了現有的和新成立的統計部門。計算機科學家近來對生物學產生了興趣,因為它們相信,生物學家將可以從計算機思維中獲益。計算機科學對於生物學的貢獻遠不止於可以透過大量搜尋序列資料來尋找圖譜。希望的是利用資料結構和演算法-計算機的抽象思維和方法, 透過闡述功能來表現出蛋白質的結構。計算機科學家正在改變生物學家的思維方式。 相似的,計算機遊戲理論正改變著經濟學家的思維方式。量子計算對物理學家也是。這樣的思維不會僅是其他科學家們的技能,它將是每個人的。普適計算的今天就是計算機思維的明天。昨天普適計算還是夢想,今天它已成為了現實。計算機思維在明天也會成為現實。是什麼,不是什麼計算機思維是研究計算的- 什麼是可以計算的,怎樣進行計算。因此,計算機思維有下面的特點:是概念化,不是程式設計計算機科學不是計算機程式設計。計算機科學家式的思維不是說給計算機程式設計。它要求在多個抽象層面進行思考。是基本技能,不是機械技能基本的技能是每個人在現代社會都必須學會運用的。機械則意味著機械的重複。具有諷刺意味的是,要是計算機科學家真解決了人工智慧的使計算機象人一樣思考的大挑戰,那時思維可就真要變機械了。是人的思維方式,不是計算機的計算機思維是人解決問題的方式,不是要人象計算機一樣思考。計算機是愚笨無趣的,人聰明富有想像力。是人類使得計算機令人振奮。使用計算機裝置,我們運用才智處理問題,那些在計算機時代之前我們不敢挑戰的問題,構建具有隻要想像得到的功能的系統。

  • 中秋節和大豐收的關聯?
  • 資料結構有哪些基本演算法?