最小化加权和

时间:2014-02-19 09:13:57

标签: algorithm data-structures greedy

我最近遇到过这个问题。 假设x轴上有n个点,x [0],x [1] .. x [n-1]。 让与这些点中的每一个相关联的权重为w [0],w [1] ... w [n-1]。 从0到n-1之间的任何点开始,目标是覆盖所有点,使得w [i] * d [i]的总和最小化,其中d [i]是到达第i个点所覆盖的距离。起点。

例:
假设点数为:1 5 10 20 40
假设权重为:1 2 10 50 13
如果我选择从第10点开始并选择移动到点20然后转到5然后转到40然后最终转到1,那么加权和将变为10 * 0 + 50 *(10)+ 2 *(10 + 15) + 13 *(10 + 15 + 35)+ 1 *(10 + 15 + 35 + 39)。

我试图通过贪婪的方法解决它,从具有最大相关权重的点开始并移动到第二个最大权重点,依此类推。但算法不起作用。有人可以指点一下解决这个问题必须采取的方法吗?

3 个答案:

答案 0 :(得分:3)

有一个非常重要的事实导致多项式时间算法:

由于点位于某个轴上,因此它们会生成路径图,这意味着对于每3个顶点v1,v2,v3,如果v2介于v1v3之间,然后v1v3之间的距离等于v1v2之间的距离加上v2v3之间的距离。因此,如果我们从v1开始,那么obj。首先转到v2然后转到v3的路径的函数值将始终小于首先转到v3然后回到v2的路径的值这是因为:

value of the first path = w[2]*D(v1,v2)+W[3]*(D(v1,v2)+D(v2,v3))

value of the second path = w[3]*D(v1,v3)+W[2]*((v1,v3)+D(v3,v2)) = w[3]*D(v1,v2)+w[3]*D(v2,v3)+w[2]*(D(v1,v2)+2*D(v3,v2))

如果我们从第二个路径值中减去第一个路径值,除非你考虑负权重,否则我们会留下等于或大于0的w[2]*2*D(v3,v2)

所有这些意味着,如果我们位于某一点,我们应该考虑的选项总是只有2个:转到左边最近的未访问点或右边最近的未访问点。

这非常重要,因为它为我们留下了2^n个可能的路径,而不是n!个可能的路径(就像旅行推销员问题一样)。

在路径图上求解TSP /最小权重哈密顿路径可以使用动态编程在多项式时间内完成,您应该应用完全相同的方法,但修改计算目标函数的方式。

由于你不知道起始顶点,你必须运行这个算法n时间,每次从不同的顶点开始,这意味着运行时间将乘以n

答案 1 :(得分:0)

也许你应该详细说明算法“不起作用”的意思。你描述的贪婪方法的基本思想对我来说似乎是可行的。你是说贪婪的方法不一定能找到最佳解决方案吗?正如评论中指出的那样,这个可能是一个NP完全问题 - 但是,可以肯定的是,必须进一步分析它:一些动态编程,也许是一些前缀和距离计算也可以导致多项式时间解决方案。

我在Java中快速实现了贪婪的解决方案(希望我能正确理解所有内容......)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MinWeightSum
{
    public static void main(String[] args)
    {
        double x[] = { 1, 5, 10, 20, 40 };
        double w[] = { 1, 2, 10, 50, 13 };

        List<Integer> givenIndices = Arrays.asList(2, 3, 1, 4, 0);
        Path path = createPath(x, w, givenIndices);
        System.out.println("Initial result "+path.sum);

        List<Integer> sortedWeightIndices =
            computeSortedWeightIndices(w);
        Path greedyPath = createPath(x, w, sortedWeightIndices);
        System.out.println("Greedy result  "+greedyPath.sum);

        System.out.println("For "+sortedWeightIndices+" sum "+greedyPath.sum);
   }

    private static Path createPath(
        double x[], double w[], List<Integer> indices)
    {
        Path path = new Path(x, w);
        for (Integer i : indices)
        {
            path.append(i);
        }
        return path;
    }


    private static List<Integer> computeSortedWeightIndices(final double w[])
    {
        List<Integer> indices = new ArrayList<Integer>();
        for (int i=0; i<w.length; i++)
        {
            indices.add(i);
        }
        Collections.sort(indices, new Comparator<Integer>()
        {
            @Override
            public int compare(Integer i0, Integer i1)
            {
                return Double.compare(w[i1], w[i0]);
            }
        });
        return indices;
    }

    static class Path
    {
        double x[];
        double w[];

        int prevIndex = -1;
        double distance;
        double sum;

        Path(double x[], double w[])
        {
            this.x = x;
            this.w = w;
        }

        void append(int index)
        {
            if (prevIndex != -1)
            {
                distance += Math.abs(x[prevIndex]-x[index]);
            }
            sum += w[index] * distance;
            prevIndex = index;
        }
    }
}

您在示例中描述的索引序列产生了解决方案

For [2, 3, 1, 4, 0] sum 1429.0

你描述的贪婪方法给出了

For [3, 4, 2, 1, 0] sum 929.0

最佳解决方案是

For [3, 2, 4, 1, 0] sum 849.0 

我通过检查索引的所有排列找到了(当然,这对于较大的n是不可行的)

答案 2 :(得分:0)

假设你是解决问题的一部分,并且到目前为止已经走了一段距离D.如果你走更远的距离x并看到一个重量为w的点,你需要花费(D + x)w。如果你走更远的距离y并看到一个重量为v的点它会花费你(D + x + y)v ..如果你将所有这些加起来有一个组件取决于你在距离D之后所采取的路径: xw + xv + yv + ...,并且有一个组件取决于距离D和您需要携带的点的权重之和:D(v + w + ...)。但是依赖于距离D的分量除了你需要访问的点的权重总和之外不依赖于其他任何东西,所以它是固定的,因为它是相同的,无论你走的距离后你采取的路径是什么d。

在我们访问它们时访问我们传递的点总是有意义的,因此最佳路径将从一个点开始(可能在要访问的点集的边缘,可能在中心),然后展开这到访问点的间隔,然后展开这个访问所有点。要预先计算访问区间外所有点的相对成本,我们只需知道当前位置和区间的大小,而不是到目前为止的距离。

因此,一种昂贵的多项式动态编程方法将当前位置(必须是其中一个点)的状态作为当前位置左侧第一个(如果有的话)未访问点的位置和位置,如果有的话,在当前点右边第一个未被访问的点。我们应该考虑下次访问最多两个点 - 当前点右侧的点和当前点左侧的点。我们可以通过查看剩余点较少的状态的预先计算的成本来计算这两种备选方案的成本,并从这一点将最佳结果存储为最佳成本。我们可以在达到当前点时D = 0的虚构下计算这些成本。当我们查找存储成本时,它们也存储在这个假设下(但是当前点的D = 0,而不是我们当前的点),但是我们知道在那个阶段留下的点的权重之和,所以我们可以添加到存储的成本,即权重总和乘以我们当前点与我们正在查找的点之间的距离,以弥补这一点。

这给出了成本O(n ^ 3),因为您正在构建一个包含O(n ^ 3)个单元格的表格,每个单元格都是一个相对简单的过程的产物。但是,因为在没有访问它们的情况下传递单元格是没有意义的,所以当前点必须紧邻间隔两端的两个点之一,因此我们只需要考虑O(n ^ 2)种可能性,这样可以降低成本低至O(n ^ 2)。 Zig-zag路径如(0,1,-1,2,-2,3,-3,4,-4 ......)可能是适合奇异权重的最佳解决方案,但仍然如此,即使例如从-2到3时,-2到3是两点3和-3之间尚未取得的最近点。

我在http://www.mcdowella.demon.co.uk/Plumber.java尝试了一个java实现。测试工具针对一些(慢速)几乎详尽的版本检查这个DP版本,这些版本的长度可达12个,包括12个。它仍然可能不是完全没有错误,但希望它会填写详细信息。