Sidneys1

C# - Generic Djikstra's w/ Caching

Apr 24th, 2015
353
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 4.86 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4.  
  5. namespace VirusFactory.Model.Geography
  6. {
  7.     public static class DijkstraHelper<T> where T : ILocation
  8.     {
  9.         private static readonly Dictionary<T,Dictionary<T, LinkedList<Connection<T>>>> CacheDictionary = new Dictionary<T, Dictionary<T, LinkedList<Connection<T>>>>();
  10.  
  11.         public static int DictionarySize => CacheDictionary.Sum(o => o.Value.Count);
  12.  
  13.         public static LinkedList<Connection<T>> CalculateShortestPathBetween(T source, T destination, IEnumerable<Connection<T>> paths)
  14.         {
  15.             if (!CacheDictionary.ContainsKey(source))
  16.                 CacheDictionary.Add(source, CalculateFrom(source, paths));
  17.            
  18.             return CacheDictionary[source][destination];
  19.         }
  20.  
  21.         public static Dictionary<T, LinkedList<Connection<T>>> CalculateShortestFrom(T source, IEnumerable<Connection<T>> paths)
  22.         {
  23.             if (!CacheDictionary.ContainsKey(source))
  24.                 CacheDictionary.Add(source, CalculateFrom(source, paths));
  25.  
  26.             return CacheDictionary[source];
  27.         }
  28.        
  29.         private static Dictionary<T, LinkedList<Connection<T>>> CalculateFrom(T source, IEnumerable<Connection<T>> paths)
  30.         {
  31.             // validate the paths
  32.             var connections = paths as IList<Connection<T>> ?? paths.ToList();
  33.             if (connections.Any(p => p.LocationA.Equals(p.LocationB)))
  34.                 throw new ArgumentException("No path can have the same source and destination");
  35.            
  36.             // keep track of the shortest paths identified thus far
  37.             var shortestPaths = new Dictionary<T, KeyValuePair<double, LinkedList<Connection<T>>>>();
  38.  
  39.             // keep track of the locations which have been completely processed
  40.             var locationsProcessed = new List<T>();
  41.            
  42.             // include all possible steps, with Int.MaxValue cost
  43.             connections.SelectMany(p => new[] { p.LocationA, p.LocationB })             // union source and destinations
  44.                     .Distinct()                                                        // remove duplicates
  45.                     .ToList()                                                          // ToList exposes ForEach
  46.                     .ForEach(s => shortestPaths.Set(s, double.MaxValue, null));        // add to ShortestPaths with MaxValue cost
  47.            
  48.             // update cost for self-to-self as 0; no path
  49.             shortestPaths.Set(source, 0, null);
  50.            
  51.             // keep this cached
  52.             var locationCount = shortestPaths.Keys.Count;
  53.  
  54.             while (locationsProcessed.Count < locationCount)
  55.             {
  56.                 var locationToProcess = default(T);
  57.  
  58.                 //Search for the nearest location that isn't handled
  59.                 foreach (var location in shortestPaths.OrderBy(p => p.Value.Key).Select(p => p.Key).ToList().Where(location => !locationsProcessed.Contains(location)))
  60.                 {
  61.                     // ReSharper disable once CompareOfFloatsByEqualityOperator
  62.                     if (shortestPaths[location].Key == double.MaxValue)
  63.                         return shortestPaths.ToDictionary(k => k.Key, v => v.Value.Value); //ShortestPaths[destination].Value;
  64.  
  65.                     locationToProcess = location;
  66.                     break;
  67.                 }
  68.  
  69.                 var selectedPaths = connections.Where(p => p.LocationA.Equals(locationToProcess));
  70.  
  71.                 foreach (var path in selectedPaths.Where(path => shortestPaths[path.LocationB].Key > path.Length + shortestPaths[path.LocationA].Key))
  72.                 {
  73.                     shortestPaths.Set(
  74.                         path.LocationB,
  75.                         path.Length + shortestPaths[path.LocationA].Key,
  76.                         shortestPaths[path.LocationA].Value.Union(new[] { path }).ToArray());
  77.                 }
  78.                
  79.                 //Add the location to the list of processed locations
  80.                 locationsProcessed.Add(locationToProcess);
  81.             } // while
  82.            
  83.             return shortestPaths.ToDictionary(k => k.Key, v => v.Value.Value);
  84.         }
  85.     }
  86.  
  87.     public struct Connection<T> where T : ILocation
  88.     {
  89.         public readonly T LocationA;
  90.         public readonly T LocationB;
  91.         public double Length;
  92.  
  93.         public Connection(T locationA, T locationB)
  94.         {
  95.             LocationA = locationA;
  96.             LocationB = locationB;
  97.  
  98.             Length = Distance(LocationA.Latitude, locationA.Longitude, locationB.Latitude, locationB.Longitude);
  99.         }
  100.  
  101.         public static double Distance(double lat1, double lon1, double lat2, double lon2)
  102.         {
  103.             var theta = lon1 - lon2;
  104.             var dist = Math.Sin(Deg2Rad(lat1))*Math.Sin(Deg2Rad(lat2)) +
  105.                        Math.Cos(Deg2Rad(lat1))*Math.Cos(Deg2Rad(lat2))*Math.Cos(Deg2Rad(theta));
  106.  
  107.             dist = Math.Acos(dist);
  108.             dist = Rad2Deg(dist);
  109.             dist *= 111.18957696; // Magic number whoooo. Convters to km;
  110.             return dist;
  111.         }
  112.  
  113.         private static double Deg2Rad(double deg)
  114.         {
  115.             return deg * Math.PI / 180.0;
  116.         }
  117.  
  118.         private static double Rad2Deg(double rad)
  119.         {
  120.             return rad/Math.PI*180.0;
  121.         }
  122.  
  123.         public override int GetHashCode()
  124.         {
  125.             return LocationA.GetHashCode() ^ LocationB.GetHashCode();
  126.         }
  127.  
  128.         public override bool Equals(object obj)
  129.         {
  130.             if (!(obj is Connection<T>))
  131.                 return false;
  132.             var o = (Connection<T>) obj;
  133.  
  134.             return ((o.LocationA.Equals(LocationA) && (o.LocationB.Equals(LocationB))) ||
  135.                     (o.LocationA.Equals(LocationB) && o.LocationB.Equals(LocationA)));
  136.         }
  137.     }
  138.     public interface ILocation
  139.     {
  140.         double Latitude { get; }
  141.         double Longitude { get; }
  142.     }
  143. }
Advertisement
Add Comment
Please, Sign In to add comment