某公司運籌優化崗位第一次面試

P問題, NP問題, NPC問題, NP-hard問題

  1. 基本概念
  • 複雜度級別: 1)多項式級別O(n^k);2)非多項式級別,如,指數級O(a^n)和階乘級別O(n!)。後者的複雜度不管如何都大於前者。
  • 歸約(約化):若是能找到這樣一個多項式變換法則,對任意一個程序A的輸入,都能按這個法則變換爲程序B的輸入,使兩程序的輸出相同,那麼咱們說,問題A可歸約爲問題B。
    • 通俗解釋:一個問題A能夠歸約爲問題B指,能夠用問題B的解法解決問題A,或者說,問題A能夠「變成」問題B。
    • 特色:「問題A可歸約爲問題B」有一個直觀意義,B的時間複雜度高於或者等於A的時間複雜度,既,問題A不比問題B難。
    • 性質:傳遞性。若是問題A能夠歸約爲問題B,問題B能夠歸約爲問題C,則問題A必定能夠歸約爲問題C。
  1. P問題, NP問題, NPC問題, NP-hard問題的定義和相互關係
  • P問題(polynomial):求解一個問題的時間複雜度是多項式級別
  • NP問題(nondeterministic polynomial):能夠在多項式時間裏驗證解是否正確的問題。定義NP問題的意義在於,若是一個問題不能在多項式時間驗證,則這個問題必定沒有多項式時間的算法。
    • 圖中某條路是不是Hamilton迴路,能夠在多項式時間驗證,是NP問題圖中
    • 是否不存在Hamilton迴路,不能夠在多項式時間驗證。
  • NPC問題(nondeterministic polynomial complete):
    • 定義:一個問題1)它是NP問題;2)全部的問題均可以約化到它,這樣的問題稱爲NPC問題。
    • 證實:1)先證實它是NP問題;2)再證實其中一個已知的NPC問題能約化到它(由約化的傳遞性,若是A能約化到B,則B的時間複雜度不低於A)。
    • 特色:NPC問題目前沒有多項式的有效解法,只能有指數級或階乘級複雜度的算法搜索
  • NP-hard問題(nondeterministic polynomial - hard):知足NPC問題的第2條可是不必定知足第1條。即便NPC問題得到了多項式級別的求解算法,NP-hard問題可能仍然找不到多項式級的算法。
  • 他們之間的關係:
    • P問題必定是NP問題,當前沒法證實NP問題是不是P問題。但廣泛認爲P≠NP。因而NP問題包含P問題。
    • NP問題能夠歸約爲NPC問題,因此NP問題包含NPC問題

參考資料:html

動態規劃解揹包問題

一維揹包問題

def dynamicAlgorithm_Knapscak(w, v, b):
    """用動態規劃方法求解揹包問題
    輸入:
        w: 物品的重量 dtype int
        v: 物品的價值 dtype int
        b: 揹包的重量
    輸出:
        x:最優解,各個物品是否裝入揹包
        max_value:裝入揹包的物品的重量
    """
    # 判斷異常
    if len(w)!=len(v):
        print('請檢查輸入')
        return -1,0
    
    # 邊界條件
    n = len(w)
    # F[i,j] 當揹包重量爲j,可取前i個物品時,可裝入物品的最大重量
    F = np.zeros((n+1, b+1), dtype=int) 
    # info[i,j],當揹包重量爲j,可取前i個物品時, 裝入的物品的最大標號
    info = np.zeros((n+1, b+1), dtype=int)  
    F[:,0] = 0
    F[0,:] = 0
    
    # 遞推 轉移方程:F_k(y) = F_k(y-x) + v_k
    for y in range(1,b+1):
        for k in range(1,n+1):
            F[k, y] = F[k-1, y]
            info[k, y] = info[k-1, y]
            if (y-w[k-1] >= 0) & ( F[k-1, y-w[k-1]]+v[k-1] > F[k, y]):
                F[k, y] = F[k-1,y-w[k-1]] + v[k-1]
                info[k, y] = k
                
    # 追蹤結果
    x = np.zeros(n, dtype=int)
    max_value = F[-1,-1]
    k = info[-1,-1]
    leftb = b
    while k > 0:
        x[k-1] = 1
        leftb = leftb - w[k-1]
        k = info[k-1, leftb]
    # 輸出
    return x, max_value
  • 測試用例
v = np.random.randint(1,100,1000,dtype=int)
w = np.random.randint(1,100,1000,dtype=int)
b = int(w.sum()*0.4)
x, max_value = dynamicAlgorithm_Knapscak(w, v, b)
print(x.sum(), max_value)

解二維揹包問題

def dynamicAlgorithm_Knapscak(w, v, b):
    """
    w: 物品的重量和體積 多維數組,dtype int
    v: 物品的價值 dtype int
    b: 最大致積和最大重量
    F_k(y) = F_k(y-x) + v_k
    """
    if len(w[0])!=len(v):
        print('請檢查輸入')
        return -1,0
    
    # 邊界條件
    n = len(v)
    F = np.zeros((n+1, b[0]+1, b[1]+1), dtype=int)
    info = np.zeros((n+1, b[0]+1, b[1]+1), dtype=int) # 裝入的物品的最大標號
    
    # 遞推
    for k in range(1,n+1):
        for y1 in range(1,b[0]+1):
            if (y1-w[0,k-1] >= 0):
                for y2 in range(1,b[1]+1):
                    F[k, y1, y2] = F[k-1, y1, y2]
                    info[k, y1, y2] = info[k-1, y1, y2]
                    if (y2-w[1,k-1] >= 0):
                        if F[k-1, y1-w[0,k-1], y2-w[1,k-1]] + v[k-1] > F[k, y1, y2]:
                            F[k, y1, y2] = F[k-1, y1-w[0,k-1], y2-w[1,k-1]]+v[k-1]
                            info[k, y1, y2] = k
            else:
                F[k, y1, :] = F[k-1, y1, :]
                info[k, y1, :] = info[k-1, y1, :]
            
    # 追蹤結果
    x = np.zeros(n, dtype=int)
    max_value = F[-1, -1, -1]
    k = info[-1, -1, -1]
    leftw = b[0]
    leftc = b[1]
    while k > 0:
        x[k-1] = 1
        leftw = leftw - w[0, k-1]
        leftc = leftc - w[1, k-1]
        k = info[k-1, leftw, leftc]
    # 輸出
    return x, max_value
  • 測試用例
w = np.random.randint(1,60,(2,1000),dtype=int)
b = [120,120]
v = np.random.randint(2,5000,1000,dtype=int)
x, max_value = dynamicAlgorithm_Knapscak(w, v, b)
print('x = :',x, '\n max_value=', max_value)
  • 僞多項式變換時間算法近似求解揹包問題(屈婉玲: 算法設計與分析p262)
    • 對偶 + 讓全部的物品的重量縮小必定倍數,並取整

參考資料:python

  • 屈婉玲: 算法設計與分析

求一個向量中的第k小的元素

def partition(nums,l,r):
        k = random.randint(l+1,r)
        nums[l], nums[k] = nums[k], nums[l]
        i = l+1    # [l+1, i) <= nums[l]
        j = r    # (j, r] > nums[l]
        while True:
            while (i <= r) and (nums[i] < nums[l]):
                i +=1
            while (j >= l+1) and nums[j] > nums[l]:
                j -=1
            if i > j:
                break
            nums[i], nums[j] = nums[j], nums[i]
            i +=1
            j -=1
        nums[l],nums[j] = nums[j], nums[l]
        return j

def findKthMinest(nums, kth):
    l = 0; r = len(nums)-1
    while True:
        mid = partition(nums, l, r)
        if mid+1 == kth:
            return nums[mid]
        elif mid+1 < kth:
            l = mid+1
            kth = kth-(mid+1)
        else:
            r = mid

參考資料:算法

整數規劃的求解方法總結

精確解法中遺漏了分支訂價算法,入門列生成法和分支訂價算法能夠閱讀羅納德《運籌學》(肖永波譯)第二版第13章 參考資料:數組

相關文章
相關標籤/搜索