พนักงานขายมันฝรั่งร้อน


23

รับรายการคะแนนค้นหาเส้นทางที่สั้นที่สุดที่เข้าชมทุกจุดและกลับไปที่จุดเริ่มต้น

เดินทางพนักงานขายปัญหาเป็นที่รู้จักกันในสาขาวิทยาศาสตร์คอมพิวเตอร์ในขณะที่มีหลายวิธีที่จะคำนวณ / ใกล้เคียงกับมัน มันได้รับการแก้ไขสำหรับกลุ่มคะแนนที่มีขนาดใหญ่มาก แต่ที่ใหญ่ที่สุดบางแห่งใช้เวลา CPU นานหลายปีจึงจะเสร็จ

อย่าถูกไฟไหม้โดยมันฝรั่ง

Hot Potatoเป็นเกมที่ผู้เล่น 2+ คนผ่าน "มันฝรั่ง" เป็นวงกลมขณะที่เล่นดนตรี เป้าหมายคือการส่งต่อไปยังผู้เล่นถัดไปอย่างรวดเร็ว หากคุณกำลังถือมันฝรั่งเมื่อเพลงหยุดลงแสดงว่าคุณไม่อยู่


เป้าหมายของพนักงานขายมันฝรั่งร้อนคือ:

ได้รับชุดของ100 จุดที่ไม่ซ้ำกันกลับจุดเหล่านั้นอยู่ในลำดับที่ดีกว่า ( รวมระยะทางสั้นลงตามที่กำหนดไว้ต่อไป ) นี่จะเป็นการ "ผ่าน" ปัญหาไปยังผู้เล่นคนถัดไป พวกเขาต้องปรับปรุงมันและส่งต่อไปยังหน้าถัดไปเป็นต้นหากผู้เล่นไม่สามารถปรับปรุงได้พวกเขาจะออกและเล่นต่อไปจนกว่าผู้เล่นคนหนึ่งจะออกไป

เพื่อป้องกันไม่ให้สิ่งนี้เป็นการแข่งขันที่ "กำลังดุร้ายกับฉัน - เส้นทาง" มีข้อกำหนดเหล่านี้:

  • คุณไม่สามารถใช้เวลามากกว่าหนึ่งนาทีในการส่งมันฝรั่ง หากคุณยังไม่พบและผ่านวิธีแก้ปัญหาที่สั้นกว่าภายในหนึ่งนาที

  • คุณไม่สามารถเปลี่ยนตำแหน่งได้มากกว่า25คะแนน เพื่อให้เป็นจริง>= 75คะแนนจะต้องอยู่ในตำแหน่งเดียวกับที่คุณได้รับ มันไม่สำคัญซึ่งคนที่คุณตัดสินใจที่จะเปลี่ยนแปลงเพียงจำนวนเงินที่คุณเปลี่ยนแปลง

เมื่อเหลือผู้เล่นเพียงคนเดียวเขาเป็นผู้ชนะของเกมนั้นและได้รับหนึ่งแต้ม Tourney ประกอบด้วย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ชั้นหลักคือ การทำเช่นนี้จะเป็นการแข่งขันแบบเต็มรูปแบบโดยผู้เข้าแข่งขันจะได้รับการโต้แย้ง มันคายออกผู้ชนะของแต่ละเกมและนับในตอนท้าย ตัวอย่าง tourney ที่มี SwapBots สองตัวจะมีลักษณะดังนี้:

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คลาสได้ สิ่งนี้จะเรียกใช้หนึ่งเกมกับผู้เล่นตามลำดับที่ให้ไว้เป็นอาร์กิวเมนต์ โดยค่าเริ่มต้นมันจะพิมพ์การเล่นแบบเล่นต่อเนื่องโดยแสดงเครื่องเล่นปัจจุบันและความยาวเส้นทาง

รวมทั้งยังเป็นผู้เล่นไม่กี่ทดสอบ: SwapBot, และBlockPermuter TwoSwapBotสองคนแรกจะไม่รวมอยู่ในการให้คะแนนดังนั้นอย่าลังเลที่จะใช้และใช้ในทางที่ผิดในระหว่างการทดสอบ TwoSwapBot จะรวมอยู่ในการตัดสินและเขาก็ไม่ง่วงเลยดังนั้นให้นำเกม A ของคุณไปด้วย

เรื่องจิปาถะ

  • คุณไม่สามารถบันทึกข้อมูลสถานะได้และในแต่ละเทิร์นเป็นการเรียกใช้โปรแกรมของคุณแยกกัน ข้อมูลเดียวที่คุณจะได้รับในแต่ละเทิร์นคือชุดของคะแนน

  • คุณไม่สามารถใช้ทรัพยากรภายนอก ซึ่งรวมถึงการโทรผ่านเครือข่ายและการเข้าถึงไฟล์

  • คุณไม่สามารถใช้ฟังก์ชั่นห้องสมุดที่ออกแบบมาเพื่อแก้ไข / ช่วยเหลือเกี่ยวกับปัญหา TSP หรือตัวแปรต่างๆ

  • คุณไม่สามารถจัดการหรือแทรกแซงผู้เล่นคนอื่น ๆ ในทางใดทางหนึ่ง

  • คุณไม่สามารถจัดการหรือแทรกแซงโปรแกรมควบคุมหรือคลาสหรือไฟล์ใด ๆ ที่รวมอยู่ด้วยไม่ว่าในทางใด

  • อนุญาตให้ใช้มัลติเธรด

  • หนึ่งการส่งต่อผู้ใช้ หากคุณส่งมากกว่าหนึ่งรายการฉันจะป้อนเพียงรายการแรกที่ส่ง หากคุณต้องการเปลี่ยนการส่งของคุณแก้ไข / ลบต้นฉบับ

  • ทัวร์นาเมนต์จะทำงานบน Ubuntu 13.04 บนคอมพิวเตอร์ที่มีซีพียู i7-3770Kและ RAM ขนาด 16GB มันจะไม่ทำงานใน VM สิ่งใดก็ตามที่ฉันเห็นว่าเป็นอันตรายจะตัดสิทธิ์รายการปัจจุบันและในอนาคตที่คุณส่งทันที

  • รายการทั้งหมดจะต้องเรียกใช้จากบรรทัดคำสั่งด้วยซอฟต์แวร์ฟรี ( เช่นเดียวกับในเบียร์ ) หากฉันมีปัญหาในการรวบรวม / เรียกใช้รายการของคุณฉันจะขอความช่วยเหลือในความคิดเห็น หากคุณไม่ตอบสนองหรือฉันไม่สามารถทำงานได้ในที่สุดจะถูกตัดสิทธิ์

ผลลัพธ์ (22 พฤษภาคม 2014)

ผลลัพธ์ใหม่มาถึงแล้ว! UntangleBot เอาชนะการแข่งขันได้ค่อนข้างดี TwoSwapBot จัดการกับเจ็ดผู้ชนะและ SANNbot เป็นผู้ชนะด้วยเช่นกัน นี่คือกระดานคะแนนและลิงก์ไปยังผลลัพธ์ดิบ :

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

ขณะที่มันยืนตอนนี้ , UntangleBotได้รับรางวัลเครื่องหมายถูก อย่าปล่อยให้สิ่งนี้กีดกันคุณไม่ให้เข้าร่วมเพราะฉันจะจัดการแข่งขันเนื่องจากมีผู้เข้าร่วมแข่งขันปรากฏขึ้นและเปลี่ยนคำตอบที่ยอมรับตามนั้น


ลบความคิดเห็นแล้ว กรุณาแจ้งฉันสำหรับข้อมูลที่หายไปใด ๆ ที่เป็นไปได้
Doorknob

ผู้ชายทำไมความท้าทายนี้ถึงต้องอยู่ในช่วงการสอบครั้งสุดท้ายของฉัน คุณแน่ใจว่าเป็นนักวางแผนที่ไม่ดี Geobits;) แปลก ๆ / เศร้าพอในตอนนั้นมีคำถามแบบคิงส์ออฟเดอะฮิลล์มากมายและตอนนี้ก็ไม่มีเลย คำใบ้) ...
Herjan

@ Herjan รู้สึกอิสระที่จะลองเล่นกับแชมป์ปัจจุบัน ฉันจะเรียกใช้ tourney อีกครั้งเมื่อมีผู้เข้าแข่งขันใหม่ปรากฏขึ้นดังนั้นการแข่งขันจึงไม่จบสิ้น เอาชนะ SirDarius และมันอาจกระตุ้นเขาหรือคนอื่นให้เอาชนะคุณหายใจชีวิตกลับคืนมา)
Geobits

@Herjan กรุณาส่งรายการ! ฉันเชื่อว่ามีห้องมากมายสำหรับการปรับปรุงที่นี่ วิธีแก้ปัญหาส่วนใหญ่ที่นี่รวมถึงของฉันไม่พึ่งพาอัลกอริทึมที่ชาญฉลาดเฉพาะสำหรับปัญหานี้
SirDarius

ฉันคิดว่ามีปัญหากับข้อ จำกัด การแก้ไข บางครั้งมันจะใช้เวลาในการปรับเปลี่ยนทั้งชุดของข้อมูลเพื่อให้ได้ทางออกที่ดีขึ้น
Ilya Gazman

คำตอบ:


8

UntangleBot (เดิมคือ NiceBot)

บอท C ++ 11 ที่ใช้สองกลยุทธ์
ในตอนแรกมันจะพยายาม "แก้ให้หายยุ่ง" เส้นทางถ้าเป็นไปได้โดยการตรวจหาจุดตัดระหว่างเส้นทางที่ใกล้กว่า 25 คะแนน
หากกลยุทธ์แรกล้มเหลวมันสุ่มสลับจุดเพื่อหาระยะทางที่ดีขึ้นจนกว่าจะพบเส้นทางที่ดีกว่า

บ็อตนี้จะเต้น 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 นาที!
Rainbolt

สิ่งนี้ควรมี upvotes มากกว่าโดยพิจารณาจากผลลัพธ์ของวันนี้
Geobits

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

แนวคิดนี้ค่อนข้างง่าย: แต่ละเทิร์นคือหนึ่ง "ขั้นตอนการระบายความร้อน" ของการจำลองแบบการอบด้วยจำนวนผู้เล่นที่ยังคงอยู่ในเกมว่า "อุณหภูมิ" (แก้ไขโดยระยะทางปัจจุบันมากกว่า 12000 คือระยะทางเริ่มต้นโดยประมาณ) ความแตกต่างเพียงอย่างเดียวกับการจำลองการอบที่เหมาะสมคือฉันตรวจสอบว่าฉันไม่ได้เปลี่ยนแปลงองค์ประกอบมากกว่า 25 รายการและถ้าฉันใช้การเคลื่อนไหว 20 ครั้งขึ้นไป แต่ลำดับผลลัพธ์นั้นมีค่ามากกว่าตอนเริ่มต้นแล้วจะเริ่มใหม่อีกครั้ง


@Geobits อาขอโทษลบบรรทัดที่ init_s ถูกกำหนดโดยอุบัติเหตุ (ดี "อุบัติเหตุ": ฉันเห็นบรรทัดและคิดว่า "ทำไมมันถึงอยู่ที่นี่อีกครั้ง?" และลบมัน :)) การแก้ไข
plannapus

ฉันลองใช้โปรแกรมของคุณjava Tourney "java Swapbot" "Rscript SANNbot.R"และดูเหมือนว่าจะใช้งานได้
plannapus

ใช่ดูเหมือนว่าจะทำงานตอนนี้
Geobits

ในทางทฤษฎี (ถ้าฉันไม่เข้าใจผิดทั้งหมด) มันควรจะทำงานได้ดีขึ้นเมื่อผู้เล่นเข้ามาในเกม
plannapus

ตามที่เป็นอยู่โปรแกรมนี้ออกไปก่อนเสมอเนื่องจาก "มีการเปลี่ยนแปลงคะแนนมากเกินไป" ในการทดสอบของฉัน หากฉันชนuขีด จำกัด การตรวจสอบสิ่งนี้จะไม่เกิดขึ้น (และจะทำงานได้ดีขึ้นมาก) ในขณะที่รหัสของคุณดูเหมือนตรงไปตรงมาฉันไม่ทราบนิสัยของ R ดังนั้นฉันไม่สามารถบอกได้ว่าตรรกะผิด (รุ่นล่าสุดของตัวควบคุมที่จะทำให้ข้อความเกี่ยวกับสาเหตุที่ ธ ปทออกไปเมื่อทำงานGameเพื่อที่จะช่วยระบุปัญหา)
Geobits

4

BozoBot

ใช้ตรรกะที่ซับซ้อนที่อยู่เบื้องหลังBozosortเพื่อค้นหาเส้นทางที่ดีกว่า ดูเหมือนว่านี้

  • สลับคะแนนสุ่ม
  • ถ้าเราปรับปรุง
    • ส่งคืนคำตอบ
  • มิฉะนั้น
    • ลองอีกครั้ง

BozoBot ได้รับการปรับปรุงด้วย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);
    }
}

อืมมม ... คุณไม่ควรใส่สมุนของคุณไว้ในกลุ่มเธรดแทนที่จะเรียกเรียกใช้ ()? AFAIK นี่ไม่ได้เป็นแบบมัลติเธรด ...
CommonGuy

@ มนูคุณจับฉัน! ฉันพยายามเรียนรู้ด้วยตัวเองและล้มเหลว ตัวชี้ใด ๆ
Rainbolt

ฉันคิดว่ามันควรจะเป็นnew Thread(new Minion()).start()
CommonGuy

1
@Manu ขอบคุณ เห็นได้ชัดว่าฉันอ่านเพียงครึ่งหนึ่งของบทช่วยสอนนี้เมื่อฉันเขียนโปรแกรม
Rainbolt

3

TwoSwapBot

การอัพเกรดเป็นSwapBotผู้ชายคนนี้ตรวจสอบทุกคู่ของการแลกเปลี่ยน ก่อนอื่นเขาจะตรวจสอบว่าการสลับใด ๆ จะทำให้เส้นทางสั้นลงหรือไม่ หากเป็นเช่นนั้นจะส่งคืนทันที ถ้าไม่เขาตรวจสอบแต่ละคนเพื่อดูว่าการแลกเปลี่ยนอื่นจะสั้นลงหรือไม่ ถ้าไม่เขาแค่ตาย

ในขณะที่เส้นทางยังคงเป็นกึ่งสุ่มโดยปกติแล้วจะส่งกลับในประมาณ 100ms หากเขาต้องตรวจสอบทุก ๆ 2-swap (ประมาณ 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 10ชิ้น25 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

เปลี่ยนprintlnเป็นprintและทำให้การนับเธรดเป็นแบบไดนามิก ตอนนี้มันเริ่มต้นด้วย 10 กระทู้ ...
CommonGuy

1

Divide And Conquer + Greedy Bot

หมายเหตุ:ฉันดูรหัสของคุณ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 นาที (ฉันให้เวลา 3 วินาทีสำหรับการเริ่มต้น / ปิดโปรแกรมและการเริ่มต้น JVM - คุณไม่มีทางรู้เลยว่ารูทีนการเริ่มต้นของ 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 ผลัก ตอนนี้มันจะฆ่าผู้เล่นแทนที่จะฆ่าโปรแกรม
Geobits

สำหรับเร็กคอร์ดฉันไม่คิดว่ามันจะผิดพลาดในบรรทัด" เฮ้ฉันสงสัยว่า ... " ต่อไป ตรวจสอบว่ามี<200โทเค็นก่อนที่จะพยายามวิเคราะห์คำหรือไม่ ยังดีกว่าที่จะตรวจสอบมันอยู่ดี
Geobits

@Geobits haha ​​ไม่ทราบว่า
DankMemes

หมายเหตุ: เพื่อให้การคอมไพล์ฉันต้องเพิ่ม)on line 19; เปลี่ยนsubstrเป็นsubstring38; เริ่มต้นidxกับบางสิ่งบางอย่างในrun()วิธีการ
Geobits
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.