public static PathStats ShortestPath(DirectedGraph g, int s)
        {
            #region Initialization
            var stats = new PathStats(g.V);
            for (var i = 0; i < stats.Dist.Length; i++)
            {
                stats.Dist[i] = int.MaxValue;
                stats.Prev[i] = -1;
            }
            stats.Dist[s] = 0;
            #endregion

            var dfsStats = Dfs.DepthFirstSearch(g);
            var linearization = dfsStats.Linearization;
            Debug.WriteLine(dfsStats);
            foreach (var u in linearization)
            {
                foreach (var v in g.Adjacent(u))
                {
                    if (stats.Dist[v.V2] > stats.Dist[v.V1] + v.Weight)
                    {
                        stats.Dist[v.V2] = stats.Dist[v.V1] + v.Weight;
                        stats.Prev[v.V2] = u;
                    }
                }
            }

            return stats;
        }
Example #2
0
        public static PathStats ShortestPath(GraphBase g, int s)
        {
            #region Initialization
            var ps = new PathStats(g.V);
            for (int i = 0; i < ps.Dist.Length; i++)
            {
                ps.Dist[i] = int.MaxValue;
                ps.Prev[i] = -1;
            }
            ps.Dist[s] = 0;
            #endregion

            for (int i = 0; i < g.V - 1; i++)//run |V|-1 times
            {
                for (var u = g.V - 1; u >= 0; u--)//'S' starts first, earlier to get finall status
                {
                    foreach (var e in g.Adjacent(u))
                    {
                        if (ps.Dist[u] == int.MaxValue)//if distance of u hasn't ever be udpated, skip
                            break;
                        if (ps.Dist[e.V2] > ps.Dist[e.V1] + e.Weight)
                        {
                            ps.Dist[e.V2] = ps.Dist[e.V1] + e.Weight;
                            ps.Prev[e.V2] = u;
                        }
                    }
                }
            }
            return ps;
        }
Example #3
0
        /// <summary>
        /// Dijkstra algorithm (shortest path) based on graph g for start vertice s. Positive cycles are allowed in shortest path algorithm
        /// </summary>
        /// <param name="g">Graph for search</param>
        /// <param name="s">Start vertice</param>
        public static PathStats ShortestPath(GraphBase g, int s)
        {
            var ps = new PathStats(g.V);

            for (var i = 0; i < ps.Dist.Length; i++)
            {
                ps.Dist[i] = int.MaxValue;
                ps.Prev[i] = -1;
            }

            ps.Dist[s] = 0;//start vertice

            var pq = new IndexMinPQ<Distance>(ps.Dist.Length);
            for (int i = 0; i < ps.Dist.Length; i++)
            {
                pq.Insert(i, new Distance { V = i, Dist = ps.Dist[i] });
            }

            while (!pq.IsEmpty())
            {
                var v = pq.DelRoot();

                foreach (var e in g.Adjacent(v))
                {
                    if (ps.Dist[e.V2] > ps.Dist[v] + e.Weight)
                    {
                        ps.Dist[e.V2] = ps.Dist[v] + e.Weight;
                        ps.Prev[e.V2] = v;
                        pq.ChangeKey(e.V2, new Distance { V = e.V2, Dist = ps.Dist[e.V2] });
                    }
                }
            }

            return ps;
        }
Example #4
0
        private static PathStats GetPathStats(GraphBase g, int s, bool isShortest)
        {
            var ps = new PathStats(g.V);

            for (var i = 0; i < ps.Dist.Length; i++)
            {
                ps.Dist[i] = isShortest ? int.MaxValue : int.MinValue;
                ps.Prev[i] = -1;
            }

            ps.Dist[s] = 0;//start vertice
            var pq = isShortest ? new MinPQ<Distance>(ps.Dist.Length) : new MaxPQ<Distance>(ps.Dist.Length);
            pq.Insert(new Distance { Dist = 0, V = s });

            while (!pq.IsEmpty())
            {
                var v = pq.DelRoot();

                foreach (var e in g.Adjacent(v.V))
                {
                    if ((isShortest && ps.Dist[e.V2] > ps.Dist[v.V] + e.Weight) ||  //shortest path
                        (!isShortest && ps.Dist[e.V2] < ps.Dist[v.V] + e.Weight))   //longest path
                    {
                        ps.Dist[e.V2] = ps.Dist[v.V] + e.Weight;
                        ps.Prev[e.V2] = v.V;
                        pq.Insert(new Distance { V = e.V2, Dist = ps.Dist[e.V2] });
                    }
                }
            }

            return ps;
        }
        public async Task <bool> ApplyPathRatingAsyc(BiblePathsCoreDbContext context)
        {
            // This Rating System is likely to change over time but for now we've got the following rules.
            // Rating is the average of the following Scores ranging from 0 - 5 (there is a little arbitrary uplift)
            // 1. Initial Rating on entry to this method counts as one Rating (all paths start at 4.5)
            // 2. A Rating is calculated from the % of Reads (FinishCount / StartCount * 100) this is a % of 5
            // 3. A "Book Diversity Rating" where a path gets 1 point for each unique Book and a point for spanning testaments up to 5
            // 4. Average of all UserRatings (uplift creates a 1.1 - 5.5 range)

            int    firstNTBook = 40; // the first book in the New Testemant is book 40 in the protestant Bible.
            int    ScoreCount  = 0;  // this becomes the number of total Scores that we will average together.
            double TotalScore  = 0;

            // load all of the PathStats for this Path... we'll need these
            // We need to load the collection of Steps assocaited with this Path, as well as the Nodes.
            context.Entry(this)
            .Collection(p => p.PathStats)
            .Load();
            context.Entry(this)
            .Collection(p => p.PathNodes)
            .Load();

            // 1. Initial Rating on entry to this method counts as one Rating (all paths start at 4.5)
            if (ComputedRating.HasValue)
            {
                TotalScore += (double)ComputedRating;
                ScoreCount++;
            }

            // 2. A Rating is calculated from the % of Reads (FinishCount / StartCount * 100) this is a % of 5.25 (a quarter point uplift)
            int NumStarts = PathStats.Where(s => s.EventType == (int)EventType.PathStarted).ToList().Count;

            if (NumStarts > 0)
            {
                int    NumCompletes = PathStats.Where(s => s.EventType == (int)EventType.PathCompleted).ToList().Count;
                double ReadPercent  = NumCompletes / NumStarts;
                TotalScore += ReadPercent * 5.25;
                ScoreCount++;
            }

            // 3. A "Book Diversity Rating" where a path gets 1 point for each unique Book up to 5 (any count over 5 = 5.25)
            if (PathNodes.Count > 0)
            {
                int BookDiversityScore = 0;
                var BookHash           = new HashSet <int>();
                foreach (PathNode node in PathNodes)
                {
                    BookHash.Add(node.BookNumber);
                }
                BookDiversityScore += BookHash.Count();
                // Does the path span testaments?
                if ((BookHash.Max() >= firstNTBook) && (BookHash.Min() < firstNTBook))
                {
                    BookDiversityScore += 1; // Add a free point for spanning testaments.
                }

                TotalScore += BookDiversityScore > 5 ? 5.25 : BookDiversityScore;
                ScoreCount++;
            }

            // 4. Average of all UserRatings
            List <PathStat> UserRatings = PathStats.Where(s => s.EventType == (int)EventType.UserRating).ToList();
            int             SumRatings  = 0;
            int             NumRatings  = 0;

            foreach (PathStat UserRating in UserRatings)
            {
                int Rating = 0;
                try
                {
                    Rating = int.Parse(UserRating.EventData);
                }
                catch
                {
                    continue;
                }
                if (Rating > 0 && Rating <= 5)
                {
                    SumRatings += Rating;
                    NumRatings++;
                }
            }
            double AvgUserRating = SumRatings / NumRatings;

            if (AvgUserRating > 0 && AvgUserRating <= 5)
            {
                TotalScore += (AvgUserRating * 1.1); // Make this count by applying a small uplift.
                ScoreCount++;
            }

            // Ok now it's time to calculate a our new rating.
            double TempRating = TotalScore / ScoreCount;

            TempRating = TempRating > 5 ? 5 : TempRating;
            TempRating = TempRating < 0 ? 0.5 : TempRating;

            // Save our Rating Now.
            context.Attach(this).State = EntityState.Modified;
            ComputedRating             = (decimal)TempRating;
            await context.SaveChangesAsync();

            return(true);
        }