Vendedor de papas calientes

23

Dada una lista de puntos, encuentre el camino más corto que visite todos los puntos y regrese al punto de partida.

El problema del vendedor ambulante es bien conocido en el campo de la informática, ya que hay muchas formas de calcularlo / aproximarlo. Se ha resuelto para grupos muy grandes de puntos, pero algunos de los más grandes requieren muchos años de CPU para terminar.

No te quemes con la papa.

Hot Potato es un juego en el que 2+ jugadores pasan una "papa" en círculo mientras suena la música. El objetivo es pasarlo al siguiente jugador rápidamente. Si estás sosteniendo la papa cuando la música se detiene, estás fuera.


El objeto del vendedor de patatas calientes es:

Dado un conjunto de 100 puntos únicos , devuelva esos puntos en un mejor orden ( distancia total más corta como se define más abajo ). Esto "pasará" el problema al siguiente jugador. Tienen que mejorarlo y pasarlo al siguiente, etc. Si un jugador no puede mejorarlo, están fuera y el juego continúa hasta que quede un jugador.

Para evitar que esto sea una competencia de "fuerza bruta-me-a-camino", existen estas estipulaciones:

  • No puede tomar más de un minuto para pasar la papa. Si no ha encontrado y pasado una solución más corta para el momento en que transcurre un minuto, está fuera.

  • No puede cambiar la posición de más de 25 puntos. Para ser exactos, los >= 75puntos deben estar en la misma posición en que los recibió. No importa lo que usted decida cual el cambio, sólo la cantidad cambia.

Cuando solo queda un jugador, él es el ganador de ese juego y recibe un punto. Un torneo consiste en 5*njuegos, donde nestá el número de jugadores. Cada juego, el jugador inicial se rotará y el orden restante del jugador se barajará . El jugador con más puntos al final es el ganador del torneo. Si el torneo termina con un empate por el primer lugar, se jugará un nuevo torneo solo con los concursantes. Esto continuará hasta que no haya empate.

El jugador inicial de cada juego recibirá un conjunto de puntos seleccionados pseudoaleatoriamente sin ningún orden en particular.

Los puntos se definen como un par de x,ycoordenadas enteras en una cuadrícula cartesiana. La distancia se mide usando la distancia Manhattan , |x1-x2| + |y1-y2|. Todas las coordenadas estarán en el [0..199]rango.

Entrada

La entrada se proporciona con un argumento de cadena única. Constará de 201 enteros separados por comas que representan el número de jugadores actuales ( m) y 100 puntos:

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

El orden de estos puntos es el camino actual. La distancia total se obtiene sumando la distancia desde cada punto al siguiente ( dist(0,1) + dist(1,2) + ... + dist(99,0)). ¡No olvide volver al inicio al calcular la distancia total!

Tenga en cuenta que nom es el número de jugadores que comenzaron el juego, es el número que todavía están en.

Salida

La salida se da de la misma manera que la entrada, menos m; una sola cadena que contiene enteros separados por comas que representan los puntos en su nuevo orden.

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

El programa de control esperará la salida solo por un minuto. Cuando se recibe la salida, verificará que:

  • la salida está bien formada
  • la salida consta de solo y todos los 100 puntos presentes en la entrada
  • >=75 los puntos están en sus posiciones originales
  • la longitud de la ruta es menor que la ruta anterior

Si alguno de estos controles falla (o no hay salida), estás fuera y el juego pasará al siguiente jugador.

Programa de control

Puede encontrar el programa de control en este enlace . El programa de control en sí mismo es determinista y se publica con una semilla ficticia de 1. La semilla utilizada durante la puntuación será diferente, así que no te molestes en analizar el orden de giro / listas de puntos que escupe.

La clase principal es Tourney. Ejecutar esto hará un torneo completo con los concursantes dados como argumentos. Escupe al ganador de cada juego y una cuenta al final. Un torneo de muestra con dos SwapBots se ve así:

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

Si quieres probar solo un juego a la vez, puedes ejecutar la Gameclase en su lugar. Esto ejecutará un juego con jugadores en el orden dado como argumentos. Por defecto, también imprimirá una jugada por jugada que muestra el jugador actual y la longitud de la ruta.

También se incluyen algunos jugadores de prueba: SwapBot, BlockPermuter, y TwoSwapBot. Los dos primeros no se incluirán en las carreras de puntuación, así que siéntete libre de usarlos y abusar de ellos durante las pruebas. TwoSwapBot se incluirá en la evaluación, y él no es holgazán, así que traiga su juego A.

Miscelánea

  • No puede guardar información de estado, y cada turno es una ejecución separada de su programa. La única información que recibirá en cada turno es el conjunto de puntos.

  • No puedes usar recursos externos. Esto incluye llamadas de red y acceso a archivos.

  • No puede utilizar las funciones de biblioteca diseñadas para resolver / ayudar con el problema de TSP o sus variantes.

  • No puedes manipular o interferir con otros jugadores de ninguna manera.

  • No puede manipular o interferir con el programa de control o cualquier clase o archivo incluido de ninguna manera.

  • Se permite el subprocesamiento múltiple.

  • Un envío por usuario. Si envía más de una entrada, solo ingresaré la primera. Si desea cambiar su envío, edite / elimine el original.

  • El torneo se ejecutará en Ubuntu 13.04, en una computadora con una CPU i7-3770K y 16 GB de RAM. No se ejecutará en una VM. Cualquier cosa que perciba como maliciosa descalificará de inmediato la entrada actual y futura que envíe.

  • Todas las entradas deben ejecutarse desde la línea de comandos con software gratuito ( como en cerveza ). Si tengo problemas para compilar / ejecutar su entrada, solicitaré ayuda en los comentarios. Si no responde o finalmente no puedo hacerlo funcionar, será descalificado.

Resultados (22 de mayo de 2014)

Nuevos resultados están en! UntangleBot ha vencido a la competencia bastante bien. TwoSwapBot logró siete victorias, y SANNbot también logró una victoria. Aquí hay un marcador y un enlace a la salida sin formato :

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

Tal como está ahora , UntangleBot ha ganado la marca de verificación. Sin embargo, no dejes que eso te desanime de entrar, ya que correré el torneo a medida que aparezcan más concursantes y cambie la respuesta aceptada en consecuencia.

Geobits
fuente
Comentarios purgados. Notifíqueme por cualquier posible pérdida de información.
Pomo de la puerta
Hombre, ¿por qué este desafío tuvo que ser durante mis exámenes finales (finalmente, hecho con la escuela yeay). Seguramente eres un mal planificador Geobits;) Extraño / tristemente en ese momento había toneladas de preguntas del rey de la colina y ahora no hay ninguna (tal vez funcione mejor si solo hay una a la vez, pista) pista) ...
Herjan
@Herjan Siéntase libre de intentar enfrentarse al campeón actual. Correré el torneo nuevamente cuando aparezcan nuevos concursantes, para que el concurso no haya terminado ni nada. Derrota a SirDarius y puede incitarlo a él oa alguien más a vencer a los tuyos, devolviéndole la vida;)
Geobits
@Herjan ¡Por favor envíe una entrada! Creo que hay mucho margen de mejora aquí. La mayoría de las soluciones aquí, incluida la mía, no se basan en algoritmos inteligentes específicos para este problema.
SirDarius
Creo que hay un problema con la limitación de modificación. Como algunas veces tomará modificar todo el conjunto de datos para obtener una mejor solución.
Ilya Gazman 01 de

Respuestas:

8

UntangleBot (anteriormente NiceBot)

Un bot C ++ 11 que usa dos estrategias.
Al principio intentará "desenredar" la ruta si es posible, detectando intersecciones entre rutas que estén más cerca de 25 puntos (ya que desenredar implica modificar todos los puntos intermedios).
Si la primera estrategia falla, intercambia puntos aleatoriamente para encontrar mejores distancias hasta que se encuentre un mejor camino.

Este bot constantemente vence a TwoSwapBot con una proporción aproximada de cinco victorias por una derrota en mis torneos de prueba.

// 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;
}
SirDarius
fuente
¡Me ganaste por 19 minutos!
Rainbolt
Esto debería tener más votos a favor, a juzgar por los resultados de hoy.
Geobits
@Geobits Todavía estoy sorprendido de que la cosa más simple que se me ocurrió funcione tan bien. ¡Esperando que entren concursantes más desafiantes!
SirDarius
@SirDarius Bien, bien . Ten un poco de desafío.
Geobits
4

SANNbot

(un bot de recocido simulado en R )

Debería ser llamado 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=","))

La idea es relativamente simple: cada turno es un "paso de enfriamiento" de un recocido simulado con el número de jugadores que todavía están en el juego como "temperatura" (modificada por la distancia actual sobre 12000, es decir, aproximadamente la distancia inicial). La única diferencia con un recocido simulado adecuado es que verifiqué que no permuto más de 25 elementos y si utilicé 20 movimientos pero la secuencia resultante vale más que la inicial, entonces comienza de nuevo.

plannapus
fuente
@Geobits ah lo siento, eliminé la línea donde init_s se definió por accidente (bueno "accidente": vi la línea y pensé "¿por qué está aquí otra vez?" Y la eliminé :)). Corregido
plannapus
Intenté usar tu programa java Tourney "java Swapbot" "Rscript SANNbot.R"y parecía funcionar.
plannapus
Sí, parece estar funcionando ahora.
Geobits
En teoría (si no estoy completamente equivocado) debería funcionar mejor cuando entran más jugadores al juego.
plannapus
Tal como está, este programa siempre sale temprano debido a "demasiados puntos cambiados" en mis pruebas. Si subo los ulímites de verificación, esto no sucede (y funciona mucho mejor). Si bien su código parece bastante sencillo, no conozco las peculiaridades de R, por lo que no puedo decir si la lógica es incorrecta. (la última versión del controlador dará mensajes sobre por qué un bot se apaga cuando se ejecuta Game, por lo que podría ayudar a detectar el problema)
Geobits
4

BozoBot

Utiliza la lógica compleja detrás de Bozosort para encontrar un mejor camino. Se parece a esto.

  • Intercambiar puntos aleatorios
  • Si mejoramos
    • Devuelve la respuesta
  • De otra manera
    • Inténtalo de nuevo

¡BozoBot ahora se ha mejorado con Multithreading ! Cuatro minions ahora hacen malabares sin rumbo con puntos hasta que tropiezan con una mejora. ¡El primero en encontrar una solución recibe una cookie!

Aparentemente fallo en el multihilo.

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);
    }
}
Perno de lluvia
fuente
Ehhmm ... ¿No deberías poner a tus secuaces en un hilo en lugar de llamar a run ()? AFAIK esto no es multihilo ...
CommonGuy
@Manu ¡Me atrapaste! Traté de aprenderlo por mi cuenta y fallé. ¿Algún puntero?
Rainbolt
Creo que debería sernew Thread(new Minion()).start()
CommonGuy
1
@Manu Gracias. Aparentemente solo leí la mitad de este tutorial cuando estaba codificando.
Rainbolt
3

TwoSwapBot

Una actualización a SwapBot, este chico comprueba cada par de intercambios. Primero, verifica si cualquier intercambio único acortará el camino. Si lo hace, vuelve inmediatamente. De lo contrario, verifica cada uno para ver si otro intercambio lo acortará. Si no, él simplemente muere.

Si bien el camino aún es semi-aleatorio, normalmente regresa en unos 100 ms. Si tiene que verificar todos y cada 2 intercambios (aproximadamente 25M), le tomará unos 20 segundos.

En el momento de la presentación, esto venció a todos los demás competidores en las rondas de prueba.

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);}
    }
}
Geobits
fuente
2

Enhebrador

Este bot

  1. Divide los 100 puntos en 4 10 piezas de 25 10 puntos
  2. Inicia un hilo para cada pieza
  3. En el hilo, baraja aleatoriamente la matriz, mientras mantienes los puntos inicial y final fijos
  4. Si la distancia de la nueva matriz es más corta, manténgala
  5. Después de 59 s, el hilo principal recopila los resultados y los imprime.

La idea es encontrar la mejor mejora en el camino, para que los otros bots fallen con su lógica.

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;
    }
}
CommonGuy
fuente
2
Nota: Cambié printlna printpara deshacerme de la nueva línea al final de su salida. De lo contrario, se estrella.
Geobits
Cambiado printlna printe hizo la dinámica hilos. Ahora comienza con 10 hilos ...
CommonGuy
1

Divide and Conquer + Greedy Bot

NOTA: Miré su código, Gameque incluye lo siguiente en 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++;

Sin embargo, no hay catch(NumberFormatException)bloque, por lo que es probable que su programa se bloquee cuando un programa de jugador emite una cadena (como se demuestra al final del mainmétodo de mi programa ). Le aconsejo que solucione esto, porque los programas pueden generar excepciones, rastreos de pila o similares. De lo contrario, comente esa línea en mi programa antes de ejecutarla.

Volver al tema

Esta implementación (en Java) divide la lista de puntos en trozos de 25, espaciados aleatoriamente en la lista. Luego, crea hilos para acortar el camino entre los puntos en cada fragmento (Por lo tanto, "Divide y vencerás"). El hilo principal monitorea a los demás y se asegura de presentar la solución más corta dentro del límite de tiempo. Si un hilo muere con o sin solución, comienza otro hilo nuevamente en un trozo diferente.

Cada hilo utiliza el algoritmo "codicioso", que comienza en un punto aleatorio, va al punto más cercano y se repite hasta que todos los puntos estén cubiertos (por lo tanto, "codicioso").

Notas

  • Esto durará todo el minuto 1 (di 3 segundos para el inicio / apagado del programa y el inicio de JVM; nunca se sabe en qué se enganchan las rutinas de inicio de JVM el próximo ...)
  • Incluso si ha encontrado soluciones, seguirá buscando y después de que transcurra 1 minuto, presenta la mejor solución que encontró.
  • No estoy seguro de si esta implementación es realmente buena. Aunque me divertí un poco codificándolo :)
  • Dado que muchas cosas son aleatorias, esto puede no dar la misma salida para la misma entrada.

Simplemente compile y use java DivideAndConquer.classpara ejecutar.

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);
        }
    }
}
DankMemes
fuente
¿Puedes creerlo? ¡SX me pidió un captcha cuando envié esto! ¿Esto te parece hecho a bot? ¿Seriamente?
DankMemes
1
Arreglo para NFException empujado. Ahora solo matará al jugador en lugar de matar el programa.
Geobits
Para el registro, no creo que se hubiera estrellado en su línea " Hey, me pregunto si ... " de todos modos. Comprueba si hay <200tokens antes de intentar el análisis. Aún mejor comprobarlo de todos modos.
Geobits
@Geobits jaja no se dio cuenta de eso
DankMemes
Nota: Para que esto se compile, tuve que agregar un )en la línea 19; cambiar substra substringen 38; Inicializar idxa algo en el run()método.
Geobits