简体   繁体   中英

Optimizing this dynamic programming solution

The Problem:

You are given an array m of size n , where each value of m is composed of a weight w , and a percentage p .

m = [m 0 , m 1 , m 2 , ... , m n ] = [[m 0 w , m 0 p ], [m 1 w , m 1 p ], [m 2 w , m 2 p ], ..., [m n w , m n p ] ]

So we'll represent this in python as a list of lists.

We are then trying to find the minimum value of this function:

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

where the only thing we can change about m is its ordering. (ie rearrange the elements of m in any way) Additionally, this needs to complete in better than O(n!) .


Brute Force Solution:

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)


Ideas On How To Optimize:

the idea:

Instead of finding the best order, see if we can find a way to compare two given values in m , when we know the state of the problem. (The code might explain this more clearly). If I can build this using a bottom-up approach (so, starting from the end, assuming I have no optimal solution) and I can create an equation that can compare two values in m and say one is definitively better than the other, then I can construct an optimal solution, by using that new value, and comparing the next set of values of m.

the code:

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)

However, this is not working as intended. The first value is always right, and roughly 60-75% of the time, the entire solution is optimal. However, in some cases, it looks like the way I am changing the value v which then gets passed back into my compare is evaluating much higher than it should. Here's the script I'm using to test against:

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])

Here's a particular test case demonstrating the error:

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

optimal solution (just the indices) = [1, 4, 3, 2, 0] my solution (just the indices) = [4, 3, 1, 2, 0]

It feels very close, but I cannot for the life of me figure out what is wrong. Am I looking at this the wrong way? Does this seem like it's on the right track? Any help or feedback would be greatly appreciated!

We don't need any information about the current state of the algorithm to decide which elements of m are better. We can just sort the values using the following key:

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

m.sort(key=key)

This requires explanation.

Suppose (w1, p1) is directly before (w2, p2) in the array. Then after processing these two items, t will be increased by an increment of w * (w1 + p1*w2) and w will be multiplied by a factor of p1*p2 . If we switch the order of these items, t will be increased by an increment of w * (w2 + p2*w1) and w will be multiplied by a factor of p1*p2 . Clearly, we should perform the switch if (w1 + p1*w2) > (w2 + p2*w1) , or equivalently after a little algebra, if w1/(1-p1) > w2/(1-p2) . If w1/(1-p1) <= w2/(1-p2) , we can say that these two elements of m are "correctly" ordered.

In the optimal ordering of m , there will be no pair of adjacent items worth switching; for any adjacent pair of (w1, p1) and (w2, p2) , we will have w1/(1-p1) <= w2/(1-p2) . Since the relation of having w1/(1-p1) <= w2/(1-p2) is the natural total ordering on the w/(1-p) values, the fact that w1/(1-p1) <= w2/(1-p2) holds for any pair of adjacent items means that the list is sorted by the w/(1-p) values.


Your attempted solution fails because it only considers what a pair of elements would do to the value of the tail of the array. It doesn't consider the fact that rather than using a low-p element now, to minimize the value of the tail, it might be better to save it for later, so you can apply that multiplier to more elements of m.


Note that the proof of our algorithm's validity relies on all p values being at least 0 and strictly less than 1. If p is 1, we can't divide by 1-p, and if p is greater than 1, dividing by 1-p reverses the direction of the inequality. These problems can be resolved using a comparator or a more sophisticated sort key. If p is less than 0, then w can switch sign, which reverses the logic of what items should be switched. Then we do need to know about the current state of the algorithm to decide which elements are better, and I'm not sure what to do then.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM