/// <summary> /// O(n * k * logk) // much faster than O(n logn) for k << n /// </summary> /// <param name="s"></param> /// <param name="p"></param> /// <param name="conf"></param> /// <returns></returns> public long UpdateKnn(IAlgorithm s, IP p, KnnConfiguration conf) { conf = conf ?? new KnnConfiguration(); var sw = new Stopwatch(); sw.Start(); s.Knn.Clear(); s.Knn.Origin = p; s.Knn.K = conf.K; //var all = new List<IPDist>(); // naive version var sortedList2 = new SortedList2(); // better naive version var n = s.Points.Count; for (var i = 0; i < n; i++) { var p1 = s.Points[i]; if (p.Equals(p1)) { continue; // don't include origin } if (conf.SameTypeOnly && p.Type != p1.Type) { continue; // only same type used } var dist = p.Distance(p1.X, p1.Y); if (dist >= conf.MaxDistance) { continue; } var pdist = new PDist { Point = p1, Distance = dist }; //all.Add(pdist); // naive version sortedList2.Add(pdist, conf.K); // better naive version } //s.Knn.NNs = all.OrderBy(i => i.Distance).Take(conf.K).ToList(); // O(n logn) s.Knn.NNs = sortedList2.GetAll(); // O(n * k * logk) // better naive version sw.Stop(); return(sw.ElapsedMilliseconds); }
// O(n * m) where m is grid cells public long UpdateKnn(IAlgorithm s, IP p, KnnConfiguration conf) { if (conf == null) { conf = new KnnConfiguration(); } var sw = new Stopwatch(); sw.Start(); var max = Math.Max(s.Rectangle.XGrid, s.Rectangle.YGrid); s.Knn.Clear(); s.Knn.Origin = p; s.Knn.K = conf.K; UpdateKnnGridStrategy(s, max, conf); sw.Stop(); return(sw.ElapsedMilliseconds); }
/// <summary> /// Missing mapping to P objects /// KdTree could be refactored to use P object instead of Math.Net /// /// O(k * log n) /// </summary> /// <param name="s"></param> /// <param name="origin"></param> /// <param name="k"></param> /// <param name="conf"></param> /// <returns></returns> public long UpdateKnn(IAlgorithm s, IP origin, KnnConfiguration conf) { if (conf == null) { conf = new KnnConfiguration(); } if (conf.SameTypeOnly) { throw new NotImplementedException(); } if (conf.MaxDistance.HasValue) { throw new NotImplementedException(); } var sw = new Stopwatch(); sw.Start(); var vector = new DenseVector(new[] { origin.X, origin.Y }); var nn = Tree.FindNearestNNeighbors(vector, conf.K).ToList(); s.Knn.Clear(); s.Knn.Origin = origin; s.Knn.K = conf.K; foreach (var i in nn) { var p = new P { X = i[0], Y = i[1] }; var dist = origin.Distance(p.X, p.Y); s.Knn.NNs.Add(new PDist { Point = p, Distance = dist }); } sw.Stop(); return(sw.ElapsedMilliseconds); }
public long UpdateKnn(IP p, KnnConfiguration configuration) { UpdateIndex(p); return(Strategy.UpdateKnn(this, p, configuration)); }
// K nearest neighbor protected void UpdateKnnGridStrategy(IAlgorithm s, int max, KnnConfiguration conf) { var g = s.GridContainer; var nn = s.Knn; var square = s.Rectangle.Square; var currRing = new List <IPDist>(); var nextRing = new List <IPDist>(); for (var i = 1; i <= max; i++) { var temp = new List <IPDist>(); foreach (var p in nextRing) { if (p.Distance < i * square) { currRing.Add(p); } else { temp.Add(p); } } nextRing.Clear(); nextRing.AddRange(temp); var list = g.GetRing(nn.Origin, i); // First 9 squares, dont include origin if (i == 1) { list.AddRange(g.GetSet(nn.Origin).Where(a => !a.Equals(nn.Origin)).ToList()); } // Only NN on same type if set if (conf.SameTypeOnly) { list = list.Where(a => a.Type == nn.Origin.Type).ToList(); } var dataWasAdded = false; foreach (var p in list) { var dist = nn.Origin.Distance(p.X, p.Y); if (dist >= conf.MaxDistance) { continue; // not within max distance } if (dist < i * square) { currRing.Add(new PDist { Point = p, Distance = dist }); } else { nextRing.Add(new PDist { Point = p, Distance = dist }); } dataWasAdded = true; } if (conf.MaxDistance.HasValue && !dataWasAdded) { break; // max distance used and no new data was added, then we are done } if (currRing.Count >= nn.K) { break; // enough neighbors? then done } } if (currRing.Count < nn.K) { // Only NN on same type if set currRing.AddRange(conf.SameTypeOnly ? nextRing.Where(a => a.Point.Type == nn.Origin.Type).ToList() : nextRing); if (conf.MaxDistance.HasValue) { currRing = currRing.Where(i => i.Distance < conf.MaxDistance.Value).ToList(); } } currRing.Sort(); nn.NNs = currRing.Count > nn.K ? currRing.Take(nn.K).ToList() : currRing.ToList(); }
/// <summary> /// Run fast and slow version with same test data and display the running time /// </summary> static void Run() { // Config, test data showing ability to use negative positions var rect = new Rectangle { XMin = -200, XMax = 200, YMin = -100, YMax = 100, MaxDistance = 20, }; rect.Validate(); var conf = new KnnConfiguration { K = 100, SameTypeOnly = false, MaxDistance = null }; // Random points IPoints points = new Points(); var rand = new Random(); for (var i = 0; i < 500000; i++) { var x = rect.XMin + rand.NextDouble() * rect.Width; var y = rect.YMin + rand.NextDouble() * rect.Height; points.Data.Add(new P { X = x, Y = y, }); } points.Round(3); // Init algo IAlgorithm algo = new Algorithm(points, rect, StrategyType.Grid); // Use algo var origin = new P { X = 0, Y = 0 }; var duration = algo.UpdateKnn(origin, conf); // Print result WL(string.Format("{0} msec. {1}:", algo.Strategy.Name, duration)); WL("K Nearest Neighbors:"); WL(string.Format("Origin: {0}", origin)); WL(string.Format("Distance sum: {0}", algo.Knn.GetDistanceSum())); algo.Knn.NNs.OrderBy(i => i.Distance).ToList().ForEach(WL); // Update strategy algo.SetAlgorithmStrategy(new NaiveStrategy()); //algo.SetAlgorithmStrategy(new KdTreeStrategy(points.Data)); // Use algo duration = algo.UpdateKnn(origin, conf); // Print result WL(string.Format("\n{0} msec. {1}:", algo.Strategy.Name, duration)); WL("K Nearest Neighbors:"); WL(string.Format("Distance sum: {0}", algo.Knn.GetDistanceSum())); algo.Knn.NNs.OrderBy(i => i.Distance).ToList().ForEach(WL); }
// Example of usage: // Get 3 nearest -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=3 // Get 3 nearest type 1 -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=3;type=1 // Get nearest within 1 km -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=1000000;dist=1 public JsonKnnReply Knn(string s) { var sw = new Stopwatch(); sw.Start(); var invalid = new JsonKnnReply { }; if (string.IsNullOrEmpty(s)) { invalid.EMsg = "param is empty"; return(invalid); } var arr = s.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); if (arr.Length < 3) { invalid.EMsg = string.Format("param length is not valid: {0}", arr.Length); return(invalid); } var nvc = new NameValueCollection(); foreach (var a in arr) { var kv = a.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); if (kv.Length != 2) { continue; } nvc.Add(kv[0], kv[1]); } foreach (var key in Ajax.KnnReq) { if (nvc[key] != null) { continue; } invalid.EMsg = string.Format("param {0} is missing", key); return(invalid); } try { var y = nvc[Ajax.lat].Replace("_", ".").ToDouble(); var x = nvc[Ajax.lon].Replace("_", ".").ToDouble(); var k = int.Parse(nvc[Ajax.k]); var type = nvc[Ajax.type] == null ? -1 : int.Parse(nvc[Ajax.type]); double?dist = null; if (!string.IsNullOrEmpty(nvc[Ajax.dist])) { dist = nvc[Ajax.dist].Replace("_", ".").ToDouble(); } // knn algo var algo = MemoryDatabase.Data as IKnnAlgorithm; if (algo == null) { invalid.EMsg = "algorithm is not available"; return(invalid); } // Use algo var origin = new SingleDetectLibrary.Code.Data.P { X = x, Y = y, Type = type }; var knnSameTypeOnly = type != -1; var conf = new KnnConfiguration { K = k, SameTypeOnly = knnSameTypeOnly, MaxDistance = dist }; var duration = algo.UpdateKnn(origin, conf); var nns = algo.Knn.NNs.Select(p => p as PDist).ToList(); var gmsNns = new List <GmcPDist>(); foreach (var i in nns) { //i.Distance = Math.Round(i.Distance, 7); var pdist = new GmcPDist { Id = i.Point.Uid.ToString(), Point = i.Point, Distance = Math.Round(i.Distance, 7) }; gmsNns.Add(pdist); } var result = new JsonKnnReply { Data = string.Format("Distance in km, x: {0}; y: {1}; k: {2}; sameTypeOnly: {3}, algo msec: {4}", x.DoubleToString(), y.DoubleToString(), k, knnSameTypeOnly, duration), Nns = gmsNns, // cannot be interface, thus casting Msec = Sw(sw), }; return(result); } catch (Exception ex) { invalid.EMsg = string.Format("Parsing error param: {0}", ex.Message); return(invalid); } }