优化这种动态编程解决方案

时间:2015-07-02 04:10:24

标签: python algorithm python-2.7 dynamic-programming

问题:

您将获得一个大小为 n 的数组 m ,其中 m 的每个值都由权重 w ,和百分比 p

m = [m0, m1, m2, ... , mn] = [[m0w, m0p], [m1w, m1p], [m2w, m2p], ..., [mnw, mnp] ]

因此我们将在python中将其表示为列表列表。

然后我们试图找到这个函数的最小值:

def minimize_me(m):
    t = 0
    w = 1
    for i in range(len(m)):
        current = m[i]
        t += w * current[0]
        w *= current[1]
    return t

我们唯一能改变的关于 m 的是它的排序。 (即以任何方式重新排列 m 的元素)此外,这需要比 O(n!)更好地完成。


暴力解决方案:

import itertools
import sys

min_t = sys.maxint
min_permutation = None

for permutation in itertools.permutations(m):
    t = minimize_me(list(permutation), 0, 1)
    if t < min_t:
        min_t = t
        min_permutation = list(permutation)


如何优化的想法:

这个想法:

当我们知道问题的状态时,看看我们是否能找到一种比较 m 中两个给定值的方法,而不是找到最佳顺序。 (代码可能会更清楚地解释这一点)。如果我可以使用自下而上的方法构建它(所以,从最后开始,假设我没有最优解)我可以创建一个方程,可以比较 m 中的两个值,并说一个是明确地优于另一个,然后我可以通过使用新值并比较下一组m值来构建最优解。

代码:

import itertools

def compare_m(a, b, v):
    a_first = b[0] + b[1] * (a[0] + a[1] * v)
    b_first = a[0] + a[1] * (b[0] + b[1] * v)

    if a_first > b_first:
        return a, a_first
    else:
        return b, b_first

best_ordering = []
v = 0

while len(m) > 1:
    best_pair_t = sys.maxint
    best_m = None

    for pair in itertools.combinations(m, 2):
        m, pair_t = compare_m(pair[0], pair[1], v)
        if pair_t < best_pair_t:
            best_pair_t = pair_t
            best_m = m

    best_ordering.append(best_m)
    m.remove(best_m)
    v = best_m[0] + best_m[1] * v

first = m[0]
best_ordering.append(first)

但是,这不符合预期。第一个值始终是正确的,大约60-75%的时间,整个解决方案是最佳的。但是,在某些情况下,它看起来像我改变值 v 的方式,然后传递回我的比较,评估要高得多。这是我用来测试的脚本:

import random

m = []
for i in range(0, 5):
    w = random.randint(1, 1023)
    p = random.uniform(0.01, 0.99)
    m.append([w, p])

这是一个展示错误的特定测试用例:

m = [[493, 0.7181996086105675], [971, 0.19915848527349228], [736, 0.5184210526315789], [591, 0.5904761904761905], [467, 0.6161290322580645]]

最优解(仅指数)= [1,4,3,2,0] 我的解决方案(只是指数)= [4,3,1,2,0]

感觉非常接近,但我不能为我的生活弄清楚出了什么问题。我是以错误的方式看待这个吗?这看起来好像是在正确的轨道上吗?任何帮助或反馈将不胜感激!

1 个答案:

答案 0 :(得分:6)

我们不需要有关算法当前状态的任何信息来决定m哪些元素更好。我们可以使用以下键对值进行排序:

def key(x):
    w, p = x
    return w/(1-p)

m.sort(key=key)

这需要解释。

假设(w1, p1)直接位于数组中的(w2, p2)之前。然后,处理完这两项后,t将增加w * (w1 + p1*w2)w将增加p1*p2因子。如果我们切换这些项目的顺序,t将增加w * (w2 + p2*w1)w将增加p1*p2因子。显然,我们应该在(w1 + p1*w2) > (w2 + p2*w1)时执行切换,或者在w1/(1-p1) > w2/(1-p2)之后执行等效的小代数。如果w1/(1-p1) <= w2/(1-p2),我们可以说m的这两个要素是&#34;正确&#34;排序。

m的最佳排序中,没有值得切换的相邻项目对;对于任何相邻的(w1, p1)(w2, p2)对,我们将w1/(1-p1) <= w2/(1-p2)。由于具有w1/(1-p1) <= w2/(1-p2)的关系是w /(1-p)值的自然总排序,w1/(1-p1) <= w2/(1-p2)对任何一对相邻项保持的事实意味着列表按w排序/(1-p)值。

您尝试的解决方案失败了,因为它只考虑了一对元素对数组尾部值的作用。它没有考虑这样的事实,即现在不是使用低p元素来最小化尾部的值,最好将其保存以供以后使用,因此可以将该乘数应用于m的更多元素。

请注意,我们算法有效性的证据依赖于所有p值至少为0且严格小于1.如果p为1,我们不能除以1-p ,如果p大于1,除以1-p就会逆转不等式的方向。可以使用比较器或更复杂的排序键来解决这些问题。如果p小于0,则w可以切换符号,这反转了应该切换的项的逻辑。那么我们需要知道算法的当前状态,以决定哪些元素更好,我不知道该怎么做。