简体   繁体   中英

Microsoft Solver Foundation Location Dispersion Goal

I am trying to use the microsoft solver foundation so I could be able to select the 3 most dispersed locations out of a collection of locations. I have added two goals to my model, one that ensures 3 locations are picked and another ensuring they are the 3 most dispersed locations.

static void Main(string[] args)
    {
        Location candidate1 = new Location(0, 43.432, -79.432);
        Location candidate2 = new Location(1, 43.0, -79.0);
        Location candidate3 = new Location(2, 23.0, 29.0);
        Location candidate4 = new Location(3, 43.0, -79.0);
        Location candidate5 = new Location(4, 43.0, -79.0);
        Location[] candidates = {candidate1, candidate2, candidate3, candidate4, candidate5};
        SolverContext solver = new SolverContext();
        Model model = solver.CreateModel();
        model.Name = "LocationModel";
        Set items = new Set(Domain.Any, "candidates");
        Decision take = new Decision(Domain.Boolean, "candidate", items);
        model.AddDecision(take);
        Parameter locations = new Parameter(Domain.RealNonnegative, "locations", items);
        locations.SetBinding(candidates, "ID", "ID");
        model.AddParameter(locations);


        var dist = from l1 in candidates from l2 in candidates select new { ID1 = l1.ID, ID2 = l2.ID, dist = Geography.GetDistance(l1.Latitude, l1.Longitude, l2.Latitude, l2.Longitude) };
        Parameter distance = new Parameter(Domain.RealNonnegative, "Location", items, items);
        distance.SetBinding(dist, "dist", "ID1", "ID2");
        model.AddParameter(distance);            
        Term goal = Model.Sum(Model.ForEach(items, i => Model.ForEach(items, j => take[i]*take[j]* distance[i, j])));

        model.AddConstraint("LocationsQuantityMax", Model.Sum(Model.ForEach(items, item => take[item])) == 3);
        model.AddGoal("Dispersion", GoalKind.Maximize, goal); 

        Directive directive = new HybridLocalSearchDirective();            
        Solution solution = solver.Solve(directive);
        List<Location> locations1 = new List<Location>();
        if (solution.Decisions.Any())
        {
            var selections = solution.Decisions.First().GetValues()
                .Where(d => (bool) d[0])
                .Select(d => Convert.ToInt32(d[1]));

            locations1.AddRange(
                from c in candidates
                join s in selections on c.ID equals s
                select c);
        }

        foreach (Location location in locations1)
        {
            Console.WriteLine("ID: {0}, Latitude: {1}, Longitude: {2}", location.ID, location.Latitude, location.Longitude);
        } 

Additionally my Location class looks as follows:

class Location
{
    public int ID { get; private set; }
    public double Latitude { get; private set; }
    public double Longitude { get; private set; }

    public Location(int LocationID, double latitude, double longitude)
    {
        this.ID = LocationID;
        this.Latitude = latitude;
        this.Longitude = longitude;
    }
} 

and my distance calculation is:

    public static double ToRad(this double num)
    {
        return num * Math.PI / 180;
    }

    public static double GetDistance(double lat1, double lon1, double lat2, double lon2)
    {
        const int r = 6371; // Radius of earth in KM

        // Convert to Radians
        lat1 = lat1.ToRad();
        lon1 = lon1.ToRad();
        lat2 = lat2.ToRad();
        lon2 = lon2.ToRad();

        // Spherical Law of Cosines
        var resultCos =
            Math.Acos(
                Math.Sin(lat1) * Math.Sin(lat2) +
                Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1)
                ) * r;

        return resultCos;
    } 

Now my code has two problems and I dont know where they originate,

Firstly,

my code as it stands works for most permutations of 5 locations with three of them being identical longitude latitude (ie in this; example candidate2, candidate4 and candidate5) but for certain permutations of candidates it will not return the most dispersed 3 (ie for the same problem instance just change the order of the candidates declaration). I cant fathom why.

Secondly,

If you comment out the constraint to select at least 3 locations, instead of selecting all of the locations like expected it selects none.

The fact that my goal works for most instances seems to indicate its correct, does anybody have an idea why it falters sometimes?

This is not homework and thanks a lot to whomever replies.

I believe the issue is that you're specifying the HybridLocalSearchDirective . I removed that from your posted code and got the optimal answer:

ID: 0, Latitude: 43.432, Longitude: -79.432
ID: 1, Latitude: 43, Longitude: -79
ID: 2, Latitude: 23, Longitude: 29

Rearranging the terms or even adding new Locations still results in the optimal solution.

Additionally, I then removed the constraint and your code selected all five Location objects as the solution.

I wish I could shed some light on why this works, but I'm just a weekend warrior when it comes to MSF. This is the most information I can find about the HybridLocalSearchDirective and how it arrives at a solution. Importantly, the first limitation noted is:

It does not guarantee optimal results.

This could be the answer to your first question (why it does not return the maximally dispersed locations).

As to your second question (why does removing the constraint not result in more selected Locations), I can't say for sure. Your problem specification could possibly be a degenerate case for that directive, though I can't figure out why.

Not the best answer, but I hope it helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM