Пример #1
0
        // Moves an item in the list. That is, if we go from position 1 to 5, the items
        // that were previously 2, 3, 4 and 5 become 1, 2, 3 and 4.
        public static void MoveLocations(Location[] locations, int fromIndex, int toIndex)
        {
            if (locations == null)
                throw new ArgumentNullException("locations");

            if (fromIndex < 0 || fromIndex >= locations.Length)
                throw new ArgumentOutOfRangeException("fromIndex");

            if (toIndex < 0 || toIndex >= locations.Length)
                throw new ArgumentOutOfRangeException("toIndex");

            var temp = locations[fromIndex];

            if (fromIndex < toIndex)
            {
                for(int i=fromIndex+1; i<=toIndex; i++)
                    locations[i-1] = locations[i];
            }
            else
            {
                for(int i=fromIndex; i>toIndex; i--)
                    locations[i] = locations[i-1];
            }

            locations[toIndex] = temp;
        }
Пример #2
0
        public static void MutateRandomLocations(Location[] locations)
        {
            if (locations == null)
                throw new ArgumentNullException("locations");

            if (locations.Length < 2)
                throw new ArgumentException("The locations array must have at least two items.", "locations");

            // I opted to give up to 10% of the chromosome size in number of mutations.
            // Maybe I should find a better number of make this configurable.
            int mutationCount = GetRandomValue(locations.Length/10) + 1;
            for(int mutationIndex=0; mutationIndex<mutationCount; mutationIndex++)
            {
                int index1 = GetRandomValue(locations.Length);
                int index2 = GetRandomValue(locations.Length-1);
                if (index2 >= index1)
                    index2++;

                switch(GetRandomValue(3))
                {
                    case 0: Location.SwapLocations(locations, index1, index2); break;
                    case 1: Location.MoveLocations(locations, index1, index2); break;
                    case 2: Location.ReverseRange(locations, index1, index2); break;
                    default: throw new InvalidOperationException();
                }
            }
        }
Пример #3
0
        public static double GetTotalDistance(Location startLocation, Location[] locations)
        {
            if (startLocation == null)
                throw new ArgumentNullException("startLocation");

            if (locations == null)
                throw new ArgumentNullException("locations");

            if (locations.Length == 0)
                throw new ArgumentException("The locations array must have at least one element.", "locations");

            foreach(var location in locations)
                if (location == null)
                    throw new ArgumentException("The locations array can't contain null values.");

            double result = startLocation.GetDistance(locations[0]);
            int countLess1 = locations.Length-1;
            for(int i=0; i<countLess1; i++)
            {
                var actual = locations[i];
                var next = locations[i+1];

                var distance = actual.GetDistance(next);
                result += distance;
            }

            result += locations[locations.Length-1].GetDistance(startLocation);

            return result;
        }
Пример #4
0
        public static Location[] GetRandomDestinations(int count)
        {
            if (count < 2)
                throw new ArgumentOutOfRangeException("count");

            Location[] result = new Location[count];
            for(int i=0; i<count; i++)
            {
                int x = GetRandomValue(700) + 50;
                int y = GetRandomValue(500) + 50;
                result[i] = new Location(x, y);
            }

            return result;
        }
        public TravellingSalesmanAlgorithm(Location startLocation, Location[] destinations, int populationCount)
        {
            if (startLocation == null)
                throw new ArgumentNullException("startLocation");

            if (destinations == null)
                throw new ArgumentNullException("destinations");

            if (populationCount < 2)
                throw new ArgumentOutOfRangeException("populationCount");

            if (populationCount % 2 != 0)
                throw new ArgumentException("The populationCount parameter must be an even value.", "populationCount");

            _startLocation = startLocation;
            destinations = (Location[])destinations.Clone();

            foreach(var destination in destinations)
                if (destination == null)
                    throw new ArgumentException("The destinations array can't contain null values.", "destinations");

            // This commented method uses a search of the kind "look for the nearest non visited location".
            // This is rarely the shortest path, yet it is already a "somewhat good" path.
            destinations = _GetFakeShortest(destinations);

            _populationWithDistances = new KeyValuePair<Location[], double>[populationCount];

            // Create initial population.
            for(int solutionIndex=0; solutionIndex<populationCount; solutionIndex++)
            {
                var newPossibleDestinations = (Location[])destinations.Clone();

                // Try commenting the next 2 lines of code while keeping the _GetFakeShortest active.
                // If you avoid the algorithm from running and press reset, you will see that it always
                // start with a path that seems "good" but is not the best.
                //for(int randomIndex=0; randomIndex<newPossibleDestinations.Length; randomIndex++)
                    //RandomProvider.FullyRandomizeLocations(newPossibleDestinations);

                var distance = Location.GetTotalDistance(startLocation, newPossibleDestinations);
                var pair = new KeyValuePair<Location[], double>(newPossibleDestinations, distance);

                _populationWithDistances[solutionIndex] = pair;
            }

            Array.Sort(_populationWithDistances, _sortDelegate);
        }
Пример #6
0
        public static void FullyRandomizeLocations(Location[] locations)
        {
            if (locations == null)
                throw new ArgumentNullException("locations");

            // This code does a full randomization of the destination locations without creating a new array.
            // If we have 3 items, for example, it will first determine which one of the 3 will be in the last
            // place, swapping items if needed. Then, it will chose which one of the first 2 items is put at the
            // second place. And, as everything works by swaps, the item in the first position is obviously the
            // only one that remains, that's why the i>0 is used instead of i>=0.
            int count = locations.Length;
            for(int i=count-1; i>0; i--)
            {
                int value = GetRandomValue(i+1);
                if (value != i)
                    Location.SwapLocations(locations, i, value);
            }
        }
Пример #7
0
        public static void ReverseRange(Location[] locations, int startIndex, int endIndex)
        {
            if (locations == null)
                throw new ArgumentNullException("locations");

            if (startIndex < 0 || startIndex >= locations.Length)
                throw new ArgumentOutOfRangeException("startIndex");

            if (endIndex < 0 || endIndex >= locations.Length)
                throw new ArgumentOutOfRangeException("endIndex");

            if (endIndex < startIndex)
            {
                int temp = endIndex;
                endIndex = startIndex;
                startIndex = temp;
            }

            while(startIndex<endIndex)
            {
                Location temp = locations[endIndex];
                locations[endIndex] = locations[startIndex];
                locations[startIndex] = temp;

                startIndex++;
                endIndex--;
            }
        }
Пример #8
0
 public double GetDistance(Location other)
 {
     int diffX = X - other.X;
     int diffY = Y - other.Y;
     return Math.Sqrt(diffX*diffX + diffY*diffY);
 }
Пример #9
0
        public static void SwapLocations(Location[] locations, int index1, int index2)
        {
            if (locations == null)
                throw new ArgumentNullException("locations");

            if (index1 < 0 || index1 >= locations.Length)
                throw new ArgumentOutOfRangeException("index1");

            if (index2 < 0 || index2 >= locations.Length)
                throw new ArgumentOutOfRangeException("index2");

            var location1 = locations[index1];
            var location2 = locations[index2];
            locations[index1] = location2;
            locations[index2] = location1;
        }
Пример #10
0
        private IEnumerable<Location> _AddEndLocation(Location[] middleLocations)
        {
            foreach (var location in middleLocations)
                yield return location;

            yield return _startLocation;
        }
Пример #11
0
        internal static void _CrossOver(Location[] locations1, Location[] locations2, bool mutateFailedCrossovers)
        {
            // I am not validating parameters because this method is internal.
            // If you want to make it public, you should validate the parameters.

            var availableLocations = new HashSet<Location>(locations1);

            int startPosition = GetRandomValue(locations1.Length);
            int crossOverCount = GetRandomValue(locations1.Length - startPosition);

            if (mutateFailedCrossovers)
            {
                bool useMutation = true;
                int pastEndPosition = startPosition + crossOverCount;
                for (int i=startPosition; i<pastEndPosition; i++)
                {
                    if (locations1[i] != locations2[i])
                    {
                        useMutation = false;
                        break;
                    }
                }

                // if the crossover is not going to give any change, we
                // force a mutation.
                if (useMutation)
                {
                    MutateRandomLocations(locations1);
                    return;
                }
            }

            Array.Copy(locations2, startPosition, locations1, startPosition, crossOverCount);
            List<int> toReplaceIndexes = null;

            // Now we will remove the used locations from the available locations.
            // If we can't remove one, this means it was used in duplicate. At this
            // moment we only register those indexes that have duplicate locations.
            int index = 0;
            foreach(var value in locations1)
            {
                if (!availableLocations.Remove(value))
                {
                    if (toReplaceIndexes == null)
                        toReplaceIndexes = new List<int>();

                    toReplaceIndexes.Add(index);
                }

                index++;
            }

            // Finally we will replace duplicated items by those that are still available.
            // This is how we avoid having chromosomes that contain duplicated places to go.
            if (toReplaceIndexes != null)
            {
                // To do this, we enumerate two objects in parallel.
                // If we could use foreach(var indexToReplace, location from toReplaceIndexex, location1) it would be great.
                using(var enumeratorIndex = toReplaceIndexes.GetEnumerator())
                {
                    using(var enumeratorLocation = availableLocations.GetEnumerator())
                    {
                        while(true)
                        {
                            if (!enumeratorIndex.MoveNext())
                            {
                                Debug.Assert(!enumeratorLocation.MoveNext());
                                break;
                            }

                            if (!enumeratorLocation.MoveNext())
                                throw new InvalidOperationException("Something wrong happened.");

                            locations1[enumeratorIndex.Current] = enumeratorLocation.Current;
                        }
                    }
                }
            }
        }
        private Location[] _Reproduce(Location[] parent)
        {
            var result = (Location[])parent.Clone();

            if (!MustDoCrossovers)
            {
                // When we are not using cross-overs, we always apply mutations.
                RandomProvider.MutateRandomLocations(result);
                return result;
            }

            // if you want, you can ignore the next three lines of code and the next
            // if, keeping the call to RandomProvider.MutateRandomLocations(result); always
            // invoked and without crossovers. Doing that you will not promove evolution through
            // "sexual reproduction", yet the good result will probably be found.
            int otherIndex = RandomProvider.GetRandomValue(_populationWithDistances.Length/2);
            var other = _populationWithDistances[otherIndex].Key;
            RandomProvider._CrossOver(result, other, MustMutateFailedCrossovers);

            if (!MustMutateFailedCrossovers)
                if (RandomProvider.GetRandomValue(10) == 0)
                    RandomProvider.MutateRandomLocations(result);

            return result;
        }
        private Location[] _GetFakeShortest(Location[] destinations)
        {
            Location[] result = new Location[destinations.Length];

            var currentLocation = _startLocation;
            for(int fillingIndex=0; fillingIndex<destinations.Length; fillingIndex++)
            {
                int bestIndex = -1;
                double bestDistance = double.MaxValue;

                for(int evaluatingIndex=0; evaluatingIndex<destinations.Length; evaluatingIndex++)
                {
                    var evaluatingItem = destinations[evaluatingIndex];
                    if (evaluatingItem == null)
                        continue;

                    double distance = currentLocation.GetDistance(evaluatingItem);
                    if (distance < bestDistance)
                    {
                        bestDistance = distance;
                        bestIndex = evaluatingIndex;
                    }
                }

                result[fillingIndex] = destinations[bestIndex];
                currentLocation = destinations[bestIndex];
                destinations[bestIndex] = null;
            }

            return result;
        }