最快的算法优化挑战


9

这是我对渐近复杂性挑战的第一个实验,尽管我完全满意代码中的答案,只要它们带有时间复杂度的解释即可。

我有以下问题。

考虑任务T_1,... T_n和过程M_1,...,M_m。每个任务需要一定的时间才能执行,具体取决于过程。

每个任务还​​需要花费一定数量才能执行,具体取决于过程。

这些任务必须严格执行(它们不能并行完成),并且更改proc需要花费时间。启动任务后,无法将其从一个进程转移到另一个进程。

最后,每个任务必须在一定时间之前完成。

任务

目的是给出一种算法(或一些代码),该算法给出上述表格的五个表,以最大程度地减少完成所有任务的总成本,同时确保所有任务在其截止日期之前完成。如果这不可能,我们只是报告无法完成。

得分了

您应该根据变量n,m和d给出解决方案的大哦复杂度,其中d是最后期限。大Oh复杂度中应该没有不必要的常量。因此,例如,O(n / 1000)应写为O(n)。

只需将n = 100,m = 100和d = 1000设置为您陈述的复杂度即可计算出分数。您想要最小的分数。

决胜局

如果是平局,则第一个答案将获胜。


增加了笔记

log 答案的时间复杂度将以2为底。

计分板

  • 来自KSFT的10 ^ 202(Python)首先提交,因此获得了赏金。
  • 来自DominikMüller(Scala)的10 ^ 202

“从行机切换到列机的时间”您的意思是从M_1切换到M_2的时间成本吗?另外,“转换成本”和“转换时间”之间有何区别?与描述调度算法有关,它们通常具有相同的含义。
发光的

@Luminous以秒为单位思考时间,以美元为单位。他们在这个问题上是不同的。这些表显示了更换机器以执行下一个任务的时间(分别为成本)。从M_1到M_2,或者从M_2到M_1。

好的,这可以澄清这一点。
发光的

简短的答案是复杂度将是O(m ^ n)。没有哪个算法会比“更快”。基于最大所需时间或成本的修剪不会改变算法的复杂性,也不会同时具有美元成本和时间成本,因此d不是复杂性的组成部分。
Bob Dalgleish 2015年

1
@BobDalgleish这给100的幂带来了100分。我相信你可以做得更好。

Answers:


2

得分:10 ^ 202

我有点希望我们现在有LaTeX支持...

既然没有人回答,我想我会尽力的,尽管这不是很有效。不过,我不确定到底有什么大问题。

我认为它有效。至少,它仅适用于发布的唯一测试用例。

它像问题一样接受输入,除了没有机器或任务号标签,并以分号代替换行符。

import itertools
time = [[int(j) for j in i.split()] for i in raw_input().split(";")]
cost = [[int(j) for j in i.split()] for i in raw_input().split(";")]
nmachines=len(time)
ntasks=len(time[0])
switchtime = [[int(j) for j in i.split()] for i in raw_input().split(";")]
switchcost = [[int(j) for j in i.split()] for i in raw_input().split(";")]
deadline = [int(i) for i in raw_input().split()]
d={}
m=itertools.product(range(nmachines),repeat=ntasks)
for i in m:
    t=-switchtime[i[-1]][i[0]]
    c=-switchcost[i[-1]][i[0]]
    e=0
    meetsdeadline=True
    for j in range(ntasks):
        t+=switchtime[i[e-1]][i[e]]+time[i[e]][j]
        c+=switchcost[i[e-1]][i[e]]+cost[i[e]][j]
        e+=1
        if t>deadline[j]:
            meetsdeadline=False
    if meetsdeadline:
        d[(c,t)]=i
print min(d.keys()),d[min(d.keys())]

您能否提供一些解释并说出您的分数应该是什么?另外,您能说明问题示例中的内容吗?

正如我在回答中指出的那样,我已经尝试过了,并且确实可以在示例中使用。我不确定大O是什么(我想在回答中提到)。
KSFT,2015年

基本上,大约需要完成几个操作。看起来大概花费了ntasks * m个时间(假设循环中的所有分配都花费了恒定的时间),这使我对必须承认的正确性感到怀疑。您能说说为什么会起作用吗?

1
哦! 我错过了。所以m实际上是nmachines ^ ntasks个大小。好吧,现在我相信它可以工作。我认为您的分数是(100 ^ 100)* 100。

4
@Lembik到目前为止是最高分!
KSFT 2015年

1

全部检查-Scala

估计分数:2m ^ n

我从每台机器开始,然后遍历所有任务,以使用符合期限的不同机器在任务中创建所有排列。这意味着如果一切都按时完成,我将获得2台机器和3项任务的9条可能路径。(m ^ n)之后,我走了成本最低的道路。

输入的结构如下(->说明各部分,因此不应输入):

M_1:5 3 5 4;M_2:4 2 7 5                 --> time
M_1:5 4 2 6;M_2:3 7 3 3                 --> cost
M_1:M_1}0 M_2}1;M_2:M_1}2 M_2}0         --> switch itme
M_1:M_1}0 M_2}2;M_2:M_1}1 M_2}0         --> switch cost
5 10 15 20                              --> deadlines

这是代码:

package Scheduling

import scala.io.StdIn.readLine

case class Cost(task: Map[String, List[Int]])
case class Switch(machine: Map[String, Map[String, Int]])
case class Path(time: Int, cost: Int, machine: List[String])

object Main {

    def main(args: Array[String]) {
        val (machines, cost_time, cost_money, switch_time, switch_money, deadlines) = getInput

        val s = new Scheduler(machines, cost_time, cost_money, switch_time, switch_money, deadlines)
        s.schedule
    }

    def getInput(): (List[String], Cost, Cost, Switch, Switch, List[Int]) = {
        val cost_time = Cost(readLine("time to complete task").split(";").map{s => 
                val parts = s.split(":")
                (parts(0) -> parts(1).split(" ").map(_.toInt).toList)
            }.toMap)

        val cost_money = Cost(readLine("cost to complete task").split(";").map{s => 
                val parts = s.split(":")
                (parts(0) -> parts(1).split(" ").map(_.toInt).toList)
            }.toMap)

        val switch_time = Switch(readLine("time to switch").split(";").map{s => 
                val parts = s.split(":")
                (parts(0) -> parts(1).split(" ").map{t =>
                        val entries = t.split("}")
                        (entries(0) -> entries(1).toInt)
                    }.toMap)
            }.toMap)

        val switch_money = Switch(readLine("time to switch").split(";").map{s => 
                val parts = s.split(":")
                (parts(0) -> parts(1).split(" ").map{t =>
                        val entries = t.split("}")
                        (entries(0) -> entries(1).toInt)
                    }.toMap)
            }.toMap)

        val deadlines = readLine("deadlines").split(" ").map(_.toInt).toList

        val machines = cost_time.task.keys.toList

        (machines, cost_time, cost_money, switch_time, switch_money, deadlines)
    }
}

class Scheduler(machines: List[String], cost_time: Cost, cost_money: Cost, switch_time: Switch, switch_money: Switch, deadlines: List[Int]) {

    def schedule() {
        var paths = List[Path]()
        var alternatives = List[(Int, Path)]()

        for (i <- machines) {
            if (cost_time.task(i)(0) <= deadlines(0)) {
                paths = paths ::: List(Path(cost_time.task(i)(0), cost_money.task(i)(0), List(i)))
            }
        }

        val allPaths = deadlines.zipWithIndex.tail.foldLeft(paths)((paths, b) => paths.flatMap(x => calculatePath(x, b._1, b._2)))

        if (allPaths.isEmpty) {
            println("It is not possible")
        } else {
            println(allPaths.minBy(p=>p.cost).machine)
        }
    }

    def calculatePath(prev: Path, deadline: Int, task: Int): List[Path] = {
        val paths = machines.map(m => calculatePath(prev, task, m))
        paths.filter(p => p.time <= deadline)
    }

    def calculatePath(prev: Path, task: Int, machine: String): Path = {
        val time = prev.time + switch_time.machine(prev.machine.last)(machine) + cost_time.task(machine)(task)
        val cost = prev.cost + switch_money.machine(prev.machine.last)(machine) + cost_money.task(machine)(task)

        Path(time, cost, prev.machine :+ machine)
    }
}

我也有一个从背面开始的想法。由于如果时间较短,那么您总是可以选择成本最低的机器,那么从前一个截止日期到新截止日期的时间差就可以了。但是,如果成本较高的任务花费的时间超过了最后期限,则这不会减少最大运行时间。

更新资料

======

这是另一个设置。时间:

M_1 2 2 2 7
M_2 1 8 5 10

成本:

M_1 4 4 4 4
M_2 1 1 1 1

切换时间:

    M_1 M_2
M_1  0   2
M_2  6   0

转换费用:

    M_1 M_2
M_1  0   2
M_2  2   0

截止日期:

5 10 15 20

作为我程序的输入:

M_1:2 2 2 7;M_2:1 8 5 10
M_1:4 4 4 4;M_2:1 1 1 1
M_1:M_1}0 M_2}2;M_2:M_1}6 M_2}0
M_1:M_1}0 M_2}2;M_2:M_1}2 M_2}0
5 10 15 20

此解决方案有两种解决方案:时间:18,成本:15,路径:List(M_1,M_1,M_1,M_2)时间:18,成本:15,路径:List(M_2,M_1,M_1,M_1)

这就提出了一个问题,该如何处理。应该全部打印还是仅打印一个?如果时间不一样怎么办?成本最低且没有错过最后期限的人是否足够?还是应该是时间最短的人?


问题是目标是“ [最小化]总成本”。顺便说一句,您能否总结一下算法的工作原理?我不了解Scala,也无法弄清楚它是如何工作的。
KSFT 2015年

遍历所有路径需要花费O(m^n)时间。遍历每台计算机以完成所有任务需要花费O(n*m^n)时间。
KSFT

难道不是O(n*m^n)在每个路径上都遍历每个任务吗?然后遍历每台机器上的每个任务,例如O(n*m)
DominikMüller'15

啊,错字了。我的意思是“在每个机器迭代写的所有路径的需要O(n*m^n)”。
KSFT 2015年

等等,不,是O(m*m^n)=O(m^n+1)。不过还是一样。
KSFT 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.