2010-05-28 6 views
13

누군가가 저에게 세일즈맨 여행 문제에 대한 2-opt 알고리즘의 코드 샘플을 제공 할 수 있습니까? 지금은 가장 가까운 이웃을 사용하여 경로를 찾지 만이 방법은 완벽하지 않습니다. 일부 연구를 마친 후에는 해당 경로를 수용 가능한 수준으로 수정하는 2-opt 알고리즘을 발견했습니다. 몇 가지 샘플 앱을 발견했지만 소스 코드가 없습니다.여행 세일즈맨 문제, 2-opt 알고리즘 C# 구현

+0

TSP에 3/2 OPT 솔루션이 있습니다. 성능을 보면 더 나아질 것입니다. (코드가 없어도 알 수 있고, 비제이 2 장에서 읽을 수 있습니다.) vazirani) – anon

답변

25

그래서 나는 지루해졌고 그것을 썼다. 그것은 가 작동하는 것처럼 보이는이지만 매우 철저하게 테스트하지는 않았습니다. 삼각형 불평등, 모든 가장자리가 존재한다고 가정합니다. 그것은 제가 설명했던 대답처럼 크게 작용합니다. 각 반복을 인쇄합니다. 마지막 하나는 2 최적화 된 것입니다.

나는 그것이 엄청나게 개선 될 것이라고 확신한다.

using System; 
using System.Collections.Generic; 
using System.Linq; 


namespace TSP 
{ 
    internal static class Program 
    { 
     private static void Main(string[] args) 
     { 
      //create an initial tour out of nearest neighbors 
      var stops = Enumerable.Range(1, 10) 
            .Select(i => new Stop(new City(i))) 
            .NearestNeighbors() 
            .ToList(); 

      //create next pointers between them 
      stops.Connect(true); 

      //wrap in a tour object 
      Tour startingTour = new Tour(stops); 

      //the actual algorithm 
      while (true) 
      { 
       Console.WriteLine(startingTour); 
       var newTour = startingTour.GenerateMutations() 
              .MinBy(tour => tour.Cost()); 
       if (newTour.Cost() < startingTour.Cost()) startingTour = newTour; 
       else break; 
      } 

      Console.ReadLine(); 
     } 


     private class City 
     { 
      private static Random rand = new Random(); 


      public City(int cityName) 
      { 
       X = rand.NextDouble() * 100; 
       Y = rand.NextDouble() * 100; 
       CityName = cityName; 
      } 


      public double X { get; private set; } 

      public double Y { get; private set; } 

      public int CityName { get; private set; } 
     } 


     private class Stop 
     { 
      public Stop(City city) 
      { 
       City = city; 
      } 


      public Stop Next { get; set; } 

      public City City { get; set; } 


      public Stop Clone() 
      { 
       return new Stop(City); 
      } 


      public static double Distance(Stop first, Stop other) 
      { 
       return Math.Sqrt(
        Math.Pow(first.City.X - other.City.X, 2) + 
        Math.Pow(first.City.Y - other.City.Y, 2)); 
      } 


      //list of nodes, including this one, that we can get to 
      public IEnumerable<Stop> CanGetTo() 
      { 
       var current = this; 
       while (true) 
       { 
        yield return current; 
        current = current.Next; 
        if (current == this) break; 
       } 
      } 


      public override bool Equals(object obj) 
      { 
       return City == ((Stop)obj).City; 
      } 


      public override int GetHashCode() 
      { 
       return City.GetHashCode(); 
      } 


      public override string ToString() 
      { 
       return City.CityName.ToString(); 
      } 
     } 


     private class Tour 
     { 
      public Tour(IEnumerable<Stop> stops) 
      { 
       Anchor = stops.First(); 
      } 


      //the set of tours we can make with 2-opt out of this one 
      public IEnumerable<Tour> GenerateMutations() 
      { 
       for (Stop stop = Anchor; stop.Next != Anchor; stop = stop.Next) 
       { 
        //skip the next one, since you can't swap with that 
        Stop current = stop.Next.Next; 
        while (current != Anchor) 
        { 
         yield return CloneWithSwap(stop.City, current.City); 
         current = current.Next; 
        } 
       } 
      } 


      public Stop Anchor { get; set; } 


      public Tour CloneWithSwap(City firstCity, City secondCity) 
      { 
       Stop firstFrom = null, secondFrom = null; 
       var stops = UnconnectedClones(); 
       stops.Connect(true); 

       foreach (Stop stop in stops) 
       { 
        if (stop.City == firstCity) firstFrom = stop; 

        if (stop.City == secondCity) secondFrom = stop; 
       } 

       //the swap part 
       var firstTo = firstFrom.Next; 
       var secondTo = secondFrom.Next; 

       //reverse all of the links between the swaps 
       firstTo.CanGetTo() 
         .TakeWhile(stop => stop != secondTo) 
         .Reverse() 
         .Connect(false); 

       firstTo.Next = secondTo; 
       firstFrom.Next = secondFrom; 

       var tour = new Tour(stops); 
       return tour; 
      } 


      public IList<Stop> UnconnectedClones() 
      { 
       return Cycle().Select(stop => stop.Clone()).ToList(); 
      } 


      public double Cost() 
      { 
       return Cycle().Aggregate(
        0.0, 
        (sum, stop) => 
        sum + Stop.Distance(stop, stop.Next)); 
      } 


      private IEnumerable<Stop> Cycle() 
      { 
       return Anchor.CanGetTo(); 
      } 


      public override string ToString() 
      { 
       string path = String.Join(
        "->", 
        Cycle().Select(stop => stop.ToString()).ToArray()); 
       return String.Format("Cost: {0}, Path:{1}", Cost(), path); 
      } 

     } 


     //take an ordered list of nodes and set their next properties 
     private static void Connect(this IEnumerable<Stop> stops, bool loop) 
     { 
      Stop prev = null, first = null; 
      foreach (var stop in stops) 
      { 
       if (first == null) first = stop; 
       if (prev != null) prev.Next = stop; 
       prev = stop; 
      } 

      if (loop) 
      { 
       prev.Next = first; 
      } 
     } 


     //T with the smallest func(T) 
     private static T MinBy<T, TComparable>(
      this IEnumerable<T> xs, 
      Func<T, TComparable> func) 
      where TComparable : IComparable<TComparable> 
     { 
      return xs.DefaultIfEmpty().Aggregate(
       (maxSoFar, elem) => 
       func(elem).CompareTo(func(maxSoFar)) > 0 ? maxSoFar : elem); 
     } 


     //return an ordered nearest neighbor set 
     private static IEnumerable<Stop> NearestNeighbors(this IEnumerable<Stop> stops) 
     { 
      var stopsLeft = stops.ToList(); 
      for (var stop = stopsLeft.First(); 
       stop != null; 
       stop = stopsLeft.MinBy(s => Stop.Distance(stop, s))) 
      { 
       stopsLeft.Remove(stop); 
       yield return stop; 
      } 
     } 
    } 
} 
+0

비록 내가 형식을 제대로 이해하지 못하는 것 같아. –

+0

+1 실제로 작동 * 거의 * 작동 구현을 –

+0

정말 작동! – garik

4

음, TSP에 대한 솔루션은 항상 완벽하지 않을 것입니다. 코드는 없지만 여기서는 2-Opt에 대해 알아 보겠습니다.

  1. Next, Prev 및 City 속성이 있고 Next 및 Prev가 포함 된 배열을 반환하는 Stops 속성이있는 클래스가 필요합니다.
  2. 함께 연결하면 둘러보기가 표시됩니다. Tour에는 Stop 속성 (중지가 수행 할 것임)과 AllStops 속성이 있으며 getter가 정지 지점을 걷고 반환합니다.
  3. 둘러보기를 수행하고 비용을 반환하는 메서드가 필요합니다. 그 Tour라고 부르 자 .Cost().
  4. 정류장을 걷고 개별적으로 걸어 다니는 Tour.Clone()이 필요합니다.
  5. 두 가장자리가 전환 된 둘러보기 세트를 생성하는 메서드가 필요합니다. 그들 모두에에
  6. 통화 PossibleMutations() 당신의 NN 솔루션
  7. 통화 비용()이 Tour.PossibleMutations()를
  8. 시작을 호출하고 비용까지 가장 낮은 결과
  9. 반복을 하나를 수행 내려 가지 않는다
+0

당신이 bruteforce에 가려고한다면, 최적의 것을 찾으십시오! –

+0

@ 모론 - 최소 스패닝 트리와 2-opt 간의 관계를 잘 모르겠습니다. MST 사전 주문서를 사용하고 2-opt 등을 신청한다고 말하고 있습니까? –

+0

내 잘못입니다. 2-opt는 최적의 2 배를 의미한다고 생각했는데 MST가 작동합니다. 나는 내 대답을 삭제했다. –

2

문제가 유클리드 거리이며 최적의 3/2은 당신이 Christofides 알고리즘을 원하는 내 알고리즘에 의해 생성 된 솔루션의 비용이합니다. ACO와 GA에는 보장 된 비용이 없습니다.