Una sfida di ottimizzazione dell'algoritmo più veloce


9

Questo è il mio primo esperimento con una sfida della complessità asintotica, anche se sono soddisfatto delle risposte interamente in codice, purché forniscano una spiegazione della loro complessità temporale.

Ho il seguente problema.

Considera le attività T_1, ... T_n e proc M_1, ..., M_m. Ogni attività richiede un certo tempo per l'esecuzione a seconda dei processi.

Ogni attività costa anche un determinato importo da eseguire a seconda delle procedure.

I compiti devono essere eseguiti in ordine rigoroso (non possono essere eseguiti in parallelo) e ci vuole tempo per cambiare proc. Un'attività non può essere spostata da un proc a un altro dopo che è stata avviata.

Infine, ogni attività deve essere completata entro un certo tempo.

l'obiettivo

L'obiettivo è fornire un algoritmo (o un codice) che fornisca cinque tabelle del modulo sopra, minimizza il costo totale per completare tutte le attività, assicurandosi che tutte le attività siano completate entro le loro scadenze. Se ciò non è possibile, segnaliamo semplicemente che non è possibile farlo.

Punto

Dovresti dare la grande complessità Oh della tua soluzione in termini di variabili n, m e d, dove d è l'ultima scadenza. Non ci dovrebbero essere costanti inutili nella tua grande complessità Oh. Quindi O (n / 1000) dovrebbe essere scritto come O (n), per esempio.

Il tuo punteggio viene calcolato semplicemente impostando n = 100, m = 100 e d = 1000 nella complessità dichiarata. Vuoi il punteggio più piccolo possibile.

demolitore

In caso di pareggio, vince la prima risposta.


note aggiunte

log nel tempo la complessità di una risposta sarà presa base 2.

tabellone segnapunti

  • 10 ^ 202 da KSFT ( Python ) Primo inviato quindi ottiene la taglia.
  • 10 ^ 202 da Dominik Müller ( Scala )

"passare il tempo dalla macchina a remi alla macchina a colonne" Intendi il costo del tempo per passare da M_1 a M_2? Inoltre, qual è la differenza tra "costo di commutazione" e "tempo di commutazione". In genere significano la stessa cosa in relazione alla descrizione degli algoritmi di pianificazione.
Luminoso

@Luminoso Pensa al tempo in secondi e al costo in dollari. Sono cose diverse in questa domanda. Le tabelle mostrano il tempo (rispettivamente il costo) del cambio macchina per eseguire l'attività successiva. Potrebbe essere compreso tra M_1 e M_2 o tra M_2 e M_1.

Ok, questo lo chiarisce.
Luminoso

La risposta breve è che la complessità sarà O(m ^ n). Nessun algoritmo sarà "più veloce" di quello. La potatura basata su un tempo o costo massimo richiesto non modifica la complessità dell'algoritmo, né ha sia un costo in dollari che un costo nel tempo, quindi dnon è un elemento della complessità.
Bob Dalgleish,

1
@BobDalgleish Questo dà un punteggio di 100 alla potenza di 100. Credo che tu possa fare molto meglio.

Risposte:


2

Punteggio: 10 ^ 202

Vorrei che avessimo il supporto di LaTeX ora ...

Dato che nessun altro ha risposto, ho pensato di provare, anche se non è molto efficiente. Non sono sicuro di quale sia la grande O, però.

Penso che funzioni Almeno, lo fa per l'unico caso di test pubblicato.

Prende input come nella domanda, tranne senza le etichette dei numeri macchina o attività e con punti e virgola anziché interruzioni di riga.

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

Puoi fornire qualche spiegazione e dire quale pensi che dovrebbe essere il tuo punteggio? Inoltre, potresti mostrare cosa fornisce nell'esempio nella domanda?

Come ho affermato nella mia risposta, l'ho provato e funziona sull'esempio. Non sono sicuro di quale sia la grande O (che intendevo menzionare nella mia risposta).
KSFT,

Fondamentalmente, approssimativamente quante operazioni ci vorranno per completare. Sembra che impieghi ntasks * m in modo approssimativo (supponiamo che tutti gli incarichi nei loop impieghino un tempo costante) che mi rendono sospettoso della sua correttezza che devo ammettere. Puoi dire qualcosa sul perché pensi che funzioni?

1
Oh! Ho perso questo. Quindi m è in realtà di dimensioni nmachine ^ ntasks. OK ora credo che funzioni. Penso che il tuo punteggio sia (100 ^ 100) * 100.

4
@Lembik Ha ottenuto il miglior punteggio finora!
KSFT,

1

Controlla tutto - Scala

Punteggio stimato: 2m ^ n

Inizio da ogni macchina e ripasso tutte le attività per creare tutte le permutazioni attraverso le attività con macchine diverse che rispettano le scadenze. Significa che se tutto è in tempo otterrei 9 possibili percorsi con 2 macchine e 3 attività. (m ^ n) Successivamente, prendo il percorso con il costo più basso.

L'input è strutturato in questo modo (-> spiega le parti e quindi non deve essere inserito):

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

Ed ecco il codice:

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

Ho anche avuto l'idea di partire da dietro. Dal momento che puoi sempre scegliere una macchina con il costo più basso se il tempo è inferiore, allora la differenza dalla scadenza precedente a quella nuova. Ciò non ridurrebbe il tempo di esecuzione massimo se l'attività con il costo migliore impiega più tempo dell'ultima scadenza.

Aggiornare

======

Ecco un altro set up. tempo:

M_1 2 2 2 7
M_2 1 8 5 10

costo:

M_1 4 4 4 4
M_2 1 1 1 1

orario di commutazione:

    M_1 M_2
M_1  0   2
M_2  6   0

cambiare costo:

    M_1 M_2
M_1  0   2
M_2  2   0

scadenze:

5 10 15 20

Come input nel mio programma:

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

Questo ha due soluzioni: tempo: 18, costo: 15, percorso: Elenco (M_1, M_1, M_1, M_2) tempo: 18, costo: 15, percorso: Elenco (M_2, M_1, M_1, M_1)

Il che solleva la domanda su come gestirlo. Tutti dovrebbero essere stampati o solo uno? E se il tempo fosse diverso? Uno con il costo più basso e nessuna scadenza mancata è sufficiente o dovrebbe essere anche quello con il tempo più basso?


La domanda dice che l'obiettivo è "[minimizzare] il costo totale". A proposito, puoi riassumere come funziona il tuo algoritmo? Non conosco Scala e non riesco a capire come funzioni.
KSFT,

L'iterazione su tutti i percorsi richiede O(m^n)tempo. L'iterazione su ogni macchina per tutte le attività richiede O(n*m^n)tempo.
KSFT,

Non sta O(n*m^n)iterando su ogni attività per ciascuno dei percorsi? E iterando su ogni macchina per ogni attività qualcosa di simile O(n*m).
Dominik Müller,

Ah, errore di battitura. Volevo scrivere "l'iterazione di ogni macchina per tutti i percorsi prende O(n*m^n)".
KSFT,

Aspetta, no, lo è O(m*m^n)=O(m^n+1). Tuttavia, è sempre lo stesso punteggio.
KSFT,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.