Продавець гарячої картоплі


23

Давши список балів, знайдіть найкоротший шлях, який відвідує всі точки і повертається до початкової точки.

завдання комівояжера добре відома в області науки комп'ютера, як і багато інші способи розрахунку / наближають його. Це вирішено для дуже великих груп балів, але для деяких найбільших потрібен багато процесорних років.

Картопля не спалиться.

Гаряча картопля - це гра, в якій 2+ гравців передають «картоплю» по колу, коли грає музика. Завдання полягає в тому, щоб швидко передати його наступному гравцеві. Якщо ви тримаєте картоплю, коли музика зупиняється, ви виходите.


Об'єктом продавця гарячої картоплі є:

Давши набір 100 унікальних точок , поверніть ці точки в кращому порядку ( коротша загальна відстань, як визначено далі вниз ). Це "передасть" проблему наступному гравцеві. Вони повинні вдосконалити його і передати його наступному і т. Д. Якщо гравець не може його покращити, вони виходять і грають, поки не залишиться одного гравця.

Щоб це не було змаганням "груба сила-мені-на-шляху", є такі умови:

  • Щоб пропустити картоплю, вам не знадобиться більше хвилини . Якщо ви не знайшли і не прийняли коротше рішення до моменту закінчення однієї хвилини, ви виходите.

  • Ви не можете змінити позицію більше 25 балів. Якщо бути точним, >= 75бали повинні знаходитися в тому ж самому положенні, як ви їх отримали. Не має значення, які з них ви вирішите змінити, лише кількість, яку ви зміните.

Коли залишився лише один гравець, він стає переможцем цієї гри і отримує один бал. Турнір складається з 5*nігор, де nкількість гравців. Кожна гра, починаючий гравець буде обертатися , а решта гравців, що залишилися, буде переміщена . Гравець з найбільшою кількістю очок в кінці - переможець турніру. Якщо турнір закінчиться нічиєю за перше місце, новий турнір буде зіграний лише з тими учасниками. Це триватиме до тих пір, поки не буде зв’язку.

Початковий гравець для кожної гри отримуватиме набір псевдовипадково вибраних очок без особливого порядку.

Точки визначаються як пара цілих x,yкоординат на декартовій сітці. Відстань вимірюється з допомогою Manhattan відстань , |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 виглядає так:

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 буде включений у суддівство, і він не сутулиться, тому принесіть свою А-гру.

Різне

  • Ви не можете зберегти інформацію про стан, і кожен виток - це окремий запуск вашої програми. Єдина інформація, яку ви будете отримувати кожного кроку, - це набір балів.

  • Ви не можете використовувати зовнішні ресурси. Сюди входять мережеві дзвінки та доступ до файлів.

  • Ви не можете використовувати функції бібліотеки, розроблені для вирішення / допомоги проблеми TSP або її варіантів.

  • Ви не можете жодним чином маніпулювати або перешкоджати іншим гравцям.

  • Ви не можете жодним чином керувати програмою управління або будь-якими включеними класами чи файлами або втручатися в них.

  • Дозволено багаторізне нанизування.

  • Одне подання на користувача. Якщо ви подасте більше одного запису, я введу лише перший поданий. Якщо ви хочете змінити подання, відредагуйте / видаліть оригінал.

  • Турнір буде проходити на Ubuntu 13.04, на комп’ютері з процесором i7-3770K та 16 Гб оперативної пам’яті. Він не буде запускатися в 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 переміг у галочці. Не дозволяйте це відштовхувати вас від участі, оскільки я буду проводити турнір у міру того, як з'являться більше учасників і відповідно змінять прийняту відповідь.


Коментарі очищені. Будь ласка, повідомте мене про будь-яку можливу втрачену інформацію.
Дверна ручка

Людина, чому цей виклик повинен був бути під час моїх випускних іспитів (нарешті, зроблено зі школою). Ви впевнені, що це поганий планувальник Геобітс;) Дивно / сумно, в той час було багато питань королівства, а зараз таких немає (можливо, це краще, якщо є лише один за одним, підказка натяк) ...
Гержан

@Herjan Не соромтеся намагатися вирішити поточний чемпіон. Я знову проведу турнір, коли з’являться нові учасники, тому конкурс ще не закінчився . Побийте Сірдарія, і це може підштовхнути його або когось іншого, щоб побити ваше, вдихнувши життя в нього;)
Геобіт

@Herjan Будь ласка, подайте заявку! Я вважаю, що тут є багато можливостей для вдосконалення. Більшість рішень тут, включаючи моє, не покладаються на розумні алгоритми, характерні для цієї проблеми.
SirDarius

Я думаю, що існує проблема з обмеженням модифікації. Як і колись, це потребує модифікації всього набору даних, щоб отримати краще рішення.
Ілля Газман

Відповіді:


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

Згідно з сьогоднішніми результатами, це повинно мати більше результатів.
Геобіць

@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 був визначений випадково (ну "випадковість": я побачив рядок і подумав "чому він знову тут?" І видалив його :)). Виправлено.
планнапус

Я спробував використовувати вашу програму, java Tourney "java Swapbot" "Rscript SANNbot.R"і, здавалося, це працює.
планнапус

Так, це, здається, працює зараз.
Геобіт

Теоретично (якщо я не зовсім помиляюся) він повинен працювати краще, коли в гру вступає більше гравців.
планнапус

Оскільки ця програма завжди виходить на ранніх стадіях через "занадто багато балів, що змінилися" в моєму тестуванні. Якщо я підмінюю uконтрольні межі вгору, цього не відбувається (і це набагато краще). Хоча ваш код здається досить простим, я не знаю химерності R, тому не можу сказати, чи не логіка неправильна. (остання версія контролера надсилатиме повідомлення про те, чому бот вимикається під час запуску Game, щоб це могло допомогти визначити проблему)
Geobits

4

BozoBot

Використовує складну логіку, що стоїть за Бозосортом, щоб знайти кращий шлях. Це виглядає приблизно так.

  • Поміняйте місцями випадкові точки
  • Якби ми покращилися
    • Поверніть відповідь
  • Інакше
    • Спробуйте ще раз

Тепер BozoBot покращено за допомогою багатопотокової роботи ! Зараз чотири міньйони безцільно жонглюють очками, поки не натрапляють на покращення. Першим, хто знайшов рішення, отримує печиво!

Мабуть, я не вдається в багатопоточності.

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

@Manu Ти мене зловив! Я спробував це навчитися самостійно і не вдалося. Якісь покажчики?
Rainbolt

Я думаю, що має бутиnew Thread(new Minion()).start()
CommonGuy

1
@Manu Дякую Мабуть, я прочитав лише половину цього підручника, коли кодував.
Rainbolt

3

TwoSwapBot

Оновлення до SwapBotцього хлопця перевіряє на кожну пару свопів. По-перше, він перевіряє, чи якась одна заміна скоротить шлях. Якщо це зробити, він повертається негайно. Якщо ні, він перевіряє кожного, щоб побачити, чи скоротить його ще одна свопа. Якщо ні, то він просто гине.

Хоча шлях все ще напів випадковий, він зазвичай повертається приблизно за 100 мс. Якщо йому доведеться перевіряти кожний 2-замін (приблизно 25М), це займе близько 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. Після 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
Примітка. Я змінив, printlnщоб printпозбутися нового рядка в кінці вашого результату. Інакше він вибивається.
Геобіць

Змінений , printlnщоб printі зробив нитку підрахунку динаміки. Зараз він починається з 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 хвилину (я дав 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. Тепер він просто вб'є програвача, а не програму.
Геобіць

Для запису, я не думаю, що це все-таки зірветься на вашій лінії " Ей, цікаво, чи ... ". Він перевіряє, чи є <200лексеми, перш ніж спробувати розібратися. Ще краще все-таки перевірити.
Геобіць

@Geobits ха-ха не усвідомлював цього
DankMemes

Примітка. Щоб зробити це для компіляції, мені довелося додати )рядок 19; змінити , substrщоб substringна 38; ініціалізувати idxщось на run()методі.
Геобіт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.