python小試牛刀: K-best演算法

飛凡可期發表於2020-11-04
"""
k-best解決的問題:
n個珠寶價值vi和重量wi;求保留k個珠寶的單位價值最大化
n,vi,wi 值域。。。
"""

def k_best(v_ls,w_ls,k):
    l,r = 0.0, 1e5 #maximun 10,0000

    while abs(r-l) > 1e-6:
        mid = (l + r)/2.0
        vw_ls = [v - mid * w for v,w in zip(v_ls, w_ls)]
        choose = list(range(len(v_ls)))
        for jj in range(k): #一個bug,序號會亂,因為排序後不;
            for i in range(jj+1, len(v_ls)):
                if vw_ls[jj] < vw_ls[i]:
                    vw_ls[jj], vw_ls[i] = vw_ls[i], vw_ls[jj]
                    choose[jj], choose[i] = choose[i], choose[jj]
        sk = sum(vw_ls[0:k])
        if sk < 0:
            r = mid
        else:
            l = mid
    return choose, mid

"""
        choose = []
        for jj in range(k): #一個bug,不應該設定為前幾個的;
            c = 0
            for i in range(jj+1,len(v_ls)):
                if vw_ls[jj] < vw_ls[i]:
                    vw_ls[jj], vw_ls[i] = vw_ls[i], vw_ls[jj]
                    c = i
            choose.append(c)
            """

if __name__ == "__main__":
    # n,k = input("輸入珠寶數目n,待選數目k:")
    # v_ls = []
    # w_ls = []
    # for i in range(int(n)):
    #     v,w = input("輸入珠寶價值,重量" ).split(',')#, input()
    #     v_ls.append(float(v))
    #     w_ls.append(float(w))

    v_ls = [3,40,1.5,10,8]
    w_ls = [1,20,1,5,4]
    k = 3
    print("list : ", v_ls, w_ls)
    chose, mid = k_best(v_ls,w_ls, k)
    print(chose, " value: ", mid)
    print("\nchoose k: ")
    for i in range(k):
        print(v_ls[chose[i]], w_ls[chose[i]])

result:

list : [3, 40, 1.5, 10, 8] [1, 20, 1, 5, 4]
[0, 4, 3, 1, 2] value: 2.1000007109250873

choose k:
3 1
8 4
10 5

comment

  • k-best目的是最優選取大集中的k個子集,是排序問題的進一步衍生。不單純能用排序解決,但是演算法也離不開排序。
  • 比如上述最大價效比的k個珠寶問題:如果按照vi/wi的單個價效比選取可能不是最優的。因為如上述(3,1),(40,20),(1.5,1),按照單個價效比依次是3,2,1.5,選前兩個;但是實際上最優的是1、3,因為第三個質量小,不如第二個佔重大,使得和比值過於偏向40,20了。
  • 值得注意的是,其中只涉及k個元素的排序,不必要排序所有,尤其集合n很大,選取k很小時候。複雜度O(kn),)(nn)會差異巨大。。。另外,可以用堆排序維持最小的k堆,或者隨機選擇來替代這裡的冒泡,複雜度降低到O(nlg(k)), O(k*lg(k)) ?; 因為k個元素的堆高度為lg(k)

相關文章