ホットポテトセールスマン


23

点のリストが与えられたら、すべての点を訪れて開始点に戻る最短経路を見つけます。

巡回セールスマン問題として計算/それを近似する多くの方法があり、コンピュータサイエンスの分野ではよく知られています。非常に大きなポイントのグループについては解決されていますが、最大のもののいくつかは完了するまでに多くのCPU年を要します。

じゃがいもに火傷しないでください。

Hot Potatoは、2人以上のプレーヤーが音楽を再生しながら「じゃがいも」を一周するゲームです。オブジェクトは、次のプレーヤーにすばやく渡すことです。音楽が停止したときにジャガイモを保持している場合、あなたは外出している。


Hot Potato Salesmanのオブジェクトは次のとおりです。

100個の一意のポイントのセットが与えられた場合、それらのポイントをより良い順序で返します(さらに下に定義されているように短い合計距離)。これにより、問題は次のプレーヤーに「渡されます」。彼らはそれを改善し、次のようにそれを渡す必要があります。プレーヤーがそれを改善できない場合、彼らは外に出て、一人のプレーヤーが去るまでプレイを続けます。

これが「総当たり戦」の競争にならないようにするために、次の規定があります。

  • ジャガイモを渡すのに1分以上かかることはありません。1分が経過するまでに短い解決策を見つけて合格しなかった場合は、外出します。

  • 25ポイントを超える位置は変更できません。正確には、>= 75ポイントは受け取った位置と同じでなければなりません。どちらを変更するかは問題はなく、変更する量だけです。

プレイヤーが1人しか残っていない場合、そのプレイヤーがそのゲームの勝者となり、1ポイントを獲得します。トーナメントは5*nゲームで構成されnます。ここで、プレイヤーの数です。各ゲーム、開始プレイヤーが回転し、残りのプレイヤーの順番がシャッフルされますます。最後にポイントが最も多いプレイヤーがトーナメントの勝者です。トーナメントが1位タイで終了した場合、新しいトーナメントはそれらの競技者のみでプレイされます。同点がなくなるまでこれが続きます。

各ゲームの開始プレーヤーは、特定の順序で疑似ランダムに選択されたポイントのセットを受け取ります。

ポイントはx,y、デカルトグリッド上の整数座標のペアとして定義されます。距離は、マンハッタン距離を使用して測定され|x1-x2| + |y1-y2|ます。すべての座標は[0..199]範囲内にあります。

入力

入力は単一の文字列引数で与えられます。現在のプレイヤー数(m)と100ポイントを表す201個のコンマ区切り整数で構成されます。

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

制御プログラムは、1分間だけ出力を待機します。出力を受信すると、次のことを確認します。

  • 出力は整形式です
  • 出力は、から構成のみ及び全ての入力で100点、本
  • >=75 ポイントは元の位置にあります
  • パスの長さが前のパスより短い

これらのチェックのいずれかが失敗した(または出力がなかった)場合、あなたは退出し、ゲームは次のプレイヤーに進みます。

制御プログラム

このリンクで制御プログラム見つけることができます。制御プログラム自体は決定論的であり、ダミーのシードとともに投稿されます1ます。スコアリング中に使用されるシードは異なるため、吐き出す順番やポイントのリストを分析しようとしないでください。

メインクラスはTourneyです。これを実行すると、引数として与えられた競技者との完全なトーナメントが行われます。それは各ゲームの勝者と最後に集計を吐き出します。2つのSwapBotを使用したサンプルトーナメントは次のようになります。

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

一度に1つのゲームのみをテストする場合は、Game代わりにクラスを実行できます。これにより、引数として指定された順序でプレイヤーと1つのゲームが実行されます。デフォルトでは、現在のプレーヤーとパスの長さを示す再生モードも出力します。

また、付属いくつかのテストプレイヤーは、次のとおりですSwapBotBlockPermuterTwoSwapBot。最初の2つはスコアリングの実行に含まれないため、テスト中にそれらを自由に使用して悪用してください。TwoSwapBot 意志判断に含まれており、彼はそう、あなたのA-ゲームを持って、何も前かがみませんこと。

その他

  • 状態情報を保存することはできず、各ターンはプログラムの個別の実行です。各ターンで受け取る唯一の情報はポイントのセットです。

  • 外部リソースは使用できません。これには、ネットワークコールとファイルアクセスが含まれます。

  • TSP問題またはその変形を解決/支援するために設計されたライブラリ関数は使用できません。

  • 他のプレイヤーを操作したり、干渉したりすることは一切できません。

  • 制御プログラムまたは含まれているクラスやファイルを操作したり、干渉したりすることは一切できません。

  • マルチスレッドが許可されています。

  • ユーザーごとに1つのサブミッション。複数のエントリを送信する場合、最初に送信されたエントリのみを入力します。提出物を変更する場合は、オリジナルを編集/削除してください。

  • トーナメントは、Ubuntu 13.04、i7-3770K CPUおよび16GB RAMを搭載したコンピューターで実行されます。VMでは実行されません。悪意があると私が感じるものは、あなたが提出した現在および将来のエントリを直ちに失格させます。

  • すべてのエントリは、無料の(ビールのように)ソフトウェアを使用してコマンドラインから実行できる必要があります。エントリのコンパイル/実行で問題が発生した場合は、コメントに支援を求めます。応答しない場合、または最終的に実行できない場合は、失格となります。

結果(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はチェックマークを獲得しました。ただし、参加者が出てきたらトーナメントを開催し、それに応じて受け入れられる答えを変更するので、入場を妨げないでください。


コメントを削除しました。失われた可能性のある情報については私に通知してください。
ドアノブ

なぜこの挑戦をしたのかは、私の最終試験中でなければなりません(最終的には学校で行われます)。あなたは確かに悪いプランナージオビットです;)その時、奇妙な/悲しいことにたくさんの王様の質問がありましたが、今は何もありません(一度に1つだけあれば良いかもしれません、ヒントヒント)...
Herjan

@Herjan現在のチャンピオンに挑戦してみてください。新しい出場者が現れたら、トーナメントをもう一度実行します。そのため、コンテストは終わっていません。SirDariusを倒すと、彼または他の誰かがあなたを打ち負かし、命を吹き込むかもしれません;)
Geobits

@Herjanエントリーを送信してください!ここには多くの改善の余地があると思います。私のソリューションを含むここでのソリューションのほとんどは、この問題に固有の巧妙なアルゴリズムに依存していません。
サーダリウス

変更の制限に問題があると思います。場合によっては、データセット全体を変更してより良いソリューションを得ることが必要になります。
イリヤガスマン14

回答:


8

UntangleBot(以前のNiceBot)

2つの戦略を使用するC ++ 11ボット。
最初は、可能であれば、25ポイントより近いパス間の交差を検出することにより、パスの「もつれを解こう」しようとします(もつれを解くことは、その間のすべてのポイントを変更することを意味するため)。
最初の戦略が失敗すると、より良いパスが見つかるまで、ランダムにポイントを交換してより良い距離を見つけます。

このボットは一貫してTwoSwapBotに勝ち、私のテストトーナメントでの1回の損失に対しておよそ5勝の割合でした。

// 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分倒した!
レインボルト

これには、今日の結果から判断して、より多くの賛成票があるはずです。
ジオビット

@Geobits私が思いついた最も単純なものがとてもうまく機能していることにまだ驚いています。より挑戦的な競技者が参加することを願っています!
サーダリウス

@SirDariusさて、結構です。持って挑戦を少し。
ジオビット

4

SANNbot

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=","))

考え方は比較的単純です。各ターンは、ゲーム内にまだいるプレイヤーの数を「温度」として、シミュレートされたアニーリングの 1つの「冷却ステップ」です(12000を超える現在の距離、つまりおおよそ初期距離)。適切なシミュレーテッドアニーリングとの唯一の違いは、25を超える要素を並べ替えないことを確認し、20の動きを使い果たしたが、結果のシーケンスが最初よりも価値がある場合、最初からやり直すことです。


@Geobitsは、init_sが誤って定義された行を削除して申し訳ありません(よく「事故」:行を見て、「なぜまたここにいるの?」と思って削除しました:))。修正しました。
プランナパス

私はあなたのプログラムを使って試しjava Tourney "java Swapbot" "Rscript SANNbot.R"ましたが、うまくいくようです。
プランナパス

うん、今は動いているようだ。
ジオビット

理論的には(完全に誤解していない場合)、より多くのプレイヤーがゲームに参加したときにパフォーマンスが向上するはずです。
プランナパス

現状では、このプログラムは、テストで「変更されたポイントが多すぎます」ため、常に早期に終了します。私はバンプ場合はu、チェックの制限を、これは発生しません(それははるかに良い行い)。あなたのコード非常に簡単に見えますが、Rの癖を知らないため、ロジックが間違っているかどうかはわかりません。(コントローラの最新バージョンが実行しているときにボットが出て行く理由に関するメッセージを与えるGameかもしれないのヘルプは、問題を特定だから、)
Geobits

4

BozoBot

Bozosortの背後にある複雑なロジックを利用して、より良いパスを見つけます。こんな感じです。

  • ランダムポイントを交換する
  • 改善したら
    • 答えを返す
  • さもないと
    • 再試行する

BozoBotはマルチスレッドで改善されました!4人のミニオンは、改善に出くわすまでポイントをジャグリングします。最初に解決策を見つけると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);
    }
}

Ehhmm ... run()を呼び出す代わりに、ミニオンをスレッドに入れるべきではありませんか?私の知る限り、これはマルチスレッドされていません...
CommonGuy

@マヌあなたは私を捕まえた!私は自分でそれを学ぼうとしたが失敗した。ポインタはありますか?
レインボルト

私はそれがあるべきだと思うnew Thread(new Minion()).start()
CommonGuy

1
@Manuありがとう。どうやらコーディング中にこのチュートリアルの半分しか読んでいなかったようです。
レインボルト

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ポイントを25 10ポイントの4 10個に分割します
  2. 各ピースのスレッドを開始します
  3. スレッドで、開始点と終了点を固定したまま、配列をランダムにシャッフルします
  4. 新しい配列の距離が短い場合は、それを保持します
  5. 59秒後、メインスレッドは結果を収集して出力します

アイデアは、他のボットがロジックで失敗するように、パスの最適な改善を見つけることです。

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
注:出力の最後で改行を削除するように変更printlnprintました。それ以外の場合はクラッシュします。
Geobits

スレッド数を動的に変更printlnprintました。それは今... 10のスレッドで始まる
CommonGuy

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個のチャンクに分割し、リスト上でランダムに間隔を空けます。次に、スレッドを作成して、各チャンク内のポイント間のパスを短縮します(したがって、「分割して征服する」)。メインスレッドは他のスレッドを監視し、制限時間内に最短のソリューションを提示するようにします。スレッドがソリューションの有無に関係なく停止した場合、別のチャンクで別のスレッドを再び開始します。

各スレッドは「貪欲な」アルゴリズムを使用します。このアルゴリズムは、ランダムなポイントから始まり、最も近いポイントに移動し、すべてのポイントがカバーされるまで繰り返されます(したがって、「貪欲」)。

ノート

  • これは1分間完全に実行されます(プログラムの起動/シャットダウンとJVMの起動に3秒を与えました-JVMの起動ルーチンが次に何に追いつくかわかりません...)
  • 解決策を見つけたとしても、検索を続け、1分経過すると、見つかった最良の解決策を提示します。
  • この実装が実際に良いかどうかはわかりません。私はそれをコーディングするのが少し楽しかったです:)
  • 多くのものはランダムであるため、同じ入力に対して同じ出力が得られない場合があります。

コンパイルして使用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

1
プッシュされたNFExceptionの修正。プログラムを殺すのではなく、プレイヤーを殺すだけです。
ジオビット

記録のために、とにかくあなたの「ねえ、私は...」行でクラッシュしたとは思わない。<200解析を試みる前にトークンがあるかどうかをチェックします。とにかくそれを確認することをお勧めします。
ジオビット

@Geobitsハハはそれを認識していませんでした
DankMemes

注:これをコンパイルするには、)19行目にaを追加する必要がありました。38に変更substrsubstringます。メソッドでidx何かに初期化しますrun()
ジオビット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.