Рубрики
Без рубрики

Генетический алгоритм – путешествие продавца с Юлией

Генетический алгоритм генетический алгоритм (GA) – это тип алгоритма, вдохновленного процессом … Помечено с машиной, Юлией, Питоном, начинающими.

Генетический алгоритм (GA) – это тип алгоритма, вдохновленного процессом естественного отбора для создания высококачественных решений для проблем, которые в противном случае были бы слишком сложно решить.

Путешествуя продавцом проблему или TSP для короткой, является печально известной проблемой, когда человек путешествует, где человек путешествует, чтобы путешествовать различные города с известным расстоянием и возвращение в город происхождения в кратчайшие сроки/путь.

Как приведено ниже, каждый круг – это городская и синяя линия – это маршрут, посещающий их.

Почему бы не грубые силы ??

Хорошо, давайте попробуем грубую силу, чтобы решить проблему. Для начала давайте возьму 5 городов, то есть, мы должны рассчитать 5! , то есть 120 маршрутов, чтобы убедиться, что мы получили кратчайший путь.

Теперь, если мы должны сделать это за 50 города,

  • 5!
  • 10!
  • 15!
  • 20!
  • 50!

Мы видим, насколько грубым принуждением наш путь просто не будет полезным здесь.

Давайте попробуем GA

Итак, мы увидим, как Генетический алгоритм Может помочь нам найти оптимальный кратчайший путь здесь.

Простым, генетический алгоритм работает следующим образом,

  • Выбор случайных начальных решений/хромосом (называется как начальная популяция или хромосомы)
  • кроссовер (генерируя новое решение или хромосому, по видам смешивания двух родительских хромосом/растворов)
  • Мутате (Делать незначительное изменение в решении)
  • Фитнес (Проверка, если решение полезно или полностью заполнено нашему требованию), если да, мы используем их для создания новых потомков (родители в следующем поколении), и цикл продолжается для многих поколений, которые мы хотим.

Так .. для наших TSP Проблема, мы сделаем следующее:

Генерировать города

function generate_cities(number_of_cities, map_limit)

    cities = []
    for city_counter in 1:number_of_cities
        push!(cities, 
            Dict(
                "id" => city_counter, 
                "x" => round(rand() * map_limit), 
                "y" => round(rand() * map_limit)
            )
        )
    end
    println("Cities Generated:", size(cities)[1])
    return cities
end
# calling generate_cities method to create cities
generate_cities(5,500)

Мы храним город как словарь.

У каждого города есть ID и (х, у) место нахождения

 ("id" => 1,"x" => 480.0,"y" => 157.0)
 ("id" => 2,"x" => 4.0,"y" => 465.0)
 ("id" => 3,"x" => 57.0,"y" => 25.0)
 ("id" => 4,"x" => 411.0,"y" => 322.0)
 ("id" => 5,"x" => 44.0,"y" => 460.0)

Представляя решение как хромосома.

Решение для нашей проблемы – это маршрут из одного города в другой город. Мы можем написать его как массив целых чисел, где каждый номер представляет собой идентификатор города.

[1,2,5,4,3,1]

Итак, для вышеупомянутого маршрута или хромосомы, мы начинаем с City1 -> City 2 и так далее … наконец вернуться в City1.

Функция кроссовера

Здесь мы генерируем новую хромосому, принимая значения из двух родительских хромосом. Для этой проблемы мы просто должны обеспечить, чтобы гены/город города не повторяются, при создании новой хромосомы.

Есть много способов сделать кроссовер, как, UniPoint Crossover, многоточечный кроссовер и т. Д. Мы сделаем UniPoint Crossover, просто выбрав случайную точку в хромосоме и выполняющую простой своп, чтобы создать новую хромосому.

function crossover(parent_one_chromosome, parent_two_chromosome, crossover_point)
    offspring_part_one = parent_one_chromosome[1:crossover_point]
    for gene in offspring_part_one
        if gene in parent_two_chromosome
            gene_loc = findfirst(el -> el == gene, parent_two_chromosome)
            splice!(parent_two_chromosome, gene_loc)
        end
    end
    return vcat(offspring_part_one, parent_two_chromosome)
end

Функция мутации

В простых терминах мутации можно определить как случайное изменение в хромосоме. Здесь мы просто поменяем два положения гена.

function mutate(offspring)
    random_mutation_point1 = rand(1:length(offspring))
    random_mutation_point2 = rand(1:length(offspring))
    offspring[random_mutation_point1], offspring[random_mutation_point2] = offspring[random_mutation_point2], offspring[random_mutation_point1]
    return offspring
end

Фитнес функция

Ответственность Фитнес-функции заключается в том, чтобы рассчитать оценку каждого хромосома, чтобы мы могли решить, хотите ли мы использовать эту хромосому в следующем поколении или для отказа.

Для нашей проблемы счет – это расстояние поездки для данного маршрута/хромосомы. Нижнее расстояние перемещения лучше.

Для расчета расстояния поездки мы просто начнем с 1-го города, перейдите к следующему А потом к следующему, так и вернусь к городу происхождения, добавив расстояние, которое мы путешествовали, мы получаем счет этого конкретного маршрута.

function calculate_distance_between_two_points(point1, point2)
    return sqrt((((point2[1] - point1[1]))^2) + (((point2[2] - point1[2]))^2))
end

function calculate_chromosome_travel_distance(chromosome)
    travel_distance = 0
    chromosome = vcat(1, chromosome, 1)
    for geneId in 1:length(chromosome) - 1
        point1 = (
            cities[chromosome[geneId]]["x"],
            cities[chromosome[geneId]]["y"]
            )
        point2 = (
            cities[chromosome[geneId + 1]]["x"],
            cities[chromosome[geneId + 1]]["y"]
            )
        travel_distance += calculate_distance_between_two_points(point1, point2)
    end
    println("travel distance:", chromosome, " : ", travel_distance)
    return travel_distance
end

Генерация IISIAL населения:

Мы определим функцию, которая может генерировать начальные группы населения.

# We shuffle the chromosome here
function shuffle_chromosome(chromosome)
    for i in 1:size(chromosome)[1]
        random_point = rand(1:5, 1)[1]
        chromosome[i], chromosome[random_point] = chromosome[random_point], chromosome[i]
    end

    println("Created chromosome", chromosome)
    return chromosome
end


function generate_initial_population(initial_population_size)
    chromosomes = []
    for population_counter in 1:initial_population_size
        chromosome = shuffle_chromosome(copy(initial_chromosome))
        push!(chromosomes, 
            Dict(
                "chromosome" => chromosome,
                "distance" => calculate_chromosome_travel_distance(chromosome)
            )
        )
    end
    return chromosomes
end
[3, 9, 4, 8, 10, 5, 1, 2, 7, 6]
[8, 1, 6, 9, 10, 5, 2, 4, 3, 7]
[2, 9, 10, 8, 5, 4, 6, 1, 3, 7]
[5, 6, 10, 7, 4, 3, 1, 2, 8, 9]
[2, 10, 9, 4, 6, 5, 1, 3, 8, 7]

основная часть

Здесь мы определили функцию, которая запускает GA для заданного количества поколений, и создает заданное количество потомков для каждого поколения, затем проверяет фитнес и цикл продолжается для каждого поколения.

function evolve(generation_count, offsprings_count, crossover_point)
    for generation in 1:generation_count

        for offspring_count in 1:offsprings_count
            println("generation: ", generation, " offspring: ", offspring_count)
            random_parent_one_id = rand(1:size(chromosomes)[1])
            random_parent_two_id = rand(1:size(chromosomes)[1])
            random_parent_one = copy(chromosomes[random_parent_one_id]["chromosome"])
            random_parent_two = copy(chromosomes[random_parent_two_id]["chromosome"])
            offspring = crossover(random_parent_one, random_parent_two, crossover_point)
            offspring = mutate(offspring)
            push!(chromosomes, 
                Dict(
                    "chromosome" => offspring,
                    "distance" => calculate_chromosome_travel_distance(offspring)
                    )
            )
        end
        sort!(chromosomes, by=x -> x["distance"], rev=false)
        splice!(chromosomes, 6:size(chromosomes)[1])
    end
end

Теперь давайте выполним GA

# creating 10 cities randomly
cities = generate_cities(10, 500)
initial_chromosome = [2:length(cities);]
# generating 10 initial chromosomes
chromosomes = generate_initial_population(10)

# we are running GA for
# 5 generations
# 5 offsprings per generation
# random crossover point as 2
evolve(5, 5, 2)
println("--------------------------------------------------------")
println("Optimal route:", vcat(1, chromosomes[1]["chromosome"], 1))
println("travel_distance:", chromosomes[1]["distance"])

Если мы выполним, мы получаем следующий вывод.

generation: 1 offspring: 1
travel distance:[1, 3, 9, 10, 7, 5, 4, 6, 8, 2, 1] : 2780.551726305925
generation: 1 offspring: 2
travel distance:[1, 5, 10, 6, 9, 3, 2, 4, 7, 8, 1] : 2236.035984494435
generation: 1 offspring: 3
travel distance:[1, 10, 7, 3, 9, 5, 4, 6, 8, 2, 1] : 2627.3533869849102
generation: 1 offspring: 4
travel distance:[1, 7, 6, 4, 10, 3, 5, 2, 8, 9, 1] : 3078.2871083508203
generation: 1 offspring: 5
travel distance:[1, 3, 9, 10, 7, 2, 4, 5, 8, 6, 1] : 3074.581278954089
generation: 2 offspring: 1
travel distance:[1, 9, 8, 5, 10, 4, 3, 2, 6, 7, 1] : 2882.8847896850843
generation: 2 offspring: 2
travel distance:[1, 5, 10, 6, 2, 3, 9, 4, 7, 8, 1] : 1864.9566066991729
generation: 2 offspring: 3
travel distance:[1, 9, 8, 5, 10, 2, 3, 4, 6, 7, 1] : 2889.200659672017
generation: 2 offspring: 4
travel distance:[1, 4, 10, 6, 9, 3, 2, 5, 7, 8, 1] : 2520.4527035595024
generation: 2 offspring: 5
travel distance:[1, 9, 10, 6, 3, 5, 2, 4, 7, 8, 1] : 2443.441192706744
generation: 3 offspring: 1
travel distance:[1, 2, 10, 6, 9, 8, 5, 7, 4, 3, 1] : 2177.1886229349225
generation: 3 offspring: 2
travel distance:[1, 2, 10, 5, 6, 9, 8, 7, 4, 3, 1] : 1745.6328718972204
generation: 3 offspring: 3
travel distance:[1, 5, 4, 2, 6, 9, 8, 7, 10, 3, 1] : 2491.210252809206
generation: 3 offspring: 4
travel distance:[1, 7, 5, 6, 9, 3, 2, 4, 10, 8, 1] : 2867.790574182069
generation: 3 offspring: 5
travel distance:[1, 10, 6, 5, 9, 3, 2, 7, 4, 8, 1] : 2158.331962819929
generation: 4 offspring: 1
travel distance:[1, 4, 10, 6, 2, 3, 9, 5, 7, 8, 1] : 2375.395804596975
generation: 4 offspring: 2
travel distance:[1, 4, 10, 2, 6, 9, 8, 5, 7, 3, 1] : 2696.2819714580255
generation: 4 offspring: 3
travel distance:[1, 5, 3, 6, 9, 10, 2, 7, 4, 8, 1] : 2585.1841206421177
generation: 4 offspring: 4
travel distance:[1, 4, 6, 2, 9, 8, 5, 7, 10, 3, 1] : 2927.589295286965
generation: 4 offspring: 5
travel distance:[1, 5, 3, 10, 6, 9, 7, 2, 4, 8, 1] : 2610.376635133937
generation: 5 offspring: 1
travel distance:[1, 3, 10, 5, 6, 9, 8, 7, 4, 2, 1] : 1919.461172591361
generation: 5 offspring: 2
travel distance:[1, 10, 6, 8, 9, 3, 2, 7, 4, 5, 1] : 2488.399757690674
generation: 5 offspring: 3
travel distance:[1, 6, 10, 3, 5, 9, 8, 7, 4, 2, 1] : 1962.725547491429
generation: 5 offspring: 4
travel distance:[1, 10, 6, 8, 9, 4, 2, 7, 3, 5, 1] : 2626.455096698985
generation: 5 offspring: 5
travel distance:[1, 2, 6, 5, 10, 3, 9, 4, 7, 8, 1] : 1631.463753062698
--------------------------------------------------------
Optimal route:[1, 2, 6, 5, 10, 3, 9, 4, 7, 8, 1]
travel_distance:1631.463753062698

Я создал TSP-GA Playground, используя Tymdercript, оформить заказ, если вы хотите воспроизвести: https://dillir07.github.io/genetic-algorithm-tsp/

Julia – исходный код доступен в Гадость

Спасибо за чтение:) … Комментарии приветствуются.

Оригинал: “https://dev.to/dillir07/genetic-algorithm-travelling-salesman-with-julia-477a”