Распараллеливание фитнес-функции в генетическом алгоритме - PullRequest
0 голосов
/ 14 января 2019

Я новичок в Java и, как домашнее задание, я должен реализовать параллелизм к генетическому алгоритму для решения задачи коммивояжера здесь . Наша цель - сделать оценку хромосомы по потокам. Так что я думаю, мне нужно переписать эту часть кода, чтобы быть многопоточным:

// Gets the best tour in the population
public Tour getFittest() {
    Tour fittest = tours[0];
    // Loop through individuals to find fittest
    for (int i = 1; i < populationSize(); i++) {
        if (fittest.getFitness() <= getTour(i).getFitness()) {
            fittest = getTour(i);
        }
    }
    return fittest;
}

// Gets population size
public int populationSize() {
    return tours.length;
}

Изначально я собирался вручную разбить потоки между массивами, но я верю, что это не лучшее решение проблемы. Поэтому я провел небольшое исследование, и все предлагают использовать либо параллельные потоки, либо ExecutorService. Однако у меня были проблемы с применением обоих этих решений, хотя я и пытался подражать примерам, размещенным в других темах. Итак, мои вопросы: как именно я реализую их в этом случае, и какой из них быстрее?

Редактировать: Извините, я забыл опубликовать решение, которое я пробовал. Вот оно:

public Tour getFittest() {
    Tour fittest = tours[0];
    synchronized (fittest) {
        final ExecutorService executor = Executors.newFixedThreadPool(4); 
        final List<Future<?>> futures = new ArrayList<>();
        for (int i = 1; i < populationSize(); i++) {
            Future<?> future = executor.submit((Runnable) () -> {
                if (fittest.getFitness() <= getTour(i).getFitness()) {
                    fittest = getTour(i);
                }
            });
            futures.add(future);
        }
        try {
            for (Future<?> future : futures) {
                future.get();
            }
        }catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    return fittest;
}
public int populationSize() {
    return tours.length;
}

Однако, при попытке запустить его, я получаю сообщение об ошибке «Локальная переменная, определенная в включенной области видимости, должна быть окончательной или фактически конечной» в строке:

fittest = getTour(i);

И я понятия не имею, почему это происходит или как я могу это исправить, добавив ключевое слово final, а при его инициализации это не исправляется. Кроме этого у меня есть некоторые сомнения по поводу использования синхронизированного ключевого слова в этом решении. Я считаю, что для достижения истинной многопоточности мне нужно использовать его из-за того, что ресурс используется несколькими потоками. Я прав? К сожалению, я не сохранил мою попытку использовать потоки, но мне сложно понять, как это работает вообще.

Edit2: мне удалось «исправить» мое решение, добавив два обходных пути. В настоящее время мой код выглядит так:

public Tour getFittest() {
    Tour fittest = tours[0];
    synchronized (fittest) {
        final ExecutorService executor = Executors.newFixedThreadPool(4); 
        final List<Future<?>> futures = new ArrayList<>();
        for (int i = 1; i < populationSize(); i++) {
            final Integer innerI = new Integer(i);
            Future<?> future = executor.submit((Runnable) () -> {
                if (fittest.getFitness() <= getTour(innerI).getFitness()) {
                    setFitness(innerI, fittest);
                    }
                }
            );
            futures.add(future);
        }
        try {
            for (Future<?> future : futures) {
                future.get();
            }
        }catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        }
    return fittest;
}

public int populationSize() {
    return tours.length;
}

public Tour setFitness (int i, Tour fittest) {
        fittest = getTour(i);
        return fittest;
}

Тем не менее, пока он компилируется, есть две проблемы. Использование памяти растет с каждой секундой, в течение которой запускается программа, увеличивая до 16 ГБ ОЗУ за десять секунд, в то время как переменная fittest не изменяется вообще. Так что, думаю, я все еще делаю что-то не так.

1 Ответ

0 голосов
/ 14 января 2019

Вот моя реализация пар:

private static Tour getFittest(Tour[] tours){
    List<Map.Entry<Tour,Double>> lengths = new ArrayList<>();
    Arrays.stream(tours).parallel().forEach(t->lengths.add(new AbstractMap.SimpleEntry<Tour,Double>(t,t.getLength())));
    return Collections.min(lengths,Comparator.comparingDouble(Map.Entry::getValue)).getKey();
}

При дальнейшем поиске может быть 1 лайнер вроде в зависимости от вашего определения

    private static Tour getFittest(Tour[] tours) {

    return Arrays.stream(tours).parallel().map(t -> new AbstractMap.SimpleEntry<Tour, Double>(t, t.getLength()))
            .min(Comparator.comparingDouble(Map.Entry::getValue)).get().getKey();
}

также после дальнейшего просмотра они используют .getFitness (), который является обратной величиной длины. если вы используете это, то используйте .max () в качестве фильтра.

на самом деле даже лучше после обзора

 return Arrays.stream(tours).parallel()
            .min(Comparator.comparingDouble(Tour::getLength)).get();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...