Simulerad glödgning för nybörjare

11 April 2013 * av Lee Jacobson

att hitta en optimal lösning för vissa optimeringsproblem kan vara en oerhört svår uppgift, ofta praktiskt taget omöjlig. Detta beror på att när ett problem blir tillräckligt stort måste vi söka igenom ett enormt antal möjliga lösningar för att hitta den optimala. Även med modern datorkraft finns det fortfarande ofta för många möjliga lösningar att överväga. I det här fallet eftersom vi inte realistiskt kan förvänta oss att hitta den optimala inom en förnuftig tid, måste vi nöja oss med något som är tillräckligt nära.
ett exempel på optimeringsproblem som vanligtvis har ett stort antal möjliga lösningar skulle vara problemet med resande säljare. För att hitta en lösning på ett problem som traveling salesman-problemet måste vi använda en algoritm som kan hitta en tillräckligt bra lösning på en rimlig tid. I en tidigare handledning tittade vi på hur vi kunde göra detta med genetiska algoritmer, och även om genetiska algoritmer är ett sätt vi kan hitta en ’tillräckligt bra’ lösning på resande säljare problem, det finns andra enklare algoritmer vi kan genomföra som också hittar oss en nära optimal lösning. I denna handledning algoritmen vi kommer att använda är, ’simulerad glödgning’.
om du inte är bekant med resande säljare problemet kan det vara värt att ta en titt på min tidigare handledning innan du fortsätter.

Vad är simulerad glödgning?

Låt oss först titta på hur simulerad glödgning fungerar, och varför det är bra att hitta lösningar på resande säljare problem i synnerhet. Den simulerade glödgningsalgoritmen inspirerades ursprungligen från glödgningsprocessen i metallarbete. Glödgning innebär uppvärmning och kylning av ett material för att ändra dess fysikaliska egenskaper på grund av förändringarna i dess inre struktur. När metallen svalnar blir dess nya struktur fixerad, vilket medför att metallen behåller sina nyligen erhållna egenskaper. Vid simulerad glödgning håller vi en temperaturvariabel för att simulera denna uppvärmningsprocess. Vi sätter det först högt och låter det långsamt ’svalna’ när algoritmen körs. Medan denna temperaturvariabel är hög kommer algoritmen att tillåtas, med mer frekvens, att acceptera lösningar som är sämre än vår nuvarande lösning. Detta ger algoritmen möjlighet att hoppa ut ur alla lokala optimum som den befinner sig i tidigt i körning. När temperaturen sänks så är chansen att acceptera sämre lösningar, vilket gör att algoritmen gradvis kan fokusera på ett område i sökutrymmet där förhoppningsvis en nära optimal lösning kan hittas. Denna gradvisa kylningsprocess är det som gör den simulerade glödgningsalgoritmen anmärkningsvärt effektiv för att hitta en nära optimal lösning när man hanterar stora problem som innehåller många lokala optimum. Den typ av resande säljare problemet gör det till ett perfekt exempel.

fördelar med simulerad glödgning

du kanske undrar om det finns någon verklig fördel med att implementera simulerad glödgning över något som en enkel bergsklättrare. Även om bergsklättrare kan vara förvånansvärt effektiva för att hitta en bra lösning, har de också en tendens att fastna i lokala optimum. Som vi tidigare bestämt är den simulerade glödgningsalgoritmen utmärkt för att undvika detta problem och är i genomsnitt mycket bättre på att hitta ett ungefärligt globalt optimalt.
för att bättre förstå låt oss snabbt ta en titt på varför en grundläggande hill climbing algoritm är så benägna att fastna i lokala optimum.
en hill climber-algoritm accepterar helt enkelt grannlösningar som är bättre än den nuvarande lösningen. När bergsklättraren inte hittar några bättre grannar stannar den.

i exemplet ovan börjar vi vår bergsklättrare vid den röda pilen och den arbetar sig uppför backen tills den når en punkt där den inte kan klättra högre utan att först gå ner. I det här exemplet kan vi tydligt se att det sitter fast i ett lokalt optimum. Om detta var ett verkligt världsproblem skulle vi inte veta hur sökutrymmet ser ut så tyvärr skulle vi inte kunna berätta om den här lösningen är någonstans nära ett globalt optimalt.
simulerad glödgning fungerar något annorlunda än detta och kommer ibland att acceptera sämre lösningar. Denna egenskap hos simulerad glödgning hjälper den att hoppa ut ur alla lokala optimum som den annars skulle ha fastnat i.

Acceptansfunktion

Låt oss ta en titt på hur algoritmen bestämmer vilka lösningar som ska accepteras så att vi bättre kan förstå hur den kan undvika dessa lokala optimum.
först kontrollerar vi om grannlösningen är bättre än vår nuvarande lösning. Om det är så accepterar vi det villkorslöst. Men om grannlösningen inte är bättre måste vi överväga ett par faktorer. För det första, hur mycket sämre grannlösningen är, och för det andra, hur hög den nuvarande ’temperaturen’ i vårt system är. Vid höga temperaturer systemet är mer sannolikt Acceptera lösningar som är sämre.
matematiken för detta är ganska enkel:

exp( (solutionEnergy – neighbourEnergy) / temperatur )

i grund och botten är ju mindre energiförändringen (lösningens kvalitet) och ju högre temperaturen desto mer sannolikt är det för algoritmen att acceptera lösningen.

algoritm översikt

så hur ser algoritmen ut? Tja, i sin mest grundläggande implementering är det ganska enkelt.

  • först måste vi ställa in den ursprungliga temperaturen och skapa en slumpmässig initial lösning.
  • sedan börjar vi looping tills vårt stoppvillkor är uppfyllt. Vanligtvis har systemet tillräckligt kylt, eller en tillräckligt bra lösning har hittats.
  • härifrån väljer vi en granne genom att göra en liten förändring av vår nuvarande lösning.
  • vi bestämmer sedan om vi ska flytta till den grannlösningen.
  • slutligen sänker vi temperaturen och fortsätter looping

Temperaturinitiering

för bättre optimering bör vi vid initiering av temperaturvariabeln välja en temperatur som initialt möjliggör praktiskt taget alla rörelser mot den aktuella lösningen. Detta ger algoritmen möjlighet att bättre utforska hela sökutrymmet innan kylning och bosättning i en mer fokuserad region.

exempelkod

låt oss nu använda det vi vet för att skapa en grundläggande simulerad glödgningsalgoritm och sedan tillämpa den på resande säljare problemet nedan. Vi kommer att använda Java i denna handledning, men logiken bör förhoppningsvis vara enkel nog att kopiera till valfritt språk.

först måste vi skapa en Stadsklass som kan användas för att modellera de olika destinationerna för vår resande säljare.
Stad.java

/*
* Stad.java
* modeller en stad
* /
paket sa;
Offentlig klass Stad {
int x;
int y;
/ / konstruerar en slumpmässigt placerad stad
Offentlig Stad () {
detta.x = (Int) (matematik.slumpmässig () * 200);
detta.y = (Int) (matematik.slumpmässigt()*200);
}
// konstruerar en stad på vald x, Y plats
Offentlig Stad (int x, int y){
detta.x = x;
detta.y = y;
}
// Gets stadens x koordinat
Offentlig int getX () {
returnera detta.x;
}
// Gets stadens y koordinat
public int getY () {
returnera detta.y;
}
// får Avståndet till given stad
public double distanceTo (City city){
int xDistance = Math.abs (getX () – stad.getX ());
int yDistance = matematik.abs (getY () – stad.getY ());
dubbelt avstånd = matematik.sqrt ((xDistance*xDistance) + (yDistance*yDistance) );
returavstånd;
}
@Override
Offentlig sträng toString () {
returnera getX ()+”, ” + getY();
}
}

nästa låt oss skapa en klass som kan hålla reda på städerna.
TourManager.java

/*
* TourManager.java
* har städerna i en rundtur
*/
paket sa;
importera java.util.ArrayList;
Offentlig klass TourManager {
/ / håller våra städer
privat statisk ArrayList destinationCities = ny ArrayList< Stad>();
// lägger till en destinationsstad
public static void addCity (City city) {
destinationCities.Lägg till (stad);
}
// få en stad
offentlig statisk stad getCity (int index){
retur (Stad)destinationstäder.get (index);
}
// få antalet destinationsstäder
public static int numberOfCities () {
returdestinationcities.storlek();
}
}

nu för att skapa den klass som kan modellera en resande försäljare tour.
tur.java

/*
* tur.java
* lagrar en kandidattur genom alla städer
*/
paket sa;
importera java.util.ArrayList;
importera java.util.Samlingar;
public class Tour{
/ / håller vår rundtur i städer
private ArrayList tour = new ArrayList< Stad>();
// Cache
privat int avstånd = 0;
/ / konstruerar en tom tour
Offentlig Tour(){
för (int i = 0; jag < TourManager.numberOfCities (); i++) {
tur.Lägg till (null);
}
}
// konstruerar en tur från en annan tur
public Tour (ArrayList tour){
detta.tur = (ArrayList) tur.klon();
}
// returer tour information
Offentlig ArrayList getTour () {
retur tour;
}
// skapar en slumpmässig individ
public void generateIndividual () {
// Loop genom alla våra destinationsstäder och lägga till dem i vår tour
för (int cityIndex = 0; cityIndex < TourManager.numberOfCities (); cityIndex++) {
setCity (cityIndex, TourManager.getCity (cityIndex));
}
// slumpmässigt ordna turen
Samlingar.shuffle (tur);
}
/ / får en stad från turen
Offentlig stad getCity (int tourPosition) {
retur (City)tour.få (tourPosition);
}
// ställer in en stad i en viss position inom en rundtur
public void setCity (int tourPosition, City city) {
tour.set (tourPosition, stad);
/ / om turerna ändrats måste vi återställa fitness och avstånd
avstånd = 0;
}
// får det totala avståndet för turen
public int getDistance () {
if (distance == 0) {
int tourDistance = 0;
/ / Loop genom vår tur städer
för (int cityIndex = 0; cityIndex < tourSize(); cityIndex++) {
// Get city vi reser från
City fromCity = getCity(cityIndex);
// City vi reser till
City destinationCity;
// kontrollera att vi inte är på vår tur sista stad, om vi är inställda vår
// tour slutdestination stad till vår start stad
om(cityIndex+1 < toursize()){
destinationcity = getcity(cityindex+1);
}
annars{
destinationCity = getCity(0);
}
// Få avståndet mellan de två städerna
tourDistance + = fromCity.distanceTo (destinationCity);
}
distance = tourDistance;
}
returavstånd;
}
// få antal städer på vår tur
public int tourSize () {
return tour.storlek();
}
@åsido
Offentlig sträng toString () {
sträng geneString =|/”;
för (int i = 0; i < tourSize (); i++) {
geneString + = getCity(i)+”|”;
}
återgå geneString;
}
}

slutligen, låt oss skapa vår simulerade glödgningsalgoritm.
Simuleradannealing.java

paketet sa;
public class SimulatedAnnealing {
/ / beräkna acceptanssannolikheten
public static double acceptanceProbability (int energi, int newEnergy, dubbel temperatur) {
/ / om den nya lösningen är bättre, acceptera den
if (newEnergy < energi) {
retur 1.0;
}
// om den nya lösningen är sämre, beräkna en acceptanssannolikhet
returmatematik.exp ((energi-newEnergy)/ temperatur);
}
public static void main(String args) {
/ / skapa och Lägg till våra städer
City city = new City(60, 200);
TourManager.addCity (stad);
Stad city2 = ny stad (180, 200);
TourManager.addCity (city2);
Stad city3 = ny stad(80, 180);
TourManager.addCity (city3);
Stad city4 = ny stad(140, 180);
TourManager.addCity (city4);
Stad city5 = ny stad(20, 160);
TourManager.addCity (city5);
Stad city6 = ny stad(100, 160);
TourManager.addCity (city6);
Stad city7 = ny stad(200, 160);
TourManager.addCity (city7);
Stad city8 = ny stad(140, 140);
TourManager.addCity (city8);
Stad city9 = ny stad(40, 120);
TourManager.addCity (stad9);
Stad city10 = ny stad(100, 120);
TourManager.addCity (city10);
Stad city11 = ny stad(180, 100);
TourManager.addCity (city11);
Stad city12 = ny stad(60, 80);
TourManager.addCity (city12);
Stad city13 = ny stad(120, 80);
TourManager.addCity (city13);
Stad city14 = ny stad(180, 60);
TourManager.addCity (city14);
Stad city15 = ny stad(20, 40);
TourManager.addCity (city15);
Stad city16 = ny stad(100, 40);
TourManager.addCity (city16);
Stad city17 = ny stad(200, 40);
TourManager.addCity (city17);
Stad city18 = ny stad (20, 20);
TourManager.addCity (city18);
Stad city19 = ny stad(60, 20);
TourManager.addCity (city19);
Stad city20 = ny stad(160, 20);
TourManager.addCity (city20);
// Ställ in initial temp
dubbel temp = 10000;
// kylhastighet
dubbel kylhastighet = 0,003;
// initiera intial lösning
Tour currentSolution = new Tour ();
currentSolution.generateIndividual();
systemet.ut.println (”Initial lösning avstånd:” + currentSolution.getDistance ());
// Ställ in som nuvarande bästa
Tour best = new Tour (currentolution.getTour ());
// Loop tills systemet har svalnat
medan (temp > 1) {
// Skapa ny granntur
Tour newSolution = ny tur (currentSolution.getTour ());
// få en slumpmässig positioner i tour
int tourPos1 = (int) (newSolution.tourSize () * matematik.slumpmässig ());
int tourPos2 = (int) (newSolution.tourSize () * matematik.random ());
// få städerna på utvalda positioner i Touren
Stad citySwap1 = newSolution.getCity (tourPos1);
Stad citySwap2 = newSolution.getCity (tourPos2);
// byt dem
newSolution.setCity (tourPos2, citySwap1);
newSolution.setCity (tourPos1, citySwap2);
// få energi av lösningar
int currentEnergy = currentSolution.getDistance ();
int neighbourEnergy = newSolution.getDistance();
// Bestäm om vi ska acceptera grannen
om (acceptanceProbability (currentEnergy, neighbourEnergy, temp) > Math.slumpmässig ()) {
currentolution = ny tur (newSolution.getTour());
}
// Håll koll på den bästa lösningen som hittades
if (currentSolution.getDistance () < bäst.getDistance ()) {
bäst = ny tur (currentolution.getTour());
}
// kylsystem
temp * = 1-kylhastighet;
}
systemet.ut.println (”slutlig lösning avstånd:” + bäst.getDistance ());
systemet.ut.println (”Tour:” + bäst);
}
}

utgång

Initial lösning avstånd: 1966
slutlig lösning avstånd: 911
Tour: |180, 200/200, 160/140, 140/180, 100/180, 60/200, 40/160, 20/120, 80/100, 40/60, 20/20, 20/20, 40/60, 80/100, 120/40, 120/20, 160/60, 200/80, 180/100, 160/140, 180|

slutsats

i det här exemplet kunde vi mer än halva avståndet för vår ursprungliga slumpmässigt genererade rutt. Detta visar förhoppningsvis hur praktiskt denna relativt enkla algoritm är när den tillämpas på vissa typer av optimeringsproblem.

författare

 Lee Jacobson Hej, Jag är Lee.
jag är en utvecklare från Storbritannien som älskar teknik och affärer. Här hittar du artiklar och handledning om saker som intresserar mig. Om du vill anställa mig eller veta mer om mig gå över till min om mig sida

You might also like

Lämna ett svar

Din e-postadress kommer inte publiceras.