Vendeur de pommes de terre chaudes

23

À partir d'une liste de points, trouvez le chemin le plus court qui visite tous les points et revient au point de départ.

Le problème des vendeurs ambulants est bien connu dans le domaine de l'informatique, de même que de nombreuses façons de le calculer / l'approcher. Il a été résolu pour de très grands groupes de points, mais certains des plus grands prennent plusieurs années CPU à terminer.

Ne vous brûlez pas avec la pomme de terre.

Hot Potato est un jeu où plus de 2 joueurs passent une "pomme de terre" en cercle pendant que la musique joue. Le but est de le passer rapidement au joueur suivant. Si vous tenez la pomme de terre lorsque la musique s'arrête, vous êtes absent.


L'objet de Hot Potato Salesman est:

Étant donné un ensemble de 100 points uniques , renvoyez ces points dans un meilleur ordre ( distance totale plus courte telle que définie plus bas ). Cela "passera" le problème au joueur suivant. Ils doivent l'améliorer et le passer au suivant, etc. Si un joueur ne peut pas l'améliorer, il est retiré et le jeu continue jusqu'à ce qu'il ne reste qu'un joueur.

Pour éviter que cela ne soit un concours "force-moi-un-chemin", il y a ces stipulations:

  • Vous ne pouvez pas prendre plus d' une minute pour passer la pomme de terre. Si vous n'avez pas trouvé et adopté une solution plus courte au bout d'une minute, vous êtes absent.

  • Vous ne pouvez pas modifier la position de plus de 25 points. Pour être exact, les >= 75points doivent être dans la même position que vous les avez reçus. Peu importe que ceux que vous décidez de changer, juste le montant que vous changez.

Lorsqu'il ne reste qu'un joueur, il est le vainqueur de cette partie et reçoit un point. Un tournoi se compose de 5*njeux, où nest le nombre de joueurs. À chaque partie, le premier joueur sera mis en rotation et l'ordre des joueurs restants sera mélangé . Le joueur avec le plus de points à la fin est le vainqueur du tournoi. Si le tournoi se termine par une égalité pour la première place, un nouveau tournoi sera joué avec seulement ces concurrents. Cela continuera jusqu'à ce qu'il n'y ait pas d'égalité.

Le premier joueur de chaque partie recevra un ensemble de points sélectionnés de manière pseudo-aléatoire sans ordre particulier.

Les points sont définis comme une paire de x,ycoordonnées entières sur une grille cartésienne. La distance est mesurée en utilisant la distance de Manhattan , |x1-x2| + |y1-y2|. Toutes les coordonnées se situeront dans la [0..199]plage.

Contribution

L'entrée est donnée avec un seul argument de chaîne. Il sera composé de 201 entiers séparés par des virgules représentant le nombre de joueurs actuels ( m) et 100 points:

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

L'ordre de ces points est le chemin actuel. La distance totale est obtenue en ajoutant la distance de chaque point au suivant ( dist(0,1) + dist(1,2) + ... + dist(99,0)). N'oubliez pas de revenir au début lors du calcul de la distance totale!

Notez que ce mn'est pas le nombre de joueurs qui ont commencé le jeu, c'est le nombre qui est encore en jeu.

Sortie

La sortie est donnée de la même manière que l'entrée, moins m; une chaîne unique contenant des entiers séparés par des virgules représentant les points dans leur nouvel ordre.

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

Le programme de commande attendra la sortie pendant une minute seulement. Une fois la sortie reçue, il vérifiera que:

  • la sortie est bien formée
  • la sortie se compose de seulement et tous les 100 points présents dans l'entrée
  • >=75 les points sont dans leur position d'origine
  • la longueur du chemin est inférieure au chemin précédent

Si l'une de ces vérifications échoue (ou s'il n'y a pas de sortie), vous êtes absent et le jeu passe au joueur suivant.

Programme de contrôle

Vous pouvez trouver le programme de contrôle sur ce lien . Le programme de contrôle lui-même est déterministe et est affiché avec une graine factice de 1. La graine utilisée lors de la notation sera différente, alors ne vous embêtez pas à essayer d'analyser l'ordre des tours / listes de points qu'elle crache.

La classe principale est Tourney. L'exécution de ce tournoi fera un tournoi complet avec des candidats donnés comme arguments. Il recrache le vainqueur de chaque match et un décompte à la fin. Un exemple de tournoi avec deux SwapBots ressemble à:

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 vous souhaitez tester un seul jeu à la fois, vous pouvez exécuter la Gameclasse à la place. Cela se déroulera un jeu avec les joueurs dans l'ordre donné comme arguments. Par défaut, il imprimera également un jeu par jeu montrant le lecteur actuel et la longueur du chemin.

Sont également inclus quelques joueurs de test: SwapBot, BlockPermuteret TwoSwapBot. Les deux premiers ne seront pas inclus dans les courses de notation, alors n'hésitez pas à les utiliser et à en abuser pendant les tests. TwoSwapBot sera inclus dans le jugement, et il n'est pas en reste, alors apportez votre A-game.

Recueil

  • Vous ne pouvez pas enregistrer les informations d'état et chaque tour est une exécution distincte de votre programme. La seule information que vous recevrez à chaque tour est l'ensemble de points.

  • Vous ne pouvez pas utiliser de ressources externes. Cela inclut les appels réseau et l'accès aux fichiers.

  • Vous ne pouvez pas utiliser les fonctions de bibliothèque conçues pour résoudre / aider avec le problème TSP ou ses variantes.

  • Vous ne pouvez en aucun cas manipuler ou interférer avec les autres joueurs.

  • Vous ne pouvez en aucun cas manipuler ou interférer avec le programme de contrôle ou les classes ou fichiers inclus.

  • Le multi-threading est autorisé.

  • Une soumission par utilisateur. Si vous soumettez plus d'une entrée, je n'entrerai que la première soumise. Si vous souhaitez modifier votre soumission, modifiez / supprimez l'original.

  • Le tournoi se déroulera sur Ubuntu 13.04, sur un ordinateur avec un processeur i7-3770K et 16 Go de RAM. Il ne sera pas exécuté sur une machine virtuelle. Tout ce que je perçois comme malveillant disqualifiera immédiatement la candidature actuelle et future que vous soumettez.

  • Toutes les entrées doivent être exécutables à partir de la ligne de commande avec un logiciel gratuit ( comme dans la bière ). Si j'ai des problèmes pour compiler / exécuter votre entrée, je demanderai de l'aide dans les commentaires. Si vous ne répondez pas ou que je ne parviens pas à le faire fonctionner, il sera disqualifié.

Résultats (22 mai 2014)

De nouveaux résultats sont arrivés! UntangleBot a battu la concurrence assez bien. TwoSwapBot a réussi sept victoires et SANNbot a également remporté une victoire. Voici un tableau de bord et un lien vers la sortie brute :

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

À l' heure actuelle , UntangleBot a remporté la coche. Ne laissez pas cela vous décourager de participer, car je dirigerai le tournoi à mesure que de plus en plus de participants apparaîtront et modifier la réponse acceptée en conséquence.

Géobits
la source
Commentaires purgés. Veuillez m'informer de toute perte éventuelle d'informations.
Poignée de porte
Homme pourquoi ce défi devait-il être lors de mes examens finaux (enfin, fait avec l'école oui). Vous êtes certainement un mauvais planificateur Geobits;) Bizarre / assez tristement à ce moment-là, il y avait des tonnes de questions royales et maintenant il n'y en a plus (peut-être que cela fonctionne mieux s'il n'y en a qu'une à la fois, indice indice) ...
Herjan
@Herjan N'hésitez pas à essayer de vous attaquer au champion actuel. Je dirigerai à nouveau le tournoi à mesure que de nouveaux candidats apparaissent, donc le concours n'est pas terminé ou quoi que ce soit. Battez SirDarius et cela pourrait l'inciter, vous ou quelqu'un d'autre, à battre le vôtre, lui redonner vie;)
Geobits
@Herjan Veuillez soumettre une entrée! Je pense qu'il y a beaucoup de place à l'amélioration ici. La plupart des solutions ici, y compris la mienne, ne reposent pas sur des algorithmes intelligents spécifiques à ce problème.
SirDarius
Je pense qu'il y a un problème avec la limitation des modifications. Comme parfois, il faudra modifier l'ensemble des données pour obtenir une meilleure solution.
Ilya Gazman du

Réponses:

8

UntangleBot (anciennement NiceBot)

Un bot C ++ 11 qui utilise deux stratégies.
Dans un premier temps, il tentera de "démêler" le chemin si possible, en détectant les intersections entre les chemins qui sont plus proches de 25 points (car le démêlage implique de modifier tous les points intermédiaires).
Si la première stratégie échoue, elle échange aléatoirement des points pour trouver de meilleures distances jusqu'à ce qu'un meilleur chemin soit trouvé.

Ce bot bat systématiquement TwoSwapBot avec un ratio approximatif de cinq victoires pour une défaite dans mes tournois de test.

// 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
la source
Tu m'as battu de 19 minutes!
Rainbolt
Cela devrait avoir plus de votes positifs, à en juger par les résultats d'aujourd'hui.
Geobits
@Geobits Je suis toujours surpris que la chose la plus simple que j'aie imaginée fonctionne si bien. En espérant que des candidats plus exigeants participeront!
SirDarius
@SirDarius D'accord, très bien . Ayez un peu de défi.
Geobits
4

SANNbot

(un robot de recuit simulé dans R )

Doit être appelé avec 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'idée est relativement simple: chaque tour est une "étape de refroidissement" d'un recuit simulé avec le nombre de joueurs encore en jeu comme "température" (modifié par la distance actuelle sur 12000, soit à peu près la distance initiale). La seule différence avec un recuit simulé approprié est que j'ai vérifié que je ne permute pas plus de 25 éléments et que si j'ai utilisé 20 mouvements mais que la séquence résultante vaut plus que l'initiale, elle recommence.

plannapus
la source
@Geobits ah désolé supprimé la ligne où init_s a été défini par accident (enfin "accident": j'ai vu la ligne et j'ai pensé "pourquoi est-elle ici encore?" Et l'ai supprimée :)). Corrigée.
plannapus
J'ai essayé votre programme en utilisant java Tourney "java Swapbot" "Rscript SANNbot.R"et cela a semblé fonctionner.
plannapus
Oui, cela semble fonctionner maintenant.
Geobits
En théorie (si je ne me trompe pas complètement), cela devrait fonctionner mieux lorsque plus de joueurs entrent dans le jeu.
plannapus
En l'état, ce programme sort toujours tôt en raison de «trop de points modifiés» dans mes tests. Si j'augmente les ulimites de vérification, cela ne se produit pas (et cela fonctionne beaucoup mieux). Bien que votre code semble assez simple, je ne connais pas les bizarreries de R, donc je ne peux pas dire si la logique est erronée. (la dernière version du contrôleur donnera des messages sur les raisons pour lesquelles un bot s'éteint lors de l'exécution Game, ce qui pourrait aider à identifier le problème)
Geobits
4

BozoBot

Utilise la logique complexe derrière Bozosort pour trouver un meilleur chemin. Cela ressemble à ceci.

  • Échanger des points aléatoires
  • Si nous nous améliorions
    • Renvoyez la réponse
  • Autrement
    • Réessayer

BozoBot a maintenant été amélioré avec le multithreading ! Quatre serviteurs jonglent maintenant sans but avec des points jusqu'à ce qu'ils tombent sur une amélioration. Le premier à trouver une solution obtient un cookie!

Apparemment, j'échoue au 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);
    }
}
Rainbolt
la source
Ehhmm ... Ne devriez-vous pas mettre vos serviteurs dans un thread au lieu d'appeler run ()? AFAIK ce n'est pas du multithreading ...
CommonGuy
@Manu Vous m'avez attrapé! J'ai essayé de l'apprendre par moi-même et j'ai échoué. Des pointeurs?
Rainbolt
Je pense que ça devrait êtrenew Thread(new Minion()).start()
CommonGuy
1
@Manu Merci. Apparemment, je n'ai lu que la moitié de ce tutoriel lorsque je codais.
Rainbolt
3

TwoSwapBot

Une mise à niveau vers SwapBot, ce type vérifie chaque paire de swaps. Tout d'abord, il vérifie si un seul échange raccourcira le chemin. Si c'est le cas, il revient immédiatement. Sinon, il vérifie chacun pour voir si un autre échange le raccourcira. Sinon, il meurt.

Bien que le chemin soit encore semi-aléatoire, il revient normalement dans environ 100 ms. S'il doit vérifier tous les 2 swaps (environ 25M), cela prend environ 20 secondes.

Au moment de la soumission, cela battait tous les autres concurrents dans les manches d'essai.

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);}
    }
}
Géobits
la source
2

Enfileur

Ce bot

  1. Divise les 100 points en 4 10 morceaux de 25 10 points
  2. Commence un fil pour chaque pièce
  3. Dans le thread, mélangez aléatoirement le tableau, tout en conservant les points de début et de fin fixes
  4. Si la distance du nouveau tableau est plus courte, conservez-le
  5. Après 59 secondes, le thread principal recueille les résultats et les imprime

L'idée est de trouver la meilleure amélioration du chemin, afin que les autres bots échouent avec leur logique.

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
la source
2
Remarque: j'ai changé printlnpour printsupprimer la nouvelle ligne à la fin de votre sortie. Sinon, il se bloque.
Geobits
Changé printlnpour printet fait le fil de comptage dynamique. Ça commence maintenant avec 10 threads ...
CommonGuy
1

Diviser et conquérir + Bot gourmand

REMARQUE: j'ai regardé votre code Gamequi comprend les éléments suivants dans 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++;

Cependant, il n'y a pas de catch(NumberFormatException)bloc, donc votre programme se bloquera probablement lorsqu'un programme de joueur sort une chaîne (comme démontré à la fin de la mainméthode de mon programme ). Je vous conseille de résoudre ce problème, car les programmes peuvent générer des exceptions, des traces de pile ou similaires. Sinon, commentez cette ligne dans mon programme avant de l'exécuter.

Retour au sujet

Cette implémentation (en Java) divise la liste de points en blocs de 25, espacés de manière aléatoire sur la liste. Ensuite, il crée des threads pour raccourcir le chemin entre les points de chaque bloc (d'où "Diviser pour régner"). Le thread principal surveille les autres et s'assure de présenter la solution la plus courte dans le délai imparti. Si un thread meurt avec ou sans solution, il démarre à nouveau un autre thread sur un bloc différent.

Chaque thread utilise l'algorithme "gourmand", qui commence sur un point aléatoire, va au point le plus proche et se répète jusqu'à ce que tous les points soient couverts (Par conséquent, "gourmand").

Remarques

  • Cela durera toute la minute (j'ai donné 3 secondes pour le démarrage / l'arrêt du programme et le démarrage de la JVM - vous ne savez jamais sur quoi les routines de démarrage de la JVM sont rattrapées ...)
  • Même s'il a trouvé des solutions, il continuera à chercher et après 1 minute, il présente la meilleure solution qu'il a trouvée.
  • Je ne sais pas si cette implémentation est réellement bonne. Je me suis bien amusé à le coder :)
  • Étant donné que beaucoup de choses sont aléatoires, cela peut ne pas donner la même sortie pour la même entrée.

Compilez et utilisez java DivideAndConquer.classpour exécuter.

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
la source
Peux-tu le croire? SX m'a demandé un captcha lorsque j'ai soumis cela! Cela vous semble-t-il conçu? Sérieusement?
DankMemes
1
Correction de NFException poussée. Il va maintenant simplement tuer le joueur au lieu de tuer le programme.
Geobits
Pour mémoire, je ne pense pas que ça se serait écrasé sur votre ligne " Hey, je me demande si ... " de toute façon. Il vérifie s'il y a des <200jetons avant d'essayer l'analyse. Encore mieux de le vérifier de toute façon.
Geobits
@Geobits haha ​​ne s'en rendait pas compte
DankMemes
Remarque: Pour obtenir cela à compiler, j'ai dû ajouter un )sur la ligne 19; changer substrpour substringle 38; initialiser idxà quelque chose dans la run()méthode.
Géobits