程序員的數學筆記3--迭代法

第三節課程,介紹的是迭代法。git

前兩節筆記的文章:程序員


03 迭代法

什麼是迭代法

迭代法,簡單來講,其實就是不斷地用舊的變量值,遞推計算新的變量值。github

這裏採用一個故事來介紹什麼是迭代法,這個故事是講述一個國王要重賞一個作出巨大貢獻的臣子,讓臣子提出他想獲得的賞賜,這個聰明的臣子說出了他想獲得的賞賜--在棋盤上放滿麥子,但要求是每一個格子的麥子數量都是前一個格子的兩倍。國王本覺得這個賞賜能夠垂手可得的知足,但真正開始放麥子後,發現即使是拿出全國的糧食也沒法知足的臣子的這個賞賜。算法

這裏咱們能夠用f(n)表示當前各自的麥子數量,而前一個格子的麥子數量就是f(n-1),那麼臣子的要求就能夠這麼表示:編程

f(n) = f(n-1) * 2
f(1) = 1
複製代碼

這也就是迭代法了,而若是用編程來實現,其實就是實現一個循環運算的過程。bash

用 Python 實現這個計算麥子的代碼以下所示:微信

def get_number_of_wheat(grid):
    ''' \計算放到給定格子數量須要的麥子數量 :param grid: 格子數 :return: '''
    # f(1) = 1
    wheat_numbers = 1

    sums = wheat_numbers
    for i in range(2, grid+1):
        wheat_numbers *= 2
        sums += wheat_numbers

    print('when grid = %d, wheats numbers = %d' % (grid, sums))

    return sums
複製代碼

簡單的測試例子:less

if __name__ == '__main__':
    print('compute numbers of wheat!')
    numbers_grid = 63
    get_number_of_wheat(numbers_grid)
    print('finish')
複製代碼

給定格子數量是 63 個,輸出結果以下:機器學習

compute numbers of wheat!
when grid = 63, wheats numbers = 9223372036854775807
finish
複製代碼

因此這個天文數字是 19 位數--9223372036854775807,真的是很是的多!假設一袋 50 斤的麥子估計有 130 萬粒麥子,那麼這個計算結果是至關於 70949 億袋 50 斤的麥子!編程語言

迭代法的應用

看完上述例子,相信應該對迭代法的基本概念比較瞭解了,而迭代法的基本步驟也很簡單,分爲三個步驟:

  • 肯定用於迭代的變量。上述例子中,這個迭代變量就是f(n)f(n-1)
  • 創建迭代變量之間的遞推關係。上述例子中,這個遞歸關係是f(n)=f(n-1)*2
  • 控制迭代的過程。這裏須要肯定迭代的初始條件和終止條件,上述例子,初始條件就是f(1)=1,而終止條件就是達到給定的格子數了。

那麼迭代法有什麼應用呢?

其實,它在數學和計算機領域都有很普遍的應用,如:

  • 求數值的精確或者近似解。典型的方法包括二分法(Bisection method)和牛頓迭代法(Newton's method);
  • 在必定範圍內查找目標值。典型方法包括二分查找,其實也是二分法在搜索方面的應用;
  • 機器學習算法中的迭代。好比 Kmeans 聚類算法(不斷迭代來對數據進行聚類)、馬爾科夫鏈(Markov chain)、梯度降低法(Gradient descent)等。迭代法在機器學習中有普遍的應用,實際上是由於機器學習的過程,就是根據已知數據和必定的假設,求一個局部最優解。迭代法能夠幫助學習算法逐步搜索,直到發現這種解。

接下來會重點介紹求數值的解和查找匹配記錄,這兩個應用其實都是採用二分法來實現。

求方程的精確或者近似解

迭代法除了用於計算龐大的數字,還能夠幫助咱們進行無窮次地逼近,求得方程的精確或者近似解

舉個例子,咱們要計算一個給定的正整數n(n>1)的平方根,而且不能採用編程語言自帶的函數,應該如何計算呢?

首先咱們能夠明確的是,對於給定的正整數n,它的平方根確定是小於它,但大於1,也就是這個平方根的取值範圍是 1 到 n ,在這個範圍內求一個數值的平方等於n

這裏就能夠經過採用剛剛說的二分法。每次查看區間內的中間值,檢查它是否符合標準。

好比咱們要求 10 的平方根,尋找的區間就是[1,10],第一個中間值就是(1+10)/2=11/2=5.5,而 5.5 的平方等於 30.25,明顯比 10 大,因此尋找區間變成 5.5 的左側,也就是[1, 5.5],中間值就是 3.25,但 3.25 的平方是 10.5625,依然大於 10,尋找區間變爲[1, 3.25],中間值變爲 2.125, 2.125 的平方是 4.515625,小於 10,因此區間就是[2.125, 3.25],這樣繼續尋找和計算中間值的平方,直到發現某個數的平方正好是 10。

具體步驟以下圖:

這裏用代碼實現,以下圖所示:

def get_square_root(n, threshold, max_try):
    ''' 計算大於 1 的正整數的平方根 :param n: 給定正整數 :param threshold: 偏差的閾值 :param max_try: 最大嘗試次數 :return: '''
    if n <= 1:
        return -1.0
    # interval boundary 區間的左右邊界
    left = 1.0
    right = float(n)
    for idx in range(max_try):
        # 防止溢出
        middle = left + (right - left) / 2
        square = middle * middle
        # 偏差
        delta = abs(square / n - 1)
        if delta <= threshold:
            return middle
        else:
            if square > n:
                right = middle
            else:
                left = middle

    return -2.0
複製代碼

簡單的測試例子:

square_root = get_square_root(10, 0.000001, 10000)
if square_root == -1.0:
    print('please input a number > 1')
elif square_root == -2.0:
    print('cannot find the square root')
else:
    print('square root==', square_root)
複製代碼

輸出結果是:

square root== 3.1622767448425293
複製代碼

這裏代碼中,設置了兩個控制迭代結束的參數:

  1. threshold:偏差的閾值,用於控制解的精度。理論上二分法能夠經過無限次迭代求到精確解,但實際應用還須要考慮時間和計算資源,因此通常咱們只須要一個近似解,而不須要徹底精確的數據;
  2. max_try:控制迭代的次數。設置這個參數也是爲了不使用while True循環可能致使的死循環,固然理論上設置了threshold是能夠避免死循環的,但這是一個良好的編程習慣,主動避免產生的可能性。
查找匹配記錄

二分法經過迭代式逼近,不只能夠求得方程的近似解,還能夠幫助查找匹配的記錄

這裏老師給的例子是在天然語言處理中,處理同義詞或者近義詞的擴展問題。這時,你是會有一個詞典,用於記錄每一個單詞的同義詞或者近義詞。對於一個待查找單詞,咱們須要在字典找到這個單詞,以及對應的全部同義詞和近義詞,而後進行拓展,例如對於單詞--西紅柿,它的同義詞包括了番茄tomato

詞典以下表格所示:

詞條 同義詞1 同義詞2 同義詞3
西紅柿 番茄 tomato ...
... ... ... ...

當處理文章的時候,遇到「西紅柿」這個單詞,就在字典裏查找,返回「番茄」和「tomato"等同義詞或者近義詞,並添加到文章做爲同義詞/近義詞的拓展。

這裏要解決的問題就是如何在字典查詢匹配單詞的問題。一種作法就是哈希表。而若是不用哈希表的方法,還能夠採用二分查找法。二分查找法進行字典查詢的思路以下:

  1. 對整個字典先進行排序(假設是從小到大)。二分法的一個關鍵前提條件就是所查找區間必須是有序的,這樣每次折半的時候,能夠知道是往左仍是右繼續查找。
  2. 使用二分法逐步定位到被查找的單詞。一樣是每次都選擇查找區間的中間值,判斷是否和待查找單詞一致,若是一致就返回;若是不一致,就進行判斷大小,若是比待查找單詞小,就須要往中間值右邊區間查找;不然就在左邊區間查找。
  3. 重複第二步操做,迭代式查找,直到找到單詞,或者沒有找到,就返回不存在。

相比於利用二分法查找方程解,二分查找必需要求數據是有序的!

用代碼實現以下:

def search_word(dictionary, word):
    ''' 查找匹配單詞 :param dictionary: 排序後的字典 :param word:待查找單詞 :return: '''
    if dictionary is None:
        return False
    if len(dictionary) < 1:
        return False

    left = 0
    right = len(dictionary) - 1
    while left <= right:
        middle = int(left + (right - left) / 2)
        if dictionary[middle] == word:
            return True
        else:
            if dictionary[middle] > word:
                right = middle - 1
            else:
                left = middle + 1

    return False

複製代碼

簡單的測試代碼:

print('find word in dictionary')
dict_list = ['i', 'am', 'coder']
dict_list = sorted(dict_list)
print('sorted dict:', dict_list)
word_to_find = 'am'
found = search_word(dict_list, word_to_find)
if found:
    print('word "%s" found in dictionary--%s!' % (word_to_find, dict_list))
else:
    print('cannot find the word "%s"' % word_to_find)
複製代碼

輸出結果:

find word in dictionary
sorted dict: ['am', 'coder', 'i']
word "am" found in dictionary--['am', 'coder', 'i']!
finish
複製代碼

迭代法的介紹就到這裏了!上述源代碼地址:

github.com/ccc013/Code…


歡迎關注個人微信公衆號--機器學習與計算機視覺,或者掃描下方的二維碼,你們一塊兒交流,學習和進步!

相關文章
相關標籤/搜索