Venditore di patate calde


23

Dato un elenco di punti, trova il percorso più breve che visita tutti i punti e ritorna al punto di partenza.

Il problema del commesso viaggiatore è ben noto nel campo dell'informatica, così come molti modi per calcolarlo / approssimarlo. È stato risolto per gruppi di punti molto grandi, ma alcuni dei più grandi impiegano molti anni CPU per finire.

Non farti bruciare dalla patata.

Hot Potato è un gioco in cui 2+ giocatori passano in circolo una "patata" mentre suona la musica. L'obiettivo è di passarlo rapidamente al giocatore successivo. Se tieni la patata quando la musica si ferma, sei fuori.


L'oggetto di Hot Potato Salesman è:

Dato un insieme di 100 punti unici , restituisci quei punti in un ordine migliore ( distanza totale più corta come definita più in basso ). Questo "passerà" il problema al giocatore successivo. Devono migliorarlo e passarlo al successivo, ecc. Se un giocatore non può migliorarlo, sono fuori e il gioco continua fino a quando rimane un giocatore.

Per evitare che si tratti di una competizione "forza-bruta-me-a-path", ci sono queste clausole:

  • Non puoi impiegare più di un minuto per passare la patata. Se non hai trovato e superato una soluzione più breve al termine di un minuto, sei fuori.

  • Non è possibile modificare la posizione di oltre 25 punti. Per l'esattezza, i >= 75punti devono trovarsi nella stessa posizione in cui li hai ricevuti. Non importa quali decidi di cambiare, ma solo l' importo che cambi.

Quando rimane un solo giocatore, è il vincitore di quel gioco e riceve un punto. Un torneo è costituito da 5*ngiochi, in cui nè il numero di giocatori. Ogni partita, il giocatore iniziale verrà ruotato e l'ordine del giocatore rimanente verrà mischiato . Il giocatore con il maggior numero di punti alla fine è il vincitore del torneo. Se il torneo termina con un pareggio per il primo posto, un nuovo torneo verrà giocato solo con quei concorrenti. Questo continuerà fino a quando non vi sarà alcun pareggio.

Il giocatore iniziale per ogni partita riceverà una serie di punti selezionati in modo pseudocasuale in nessun ordine particolare.

I punti sono definiti come una coppia di x,ycoordinate intere su una griglia cartesiana. La distanza è misurata utilizzando Manhattan distanza , |x1-x2| + |y1-y2|. Tutte le coordinate saranno [0..199]comprese nell'intervallo.

Ingresso

L'input viene fornito con un singolo argomento stringa. Sarà composto da 201 numeri interi separati da virgola che rappresentano il numero di giocatori attuali ( m) e 100 punti:

m,x0,y0,x1,y1,x2,y2,...,x99,y99

L'ordine di questi punti è il percorso corrente. La distanza totale si ottiene aggiungendo la distanza da ciascun punto al successivo ( dist(0,1) + dist(1,2) + ... + dist(99,0)). Non dimenticare di tornare all'inizio per calcolare la distanza totale!

Nota che non lom è il numero di giocatori che hanno iniziato il gioco, è il numero che è ancora presente.

Produzione

L'output è dato allo stesso modo dell'input, meno m; una singola stringa contenente numeri interi separati da virgola che rappresentano i punti nel loro nuovo ordine.

x0,y0,x1,y1,x2,y2,...,x99,y99

Il programma di controllo attenderà l'output solo per un minuto. Alla ricezione dell'output, verificherà che:

  • l'output è ben formato
  • l'uscita consiste di soli e tutti i punti 100 presente in ingresso
  • >=75 i punti sono nelle loro posizioni originali
  • la lunghezza del percorso è inferiore al percorso precedente

Se uno di questi controlli fallisce (o non c'è output), sei fuori e il gioco passerà al giocatore successivo.

Programma di controllo

Puoi trovare il programma di controllo a questo link . Il programma di controllo stesso è deterministico ed è pubblicato con un seme fittizio di1 . Il seme utilizzato durante il punteggio sarà diverso, quindi non preoccuparti di cercare di analizzare l'ordine dei turni / gli elenchi di punti che sputa.

La classe principale è Tourney. Correre in questo modo farà un torneo completo con i concorrenti indicati come argomenti. Sputa il vincitore di ogni partita e un conteggio alla fine. Un torneo di esempio con due SwapBot è simile:

Starting tournament with seed 1

(0) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 3
(0) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 4
(1) SwapBot wins a game! Current score: 5
(1) SwapBot wins a game! Current score: 6
(1) SwapBot wins a game! Current score: 7
(1) SwapBot wins a game! Current score: 8

Final Results:

Wins        Contestant
2       (0) SwapBot
8       (1) SwapBot

Se vuoi provare solo un gioco alla volta, puoi eseguire la Gameclasse. Questo eseguirà una partita con i giocatori nell'ordine indicato come argomenti. Per impostazione predefinita, stamperà anche un play-by-play che mostra il giocatore attuale e la lunghezza del percorso.

Sono inclusi anche un paio di giocatori di prova: SwapBot, BlockPermuter, e TwoSwapBot. I primi due non saranno inclusi nelle sessioni di punteggio, quindi sentitevi liberi di usarli e abusarli durante i test. TwoSwapBot volontà incluso nel giudicare, e non è sciatto, quindi porta il tuo A-game.

Miscellanea

  • Non è possibile salvare le informazioni sullo stato e ogni turno è un'esecuzione separata del programma. L'unica informazione che riceverai ad ogni turno è l'insieme di punti.

  • Non è possibile utilizzare risorse esterne. Ciò include le chiamate di rete e l'accesso ai file.

  • Non è possibile utilizzare le funzioni di libreria progettate per risolvere / assistere con il problema TSP o le sue varianti.

  • Non puoi manipolare o interferire con altri giocatori in alcun modo.

  • Non è possibile manipolare o interferire con il programma di controllo o eventuali classi o file inclusi in alcun modo.

  • Il multithreading è consentito.

  • Un invio per utente. Se invii più di una voce, inserirò solo la prima inviata. Se si desidera modificare l'invio, modificare / eliminare l'originale.

  • Il torneo si svolgerà su Ubuntu 13.04, su un computer con una CPU i7-3770K e 16 GB di RAM. Non verrà eseguito in una macchina virtuale. Qualunque cosa che percepisca come dannosa squalificherà immediatamente la voce corrente e quella futura che invii.

  • Tutte le voci devono essere eseguibili dalla riga di comando con software gratuito ( come nella birra ). In caso di problemi durante la compilazione / esecuzione della voce, chiederò aiuto nei commenti. Se non rispondi o alla fine non riesco a farlo funzionare, verrà squalificato.

Risultati (22 maggio 2014)

Nuovi risultati sono arrivati! UntangleBot ha battuto la concorrenza abbastanza profondamente. TwoSwapBot ha registrato sette vittorie e SANNbot ha anche conquistato una vittoria. Ecco un quadro di valutazione e un collegamento all'output non elaborato :

Wins        Contestant
22      (2) ./UntangleBot
7       (0) TwoSwapBot
1       (5) SANNbot.R
0       (1) BozoBot
0       (3) Threader
0       (4) DivideAndConquer

Così com'è ora , UntangleBot ha vinto il segno di spunta. Non lasciarti scoraggiare dall'entrare, però, dato che gestirò il torneo quando compaiono più concorrenti e cambierò la risposta accettata di conseguenza.


Commenti eliminati. Si prega di avvisare per eventuali informazioni perse.
Maniglia della porta

Amico, perché questa sfida doveva essere durante i miei esami finali (finalmente, con la scuola sì). Sei sicuramente un cattivo pianificatore Geobit;) Strano / abbastanza tristemente a quel tempo c'erano tonnellate di domande da re della collina e ora non ce ne sono (forse funziona meglio se ce n'è solo una alla volta, suggerimento suggerimento) ...
Herjan,

@Herjan Sentiti libero di provare ad affrontare l'attuale campione. Gestirò di nuovo il torneo quando compaiono nuovi concorrenti, quindi il concorso non è finito o altro. Batti SirDario e potrebbe spronarlo o qualcun altro a battere il tuo, riprendendoti la vita;)
Geobits,

@Herjan Si prega di inviare una voce! Credo che qui ci sia molto margine di miglioramento. La maggior parte delle soluzioni qui, compresa la mia, non si basano su algoritmi intelligenti specifici per questo problema.
SirDarius,

Penso che ci sia un problema con la limitazione delle modifiche. Come alcune volte ci vorrà per modificare l'intero set di dati per ottenere una soluzione migliore.
Ilya Gazman,

Risposte:


8

UntangleBot (precedentemente NiceBot)

Un bot C ++ 11 che utilizza due strategie.
All'inizio proverà a "districare" il percorso, se possibile, rilevando intersezioni tra percorsi che sono più vicini di 25 punti (poiché districare implica la modifica di tutti i punti tra di loro).
Se la prima strategia fallisce, scambia casualmente punti per trovare distanze migliori fino a quando non viene trovato un percorso migliore.

Questo bot batte costantemente TwoSwapBot con un rapporto approssimativo di cinque vittorie per una perdita nei miei tornei di prova.

// g++ -std=c++11 -O3 -o UntangleBot UntangleBot.cpp
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iostream>
#include <iterator>
#include <random>
#include <set>
#include <sstream>

const int NPOINTS = 100;

struct Point {
    int x, y;

    Point():x(0),y(0) {}    
    Point(int x, int y):x(x),y(y) {}

    int distance_to(const Point & pt) const {
        return std::abs(x - pt.x) + std::abs(y - pt.y);
    }
};

std::ostream & operator<<(std::ostream & out, const Point & point) {
    return out << point.x << ',' << point.y;
}

int total_distance(const Point points[NPOINTS]) {
    int distance = 0;
    for (int i = 0; i < NPOINTS; ++i) {
        distance += points[i].distance_to(points[(i+1)%NPOINTS]);
    }
    return distance;
}

bool intersects(const Point & p1, const Point & p2, const Point & p3, const Point & p4) {
    double s1_x, s1_y, s2_x, s2_y;
    s1_x = p2.x - p1.x;
    s1_y = p2.y - p1.y;
    s2_x = p4.x - p3.x;
    s2_y = p4.y - p3.y;

    double s, t;
    s = (-s1_y * (p1.x - p3.x) + s1_x * (p1.y - p3.y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p1.y - p3.y) - s2_y * (p1.x - p3.x)) / (-s2_x * s1_y + s1_x * s2_y);

    return s >= 0 && s <= 1 && t >= 0 && t <= 1;
}

int main(int argc, char ** argv) {
    Point points[NPOINTS];

    using Clock = std::chrono::system_clock;
    const Clock::time_point start_time = Clock::now();

    // initialize points
    if (argc < 2) {
        std::cerr << "Point list is missing" << std::endl;
        return 1;
    }
    std::stringstream in(argv[1]);
    int players;
    char v;
    in >> players >> v;
    for (int i = 0; i < NPOINTS; ++i) {
        in >> points[i].x >> v >> points[i].y >> v;
    }

    int original_distance = total_distance(points);

    // detect intersection between any 4 points
    for (int i = 0; i < NPOINTS; ++i) {
        for (int j = i+1; j < NPOINTS; ++j) {
            Point & p1 = points[i];
            Point & p2 = points[(i+1)%NPOINTS];
            Point & p3 = points[j];
            Point & p4 = points[(j+1)%NPOINTS];

            // points must all be distinct
            if (p1.distance_to(p3) == 0 || p1.distance_to(p4) == 0 || p2.distance_to(p3) == 0 || p2.distance_to(p4) == 0) {
                continue;
            }

            // do they intersect ?
            if (!intersects(p1, p2, p3, p4)) {
                continue;
            }

            // can modify less than 25 points ?
            if (std::abs(j-i) > 25) {
                continue;
            }

            // swap points
            for (int m = 0; m < std::abs(j-i)/2; ++m) {
                if (i+1+m != j-m) {
                    std::swap(points[i+1+m], points[j-m]);
                    //std::cerr << "untangle " << i+1+m << " <=> " << j-m << '\n';
                }
            }

            int new_distance = total_distance(points);
            if (new_distance < original_distance) {
                std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
                std::cout << points[NPOINTS-1];
                return 0;
            }
            else {
                // swap points back
                for (int m = 0; m < std::abs(j-i)/2; m++) {
                    if (i+1+m != j-m) {
                        std::swap(points[i+1+m], points[j-m]);
                    }
                }
            }
        }
    }

    // more traditional approach if the first fails
    std::mt19937 rng(std::chrono::duration_cast<std::chrono::seconds>(start_time.time_since_epoch()).count());
    std::uniform_int_distribution<> distr(0, NPOINTS-1);
    while (true) {
        // try all possible swaps from a random permutation
        int p1 = distr(rng);
        int p2 = distr(rng);
        std::swap(points[p1], points[p2]);

        for (int i = 0; i < NPOINTS; ++i) {
            for (int j = i+1; j < NPOINTS; ++j) {
                std::swap(points[i], points[j]);
                if (total_distance(points) < original_distance) {
                    std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
                    std::cout << points[NPOINTS-1];
                    return 0;
                }
                else {
                    std::swap(points[i], points[j]);
                }
            }
        }

        // they didn't yield a shorter path, swap the points back and try again
        std::swap(points[p1], points[p2]);
    }
    return 0;
}

Mi hai battuto di 19 minuti!
Rainbolt

Questo dovrebbe avere più voti, a giudicare dai risultati di oggi.
Geobits,

@Geobits Sono ancora sorpreso che la cosa più semplice che mi è venuta in mente si esibisca così bene. Sperando che entrino concorrenti più impegnativi!
SirDarius,

@SirDarius Ok, bene . Hai un po 'di sfida.
Geobits

4

SANNbot

(un robot di ricottura simulato in R )

Dovrebbe essere chiamato con Rscript SANNbot.R.

input <- strsplit(commandArgs(TRUE),split=",")[[1]]
n <- as.integer(input[1])                            # Number of players
init_s <- s <- matrix(as.integer(input[-1]),ncol=2,byrow=TRUE) # Sequence of points
totdist <- function(s){                              # Distance function
    d <- 0
    for(i in 1:99){d <- d + (abs(s[i,1]-s[i+1,1])+abs(s[i,2]-s[i+1,2]))}
    d
    }
gr <- function(s){                                   # Permutation function
    changepoints <- sample(1:100,size=2, replace=FALSE)
    tmp <- s[changepoints[1],]
    s[changepoints[1],] <- s[changepoints[2],]
    s[changepoints[2],] <- tmp
    s
    }
d <- init_d <- totdist(s)                            # Initial distance
k <- 1                                               # Number of iterations
v <- 0
t <- n*(init_d/12000)                                 # Temperature
repeat{
    si <- gr(s)                                      # New sequence
    di <- totdist(si)                                # New distance
    dd <- di - d                                     # Difference of distances
    if(dd <= 0 | runif(1) < exp(-dd/t)){             # Allow small uphill changes
        d <- di
        s <- si
        v <- v+2
        }
    k <- k+1
    if(k > 10000){break}
    if(d > init_d & v>20){s <- init_s; d <- init_d; v <- 0}
    if(v>23){break}
}
cat(paste(apply(s,1,paste,collapse=","),collapse=","))

L'idea è relativamente semplice: ogni turno è una "fase di raffreddamento" di una ricottura simulata con il numero di giocatori ancora in gioco come "temperatura" (modificata dalla distanza corrente oltre 12000, cioè approssimativamente la distanza iniziale). L'unica differenza con una corretta ricottura simulata è che ho verificato che non permetto più di 25 elementi e se ho usato 20 mosse ma la sequenza risultante vale rispetto all'iniziale, ricomincia da capo.


@Geobits ah scusa cancellato la linea in cui init_s è stata definita per caso (bene "incidente": ho visto la linea e ho pensato "perché è di nuovo qui?" E l'ho cancellata :)). Corretto.
plannapus,

Ho provato il tuo programma usando java Tourney "java Swapbot" "Rscript SANNbot.R" e sembrava funzionare.
plannapus,

Sì, sembra funzionare ora.
Geobits

In teoria (se non sbaglio del tutto) dovrebbe funzionare meglio quando più giocatori entrano nel gioco.
plannapus,

Così com'è, questo programma esce sempre presto a causa di "troppi punti modificati" nei miei test. Se sbatto ilu aumento i limiti di controllo, ciò non accade (e funziona molto meglio). Mentre il tuo codice sembra piuttosto semplice, non conosco le stranezze di R, quindi non posso dire se la logica è sbagliata. (l'ultima versione del controller fornirà messaggi sul perché un bot si spegne durante l'esecuzione Game, in modo che possa aiutare a individuare il problema)
Geobits

4

BozoBot

Utilizza la complessa logica alla base di Bozosort per trovare un percorso migliore. Sembra così

  • Scambia punti casuali
  • Se siamo migliorati
    • Restituire la risposta
  • Altrimenti
    • Riprova

BozoBot è stato ora migliorato con il multithreading ! Quattro servitori ora manipolano i punti senza meta fino a quando non inciampano in un miglioramento. Il primo a trovare una soluzione ottiene un cookie!

Apparentemente non riesco a multithreading.

import java.util.Random;
public class BozoBot {
    public static String[] input;
    public static void main(String[] args) {
        input = args;
        for(int i = 0; i < 4; i++) new Minion().run();
    }
}

class Minion implements Runnable {
    public static boolean completed = false;
    public static synchronized void output(int[] x, int[] y) {
        if(!completed) {
            completed = true;
            String result = x[0]+","+y[0];
            for (int i = 1; i < 100; i++)
                result+=","+x[i]+","+y[i];
            System.out.print(result);
            // receiveCookie(); // Commented out to save money
        }
    }
    public void run() {
        String[] args = BozoBot.input[0].split(",");
        int[] x = new int[100];
        int[] y = new int[100];
        for (int i = 1; i < 201; i+=2) {
            x[(i-1)/2] = Integer.parseInt(args[i]);
            y[i/2] = Integer.parseInt(args[i+1]);
        }
        int startDistance = 0;
        for (int i = 1; i < 100; i++)
            startDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
        int r1, r2, r3, r4, tX, tY, newDistance;
        Random gen = new java.util.Random();
        while (true) {
            r1 = gen.nextInt(100);
            r2 = gen.nextInt(100);
            r3 = gen.nextInt(100);
            r4 = gen.nextInt(100);
            tX = x[r1];
            x[r1] = x[r2];
            x[r2] = tX;
            tY = y[r1];
            y[r1] = y[r2];
            y[r2] = tY;
            tX = x[r3];
            x[r3] = x[r4];
            x[r4] = tX;
            tY = y[r3];
            y[r3] = y[r4];
            y[r4] = tY;
            newDistance = 0;
            for (int i=1; i < 100; i++)
                newDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
            if(newDistance < startDistance)
                break;
            tX = x[r1];
            x[r1] = x[r2];
            x[r2] = tX;
            tY = y[r1];
            y[r1] = y[r2];
            y[r2] = tY;
            tX = x[r3];
            x[r3] = x[r4];
            x[r4] = tX;
            tY = y[r3];
            y[r3] = y[r4];
            y[r4] = tY;
        }
        output(x,y);
    }
}

Ehhmm ... Non dovresti mettere i tuoi seguaci in un thread invece di chiamare run ()? AFAIK, questo non è multithreading ...
CommonGuy,

@Manu Mi hai beccato! Ho provato a impararlo da solo e non ci sono riuscito. Qualche puntatore?
Rainbolt,

Penso che dovrebbe esserenew Thread(new Minion()).start()
CommonGuy

1
@Manu Grazie. Apparentemente ho letto solo metà di questo tutorial mentre stavo scrivendo un codice.
Rainbolt,

3

TwoSwapBot

Un aggiornamento a SwapBot, questo ragazzo controlla ogni coppia di swap. Innanzitutto, controlla se un singolo swap accorcia il percorso. Se lo fa, ritorna immediatamente. In caso contrario, controlla ciascuno per vedere se un altro scambio lo accorcia. Altrimenti, muore e basta.

Mentre il percorso è ancora semi-casuale, normalmente ritorna in circa 100ms. Se deve controllare ogni 2 swap (circa 25 M), ci vogliono circa 20 secondi.

Al momento della presentazione, questo ha battuto tutti gli altri concorrenti nei round di prova.

public class TwoSwapBot {

    static int numPoints = 100;

    String run(String input){
        String[] tokens = input.split(",");
        if(tokens.length < numPoints*2)
            return "bad input? nope. no way. bye.";

        Point[] points = new Point[numPoints];  
        for(int i=0;i<numPoints;i++)
            points[i] = new Point(Integer.valueOf(tokens[i*2+1]), Integer.valueOf(tokens[i*2+2]));
        int startDist = totalDistance(points);

        Point[][] swapped = new Point[(numPoints*(numPoints+1))/2][];       
        int idx = 0;
        for(int i=0;i<numPoints;i++){
            for(int j=i+1;j<numPoints;j++){
                Point[] test = copyPoints(points);
                swapPoints(test,i,j);
                int testDist = totalDistance(test);
                if( testDist < startDist)
                    return getPathString(test);
                else
                    swapped[idx++] = test;
            }
        }

        for(int i=0;i<idx;i++){
            for(int k=0;k<numPoints;k++){
                for(int l=k+1;l<numPoints;l++){
                    swapPoints(swapped[i],k,l);
                    if(totalDistance(swapped[i]) < startDist)
                        return getPathString(swapped[i]);
                    swapPoints(swapped[i],k,l);
                }
            }
        }
        return "well damn";
    }

    void swapPoints(Point[] in, int a, int b){
        Point tmp = in[a];
        in[a] = in[b];
        in[b] = tmp;
    }

    String getPathString(Point[] in){
        String path = "";
        for(int i=0;i<numPoints;i++)
            path += in[i].x + "," + in[i].y + ",";
        return path.substring(0,path.length()-1);
    }

    Point[] copyPoints(Point[] in){
        Point[] out = new Point[in.length];
        for(int i=0;i<out.length;i++)
            out[i] = new Point(in[i].x, in[i].y);
        return out;
    }

    static int totalDistance(Point[] in){
        int dist = 0;
        for(int i=0;i<numPoints-1;i++)
            dist += in[i].distance(in[i+1]);
        return dist + in[numPoints-1].distance(in[0]);
    }

    public static void main(String[] args) {
        if(args.length < 1)
            return;
        System.out.print(new TwoSwapBot().run(args[0]));
    }

    class Point{
        final int x; final int y;
        Point(int x, int y){this.x = x; this.y = y;}
        int distance(Point other){return Math.abs(x-other.x) + Math.abs(y-other.y);}
    }
}

2

Infila

Questo robot

  1. Suddivide i 100 punti in 4 10 pezzi di 25 10 punti
  2. Inizia una discussione per ogni pezzo
  3. Nel thread, mescola casualmente l'array, mantenendo fissi l'inizio e l'endpoint
  4. Se la distanza del nuovo array è più breve, mantenerlo
  5. Dopo 59 anni, il thread principale raccoglie i risultati e li stampa

L'idea è quella di trovare il miglior miglioramento del percorso, in modo che gli altri robot falliscano con la loro logica.

import java.util.Arrays;
import java.util.Collections;

public class Threader {
    public static final int THREAD_COUNT = 10;
    public static final int DOT_COUNT = 100;
    private final Dot[] startDots = new Dot[THREAD_COUNT];
    private final Dot[] endDots = new Dot[THREAD_COUNT];
    private final Dot[][] middleDots = new Dot[THREAD_COUNT][DOT_COUNT/THREAD_COUNT-2];
    private final Worker worker[] = new Worker[THREAD_COUNT];
    private final static long TIME = 59000; 

    public static void main(String[] args) {
        Threader threader = new Threader();
        //remove unnecessary player count to make calculation easier
        threader.letWorkersWork(args[0].replaceFirst("^[0-9]{1,3},", "").split(","));
    }

    public void letWorkersWork(String[] args) {
        readArgs(args);
        startWorkers();
        waitForWorkers();
        printOutput();
    }

    private void readArgs(String[] args) {
        final int magigNumber = DOT_COUNT*2/THREAD_COUNT;
        for (int i = 0; i < args.length; i += 2) {
            Dot dot = new Dot(Integer.parseInt(args[i]), Integer.parseInt(args[i + 1]));
            if (i % magigNumber == 0) {
                startDots[i / magigNumber] = dot;
            } else if (i % magigNumber == magigNumber - 2) {
                endDots[i / magigNumber] = dot;
            } else {
                middleDots[i / magigNumber][(i % magigNumber) / 2 - 1] = dot;
            }
        }
    }

    private void startWorkers() {
        for (int i = 0; i < THREAD_COUNT; i++) {
            worker[i] = new Worker(startDots[i], endDots[i], middleDots[i]);
            Thread thread = new Thread(worker[i]);
            thread.setDaemon(true);
            thread.start();
        }
    }

    private void waitForWorkers() {
        try {
            Thread.sleep(TIME);
        } catch (InterruptedException e) {
        }
    }

    private void printOutput() {
        //get results
        Worker.stopWriting = true;
        int workerOfTheYear = 0;
        int bestDiff = 0;
        for (int i = 0; i < THREAD_COUNT; i++) {
            if (worker[i].diff() > bestDiff) {
                bestDiff = worker[i].diff();
                workerOfTheYear = i;
            }
        }
        //build output
        StringBuilder result = new StringBuilder(1000);
        for (int i = 0; i < THREAD_COUNT; i++) {
            result.append(startDots[i]);
            Dot middle[] = middleDots[i];
            if (i == workerOfTheYear) {
                middle = worker[i].bestMiddle;
            }
            for (int j = 0; j < middle.length; j++) {
                result.append(middle[j]);
            }
            result.append(endDots[i]);
        }
        result.replace(0, 1, ""); //replace first comma
        System.out.print(result);
    }

}

class Worker implements Runnable {

    public Dot start;
    public Dot end;
    private Dot[] middle = new Dot[Threader.DOT_COUNT/Threader.THREAD_COUNT-2];
    public Dot[] bestMiddle = new Dot[Threader.DOT_COUNT/Threader.THREAD_COUNT-2];
    public static boolean stopWriting = false;
    private int bestDist = Integer.MAX_VALUE;
    private final int startDist;

    public Worker(Dot start, Dot end, Dot[] middle) {
        this.start = start;
        this.end = end;
        System.arraycopy(middle, 0, this.middle, 0, middle.length);
        System.arraycopy(middle, 0, this.bestMiddle, 0, middle.length);
        startDist = Dot.totalDist(start, middle, end);
    }

    @Override
    public void run() {
        while (true) {
            shuffleArray(middle);
            int newDist = Dot.totalDist(start, middle, end);
            if (!stopWriting && newDist < bestDist) {
                System.arraycopy(middle, 0, bestMiddle, 0, middle.length);
                bestDist = newDist;
            }
        }
    }

    public int diff() {
        return startDist - bestDist;
    }

    private void shuffleArray(Dot[] ar) {
        Collections.shuffle(Arrays.asList(ar));
    }

}

class Dot {

    public final int x;
    public final int y;

    public Dot(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int distTo(Dot other) {
        return Math.abs(x - other.x) + Math.abs(y - other.y);
    }

    public static int totalDist(Dot start, Dot[] dots, Dot end) {
        int distance = end.distTo(start);
        distance += start.distTo(dots[0]);
        distance += end.distTo(dots[dots.length - 1]);
        for (int i = 1; i < dots.length; i++) {
            distance += dots[i].distTo(dots[i - 1]);
        }
        return distance;
    }

    @Override
    public String toString() {
        return "," + x + "," + y;
    }
}

2
Nota: sono stato modificato printlnper printeliminare la nuova riga alla fine dell'output. Altrimenti si schianta.
Geobits

Cambiato printlnper printe fatto la dinamica thread-count. Ora inizia con 10 thread ...
CommonGuy

1

Divide And Conquer + Greedy Bot

NOTA: ho esaminato il tuo codice per il Gamequale include quanto segue in Game.parsePath:

for(int i=0;i<numPoints;i++){
        test[i] = new Point(Integer.valueOf(tokens[i*2]), Integer.valueOf(tokens[i*2+1]));
        if(test[i].equals(currentPath[i]))
            same++;

Tuttavia, non esiste alcun catch(NumberFormatException)blocco, quindi il tuo programma probabilmente si arresterà in modo anomalo quando un programma del giocatore emette una stringa (come dimostrato alla fine del mainmetodo del mio programma ). Ti consiglio di risolvere questo problema, poiché i programmi potrebbero generare eccezioni, tracce di stack o simili. Altrimenti, commenta quella riga nel mio programma prima di eseguirla.

Torna all'argomento

Questa implementazione (in Java) suddivide l'elenco di punti in blocchi di 25, distanziati casualmente nell'elenco. Quindi, crea thread per accorciare il percorso tra i punti in ogni blocco (quindi, "Dividi e conquista"). Il thread principale monitora gli altri e si assicura di presentare la soluzione più breve entro il limite di tempo. Se un thread muore con o senza una soluzione, inizia nuovamente un altro thread su un blocco diverso.

Ogni thread usa l'algoritmo "goloso", che inizia in un punto casuale, arriva al punto più vicino e si ripete fino a coprire tutti i punti (quindi, "goloso").

Gli appunti

  • Questo durerà per l'intero minuto (ho dato 3 secondi per l'avvio / l'arresto del programma e l'avvio di JVM - non si sa mai quali saranno le routine di avvio di JVM coinvolte nel prossimo ...)
  • Anche se ha trovato soluzioni, continuerà a cercare e dopo 1 minuto è presente la migliore soluzione trovata.
  • Non sono sicuro che questa implementazione sia effettivamente valida. Mi sono divertito un po 'a programmarlo :)
  • Poiché molte cose sono casuali, ciò potrebbe non fornire lo stesso output per lo stesso input.

Basta compilare e utilizzare java DivideAndConquer.classper eseguire.

public class DivideAndConquer extends Thread {
    static LinkedList<Point> original;
    static Solution best;
    static LinkedList<DivideAndConquer> bots;
    static final Object _lock=new Object();

    public static void main(String[] args){
        if(args.length != 1) {
            System.err.println("Bad input!");
            System.exit(-1);
        }
        // make sure we don't sleep too long... get the start time
        long startTime = System.currentTimeMillis();
        // parse input
        String[] input=args[0].split(",");
        int numPlayers=Integer.parseInt(input[0]);
        original=new LinkedList<Point>();
        for(int i=1;i<input.length;i+=2){
            original.add(new Point(Integer.parseInt(input[i]), Integer.parseInt(input[i+1])));
        }
        // start threads
        bots=new LinkedList<DivideAndConquer>();
        for(int i=0;i<6;i++)
            bots.add(new DivideAndConquer(i));
        // sleep
        try {
            Thread.sleep(57000 - (System.currentTimeMillis() - startTime));
        } catch(Exception e){} // should never happen, so ignore
        // time to collect the results!
        Solution best=getBestSolution();
        if(best!=null){
            best.apply(original);
            String printStr="";
            for(int i=0;i<original.size();i++){
                Point printPoint=original.get(i);
                printStr+=printPoint.x+","+printPoint.y+",";
            }
            printStr=printStr.substring(0, printStr.length()-1);
            System.out.print(printStr);
        } else {
            System.out.println("Hey, I wonder if the tournament program crashes on NumberFormatExceptions... Anyway, we failed");
        }
    }

    // check the distance
    public static int calculateDistance(List<Point> points){
        int distance = 0;
        for(int i=0;i<points.size();i++){
            int next=i+1;
            if(next>=points.size())next=0;
            distance+=points.get(i).distance(points.get(next));
        }
        return distance;
    }

    public static void solutionFound(Solution s){
        // thanks to Java for having thread safety features built in
        synchronized(_lock){
            // thanks to Java again for short-circuit evaluation
            // saves lazy programmers lines of code all the time
            if(best==null || best.distDifference < s.distDifference){
                best=s;
            }
        }
    }

    public static Solution getBestSolution(){
        // make sure we don't accidentally return
        // the best Solution before it's even
        // done constructing
        synchronized(_lock){
            return best;
        }
    }

    List<Point> myPoints;
    int start;
    int length;
    int id;

    public DivideAndConquer(int id){
        super("DivideAndConquer-Processor-"+(id));
        this.id=id;
        myPoints=new LinkedList<Point>();
        start=(int) (Math.random()*75);
        length=25;
        for(int i=start;i<start+length;i++){
            myPoints.add(original.get(i));
        }
        start();
    }

    public void run(){
        // copy yet again so we can delete from it
        List<Point> copied=new LinkedList<Point>(myPoints);
        int originalDistance=calculateDistance(copied);
        // this is our solution list
        List<Point> solution=new LinkedList<Point>();
        int randomIdx=new Random().nextInt(copied.size());
        Point prev=copied.get(randomIdx);
        copied.remove(randomIdx);
        solution.add(prev);
        while(copied.size()>0){
           int idx=-1;
           int len = -1;
           for(int i=0;i<copied.size();i++){
               Point currentPoint=copied.get(i);
               int dist=prev.distance(currentPoint);
               if(len==-1 || dist<len){
                   len=dist;
                   idx=i;
               }
           }
           prev=copied.get(idx);
           copied.remove(idx);
           solution.add(prev);
        }
        // aaaand check our distance
        int finalDistance=calculateDistance(solution);
        if(finalDistance<originalDistance){
            // yes! solution
            Solution aSolution=new Solution(start, length, solution, originalDistance-finalDistance);
            solutionFound(aSolution);
        }
        // start over regardless
        bots.set(id, new DivideAndConquer(id));
    }

    // represents a solution
    static class Solution {
        int start;
        int length;
        int distDifference;
        List<Point> region;
        public Solution(int start, int length, List<Point> region, int distDifference){
            this.region=new LinkedList<Point>(region);
            this.start=start;
            this.length=length;
            this.distDifference=distDifference;
        }
        public void apply(List<Point> points){
            for(int i=0;i<length;i++){
                points.set(i+start, region.get(i));
            }
        }
    }

    // copied your Point class, sorry but it's useful...
    // just added public to each method for aesthetics
    static class Point{
        int x;
        int y;
        Point(int x, int y){
            this.x = x;
            this.y = y;
        }
        Point(Point other){
            this.x = other.x;
            this.y = other.y;
        }
        public boolean equals(Point other){
            if(this.x==other.x && this.y==other.y)
                return true;
            return false;
        }

        public int distance(Point other){
            return Math.abs(x-other.x) + Math.abs(y-other.y);
        }
    }
}

Potete crederci? SX mi ha chiesto un captcha quando ho inviato questo! Ti sembra fatto da bot? Sul serio?
DankMemes,

1
Correzione per NFException inviata. Ora ucciderà il giocatore invece di uccidere il programma.
Geobits

Per la cronaca, non credo che si sarebbe schiantato sulla tua linea " Ehi, mi chiedo se ... " comunque. Controlla se ci sono <200token prima di provare ad analizzare. Ancora meglio verificarlo comunque.
Geobits

@Geobits haha ​​non se ne è reso conto
DankMemes

Nota: per compilare questo, ho dovuto aggiungere un )on 19; passare substra substringsu 38; inizializzare idxa qualcosa nel run()metodo.
Geobits
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.