Dijkstra vs迭代。如何建模图并确定两种情况下的时间复杂度?

时间:2017-03-07 15:38:05

标签: algorithm time-complexity graph-theory dijkstra

我想知道我在这里找到的问题:Finding cheapest combination of items with conditions on the selection 我想继续讨论这个因为我发现这非常有趣并且解决方案非常酷,但我完全理解它有问题。所以我想在另一个问题的评论中问这个问题可能超出了范围。

问题在于如何找到以最便宜的方式从卖家n购买S金额i,这些金额在Name Price Units in storage Supplier #1 17$ 1 Unit Supplier #2 18$ 3 Units Supplier #3 23$ 5 Units 项上有不同的价格。为了完整起见,表格如下:

public class Graph2 {

    public static class Vendor {
        int itemCount = 0;
        double price = 0.0;

        public Vendor(int itemCount, double price ) {
            this.price = price;
            this.itemCount = itemCount;
        }
    }

    public static class Combo {

        public Combo(int aCount, int bCount, int cCount, double total) {
            this.aCount = aCount;
            this.bCount = bCount;
            this.cCount = cCount;
            this.total = total;
        }

        public int aCount;
        public int bCount;
        public int cCount;

        public double total;

    }

    public static int counter = 0;

    public static void main(String[] args) {

        Vendor a = new Vendor(1, 17.0);
        Vendor b = new Vendor(3, 18.0);
        Vendor c = new Vendor(5, 23.0);

        Map<Integer, List<Combo>> comboMap = new HashMap<>();

        for(int i_a = 0; i_a <= a.itemCount; i_a++) {

            for(int i_b = 0; i_b <= b.itemCount; i_b++) {

                for(int i_c = 0; i_c <= c.itemCount; i_c++) {

                    StringBuffer buf = new StringBuffer();
                    buf.append("A: ").append(i_a).append(" B: ").append(i_b).append(" C: ").append(i_c);

                    int totalCount = i_a + i_b + i_c;

                    List<Combo> combos = comboMap.computeIfAbsent(totalCount, k -> new ArrayList<>());

                    combos.add(new Combo(i_a, i_b, i_c, i_a * a.price + i_b * b.price + i_c * c.price));

                }
            }
        }

        comboMap.entrySet().stream().forEach(e -> {

            Integer totalCount = e.getKey();
            List<Combo> combos = e.getValue();

            combos.forEach( combo -> {
                counter++;
                StringBuffer buffer = new StringBuffer();
                buffer.append("Buying ").append(totalCount).append(" items. ").append("A: ").append(combo.aCount)
                      .append(" B: ").append(combo.bCount).append(" C: ").append(combo.cCount).append(" Costs: ").append(combo.total);

                System.out.println(buffer.toString());
            });

        });


        System.out.println("Combinations: " + counter);
    }
}

建议的解决方案(和正确的答案)是使用Dijkstra的算法。我想讨论这个问题的影响和时间复杂性。首先,我创建了一个非常简单的java循环来确定不同组合的不同价格:

// omitting the first X lines for they are not that important
Buying 8 items. A: 1 B: 3 C: 4 Costs: 163.0
Buying 9 items. A: 1 B: 3 C: 5 Costs: 186.0
Combinations: 48

这产生了这样的结果:

S -> A  // connect to A
A -> B|C // connect A to B or C  
B -> B|C // connect B to B or C 
C -> B|C // connect C to B or C 
... 

因此,对于这个特定问题,遍历所有卖家的物品数量并计算物品成本的任何组合需要48次迭代。或者:a * b * c,其中a,b和c是卖家的相应项目数(48因为我也计算0结果)

现在,转到图表。我知道这是一个最短路问题(或者可以这样建模)。这意味着我们可以有一个代表买入操作的图表,一次一个,然后我们可以使用Dijkstra并通过将每个边缘的价格加到总重量来确定最短路径。

我甚至构造了图形(除了手动添加顶点和创建边缘,这是我停止的那么繁琐)。但这让我想到了这一点:图表的顶点/边缘数是否大于a * b * c?

例如,对于只有1个子图,我会这样做:(我不做完整的,只是为了表明我的想法)。 A,B,C是卖家。 S是来源(没有项目):

from S -> Amount of sellers (3) 
from each of the sellers node on level 1 -> Amount of sellers (3 * 3)
This continues until the items count goes to zero I believe, at which point it will be *2 

这意味着粗略意味着:

a -> b -> c == c -> b -> a

这对我来说意味着图表将具有计算事物的每种方式的组合(这将反过来导致重复和更高的复杂性)。这是因为 public static void main(String[] args) { // A 17$ 1 Unit // B 18$ 3 Units // C 23$ 5 Units Graph g = new Graph(); // Nodes List<Vertex> vertexes = new ArrayList<>(); vertexes.add(v("source", 0, 0.0)); vertexes.addAll(IntStream.range(1, 2).mapToObj(i -> v("A_"+ i, i, 17.0)).collect(Collectors.toList())); vertexes.addAll(IntStream.range(1, 4).mapToObj(i -> v("B_"+ i, i, 18.0)).collect(Collectors.toList())); vertexes.addAll(IntStream.range(1, 6).mapToObj(i -> v("C_"+ i, i, 23.0)).collect(Collectors.toList())); // Sort the nodes by level (e.g. level 1 means buying 1 item from the seller) Collections.sort(vertexes, new Comparator<Vertex>() { @Override public int compare(Vertex o1, Vertex o2) { return Integer.compare(o1.level, o2.level); } }); // connect the vertexes: for(int i = 0; i< vertexes.size(); i++) { Vertex s = vertexes.get(i); for(int j = i+1; j < vertexes.size(); j++) { Vertex d = vertexes.get(j); if(d.level == s.level) continue; // same level can't connect as we can not buy 1 and end up with the same count if(d.level -1 != s.level) break; // level difference > 1 means we would buy 1 and end up with 2 items Edge e = e(s, d, d.price); // Create the edge from n to n+1 so that we can buy this g.edges.add(e); } } g.edges.forEach(System.out::println); g.vertexes.addAll(vertexes); } 等等。但是,如何在对图形建模时确定这一点和/或我如何处理这个问题呢?我想在我的算法训练中,我从来没有真正构建图形,而是将一个作为我操作的输入。

所以,总结一下,我想知道:

  • 我如何决定在这里使用Dijkstra的algortihm?
  • 如何将上述表格翻译成图表?
  • 是不是在表格上迭代并且比建模图表更容易,更有效地计算这些组合?
  • 假设我们使用Dijkstra,我们是否每次都想要执行构建图形,或者是否需要我们立即将信息存储在图形中?

为了完整起见,在Java中,这是我尝试建模图形的方式:

<source> to <A_1> Cost 17.0
<source> to <B_1> Cost 18.0
<source> to <C_1> Cost 23.0
<A_1> to <B_2> Cost 18.0
<A_1> to <C_2> Cost 23.0
<B_1> to <B_2> Cost 18.0
<B_1> to <C_2> Cost 23.0
<C_1> to <B_2> Cost 18.0
<C_1> to <C_2> Cost 23.0
<B_2> to <B_3> Cost 18.0
<B_2> to <C_3> Cost 23.0
<C_2> to <B_3> Cost 18.0
<C_2> to <C_3> Cost 23.0
<B_3> to <C_4> Cost 23.0
<C_3> to <C_4> Cost 23.0
<C_4> to <C_5> Cost 23.0

这将为我打印以下图表:

<?php

function calculateAge($born, $death) {
    $born = strpos($born, '-') === false ? $born . '-01-01' : $born;
    $death = strpos($death, '-') === false ? $death . '-01-01' : $death;

    $d1 = new DateTime($born);
    $d2 = new DateTime($death);
    return $d2->diff($d1)->y;
}

然而,这是不完整的,它只允许购买所有卖家的最大数量的物品。这就是为什么我认为我需要一个图表,其中包含所有卖家的所有组合,以便我可以确定任何订单及其路径。

我希望我设法写下我的想法/问题,这样他们才符合逻辑:)如果你发现它不清楚请告诉我,我会尽量让它更清楚一点。

1 个答案:

答案 0 :(得分:1)

您不需要明确地创建图形,您只需要使用类似于Dijkstra的贪婪原则。换句话说,在每个购买步骤中,您应该从最便宜的供应商处购买。

您也可以将此问题表述为线性编程,尽管在这种情况下不需要。