热土豆推销员


23

给定点列表,找到访问所有点并返回起点的最短路径。

旅行商问题是在计算机科学领域的知名,因为有很多方法来计算/近似它。它已经解决了非常多的问题,但是其中一些最大的问题需要很多CPU年才能完成。

不要被土豆烧死。

Hot Potato游戏是2位以上的玩家在播放音乐的同时绕过一个“土豆”的游戏。目的是将其快速传递给下一个玩家。如果在音乐停止播放时握住马铃薯,那就出门了。


Hot Potato Salesman的对象是:

给定一组100个唯一点,请以更好的顺序返回这些点(较短的总距离(进一步定义)。这会将问题“传递”给下一个玩家。他们必须对其进行改进,然后再传递给下一个,依此类推。如果一个玩家无法对其进行改进,则他们将退出比赛并继续比赛,直到剩下一名玩家为止。

为了避免这种竞争成为“暴力之路”竞赛,有以下规定:

  • 您传递马铃薯的时间不能超过一分钟。如果一分钟后还没有找到并通过一个较短的解决方案,那么您就出局了。

  • 您不能更改超过25点的位置。确切地说,>= 75分数必须与收到分数的位置相同。决定更改哪些内容与您更改的金额无关紧要。

当只剩下一名球员时,他就是那场比赛的获胜者,并获得一分。锦标赛由5*n游戏组成n,玩家人数在哪里。每场比赛,起始玩家将被轮换,其余的玩家顺序将被洗牌。最后得分最高的玩家是比赛的获胜者。如果比赛以并列第一名结束,则只有那些参赛者才能参加新的比赛。这将一直持续到没有平局为止。

每个游戏的起始玩家将收到一组伪随机选择的点,顺序不特定。

点定义为x,y笛卡尔网格上的一对整数坐标。距离使用测量曼哈顿距离|x1-x2| + |y1-y2|。所有坐标都将在[0..199]范围内。

输入项

输入带有单个字符串参数。它将由201个逗号分隔的整数组成,这些整数代表当前玩家(m)的数量和100分:

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

这些点的顺序是当前路径。总距离是通过将每个点到下一个(dist(0,1) + dist(1,2) + ... + dist(99,0))的距离相加得出的。计算总距离时,不要忘记返回开始!

请注意,m不是的球员在比赛开始的数字,它是仍处于数量。

输出量

输出与输入减负的方式相同m;单个字符串,其中包含用逗号分隔的整数,这些整数表示其新顺序的点。

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

控制程序将仅等待一分钟的输出。收到输出后,它将验证:

  • 输出格式正确
  • 输出包括所有的100个点中的输入呈现
  • >=75 点位于其原始位置
  • 路径长度小于上一个路径

如果这些检查中的任何一项失败(或没有输出),则说明您退出比赛了,游戏将继续进行到下一位玩家。

控制程序

您可以在此链接中找到控制程序。控制程序本身是确定性的,并带有一个虚拟种子1。计分过程中使用的种子会有所不同,因此不要费心尝试分析它吐出的转弯顺序/点列表。

主要的课程是Tourney。运行该程序将进行完整的锦标赛,并以参赛者作为参数。它吐出每场比赛的获胜者,并在末尾统计。具有两个SwapBots的示例Tourney看起来像:

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

如果您想一次只测试一个游戏,则可以运行Game该类。这将按照给定参数的顺序与玩家进行一场比赛。默认情况下,它还将打印逐个播放的内容,显示当前播放器和路径长度。

此外,还包括一些测试玩家:SwapBotBlockPermuter,和TwoSwapBot。前两个不会包含在评分运行中,因此可以在测试过程中随意使用和滥用它们。TwoSwapBot 包括在评选中,而且他也没有懈怠,因此请带上您的A游戏。

杂记

  • 您无法保存状态信息,并且每个回合都是程序的单独运行。每回合您将获得的唯一信息是积分集。

  • 您不能使用外部资源。这包括网络呼叫和文件访问。

  • 您不能使用旨在解决/协助TSP问题或其变体的库函数。

  • 您不能以任何方式操纵或干扰其他玩家。

  • 您不能以任何方式操纵或干扰控制程序或任何包含的类或文件。

  • 允许多线程。

  • 每个用户一个提交。如果您提交多个条目,我将仅输入第一个提交的条目。如果要更改提交,请编辑/删除原始文件。

  • 比赛将在Ubuntu 13.04上,具有i7-3770K CPU和16GB RAM 的计算机上运行。它不会在VM中运行。我认为是恶意的任何内容都会立即取消您提交的当前将来条目的资格。

  • 所有条目都必须可以使用免费软件(如Beer)从命令行运行。如果我在编译/运行您的条目时遇到问题,我将在评论中寻求帮助。如果您没有回应,或者我最终无法使它运行,它将被取消参赛资格。

结果(2014年5月22日)

有新结果!UntangleBot击败了竞争对手。TwoSwapBot取得了7场胜利,SANNbot也获得了胜利。这是一个计分板,以及指向原始输出链接

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

目前而言UntangleBot赢得了选中标记。但是,请不要阻止您参加比赛,因为随着更多参赛者出现,我将举办比赛,并相应地更改已接受的答案。


评论已清除。如果有任何可能丢失的信息,请通知我。
门把手

曼,为什么这个挑战必须在我的期末考试中完成(最终,要通过学校考试完成)。您肯定是个不好的计划者Geobits;)那时很奇怪/很奇怪,有成千上万的“山丘之王”问题,现在没有了(如果一次只回答一个问题,可能会更好一些)提示)
Herjan 2014年

@Herjan随时尝试解决当前的难题。当新的参赛者出现时,我将再次举办比赛,所以比赛还没有结束。击败SirDarius,这可能会刺激他或其他人击败您,为您带来生命;)
Geobits 2014年

@Herjan请务必提交条目!我相信这里还有很多改进的空间。这里的大多数解决方案(包括我的解决方案)都不依赖于针对此问题的聪明算法。
SirDarius 2014年

我认为修改限制存在问题。有时需要修改整个数据集以获得更好的解决方案。
Ilya Gazman 2014年

Answers:


8

UntangleBot(以前的NiceBot)

使用两种策略的C ++ 11机器人。
首先,它将通过检测路径之间的交点(小于25个点)来尝试使该路径“解缠结”(因为解缠意味着修改其间的所有点)。
如果第一个策略失败,它将随机交换点以查找更好的距离,直到找到更好的路径。

在我的测试锦标赛中,该机器人始终以大约5胜的比率击败TwoSwapBot,一败涂地。

// 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;
}

你把我击败了19分钟!
2014年

从今天的结果来看,这应该有更多的支持。
Geobits,2014年

@Geobits我仍然对我想到的最简单的东西表现如此出色感到惊讶。希望更多具有挑战性的参赛者能参加!
SirDarius 2014年

@SirDarius好的,很好。有一点挑战。
Geobits,2014年

4

桑博

R中的模拟退火机器人)

应该用来调用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=","))

这个想法相对简单:每回合是模拟退火的一个“冷却步骤”,游戏中仍在游戏中的玩家人数为“温度”(由超过12000的当前距离修改,即大致为初始距离)。正确模拟退火的唯一区别在于,我检查了我是否置换了25个以上的元素,并且如果我用完了20个动作,但所得序列比初始序列有价值,那么它将重新开始。


@Geobits啊,抱歉,删除了意外定义init_s的那一行(好吧“意外”:我看到这行,以为“为什么又在这里?”并删除了它:))。已更正。
plannapus 2014年

我尝试使用您的程序java Tourney "java Swapbot" "Rscript SANNbot.R",它似乎可以工作。
plannapus 2014年

是的,它现在似乎正在工作。
Geobits,2014年

从理论上讲(如果我没有完全误解),当有更多玩家进入游戏时,它应该会表现更好。
plannapus 2014年

照原样,由于我的测试中“更改了太多点”,所以该程序总是提前退出。如果我提高了u检查限制,就不会发生(它的性能要好得多)。虽然您的代码看起来很简单,但我不了解R的怪癖,因此我无法确定逻辑是否错误。(最新版本的控制器会给出有关机器人为何在运行时熄灭的消息Game,这样可能有助于查明问题所在)
Geobits,2014年

4

BozoBot

利用Bozosort背后的复杂逻辑找到一条更好的道路。看起来像这样。

  • 交换随机点
  • 如果我们有所改善
    • 返回答案
  • 除此以外
    • 再试一次

BozoBot现在已通过多线程进行了改进!四个小兵现在漫无目的地玩弄积分,直到偶然发现了一个改进。第一个找到解决方案的人得到了一个cookie!

显然我在多线程上失败了。

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

恩……您不应该将奴才放进一个线程中,而不是调用run()吗?AFAIK这不是多线程……
CommonGuy 2014年

@Manu你抓到我了!我试图自己学习,但失败了。有指针吗?
Rainbolt 2014年

我认为应该是new Thread(new Minion()).start()
CommonGuy

1
@Manu谢谢。显然,我在编码时只阅读了本教程的一半。
Rainbolt 2014年

3

TwoSwapBot

升级到SwapBot,此人会检查每对交换。首先,他检查是否有任何一次交换会缩短路径。如果是这样,它将立即返回。如果不是,他会检查每个对象,看看是否有另一个交换可以缩短它。如果没有,他就死了。

虽然路径仍是半随机的,但通常会在100毫秒内返回。如果他必须检查每个2交换(大约25M),则大约需要20秒。

在提交时,这在测试回合中击败了所有其他竞争对手。

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

穿线器

这个机器人

  1. 将100点分成4 1025 10
  2. 为每个零件启动一个线程
  3. 在线程中,随机调整数组,同时保持起点和终点固定
  4. 如果新数组的距离较短,请保留它
  5. 59s之后,主线程收集结果并将其打印出来

这样做的目的是找到对路径的最佳改进,以使其他漫游器因其逻辑失败。

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
注意:我更改println为,print以消除输出末尾的换行符。否则会崩溃。
Geobits

更改printlnprint并使线程数动态化。现在它以10个线程开始...
CommonGuy 2014年

1

分而治之+贪婪的机器人

注意:我查看了您的代码,Game其中在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++;

但是,没有 catch(NumberFormatException)障碍,因此当播放器程序输出一个字符串时,您的程序可能会崩溃(如我的程序main方法结尾所示)。我建议您解决此问题,因为程序可能会输出异常,堆栈跟踪等。否则,请在运行程序之前注释掉该行。

返回主题

此实现(在Java中)将点列表分为25个块,在列表上随机间隔。然后,它创建线程以缩短每个块中各点之间的路径(因此,“划分并征服”)。主线程监视其他线程,并确保在时限内提供最短的解决方案。如果一个线程死于有或没有解决方案,它将在另一个块上再次启动另一个线程。

每个线程都使用“贪心”算法,该算法从一个随机点开始,一直到最接近的点,然后重复进行直到所有点都被覆盖为止(因此称为“贪心”)。

笔记

  • 这将持续一整分钟(我花了3秒钟进行程序启动/关闭和JVM启动-您永远都不知道接下来会遇到什么JVM启动例程...)
  • 即使找到了解决方案,它也会继续搜索,并且等待一分钟后,它将提供找到的最佳解决方案。
  • 我不确定此实现是否真的有用。我虽然编码很有趣:)
  • 由于很多事情都是随机的,因此对于相同的输入可能不会提供相同的输出。

只需编译并使用java DivideAndConquer.class即可运行。

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

你相信吗?我提交此文件时,SX要求我提供验证!这看起来像机器人吗?认真吗
DankMemes,2014年

1
修复了NFException已推送的问题。现在它将只是杀死播放器,而不是杀死程序。
Geobits,2014年

作为记录,我认为它不会在您的“ 嘿,我想知道... ”行上崩溃了。<200在尝试解析之前,它会检查是否有令牌。无论如何还是要检查一下。
Geobits,2014年

@Geobits哈哈没有意识到这一点
DankMemes 2014年

注意:要对此进行编译,我必须)在第19行添加一个;更改substrsubstring38;初始化idxrun()方法中的某些内容。
Geobits 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.