List<Point> cities = /* ... */ ;
double distance(Point a, Point b) { /* ... */ };
hay una sola consulta LINQ que devuelve el viajante ruta más corta por el algoritmo del vecino más cercano como List<int>
de los índices de cities
?
List<Point> cities = /* ... */ ;
double distance(Point a, Point b) { /* ... */ };
hay una sola consulta LINQ que devuelve el viajante ruta más corta por el algoritmo del vecino más cercano como List<int>
de los índices de cities
?
No creo que pueda hacer todo en una sola consulta, algunas partes de los algoritmos deberán implementarse por separado.
Aquí hay una aplicación de fuerza bruta que examina todas las permutaciones de la ciudad y devuelve el camino más corto que visita todas las ciudades:
var bestPath =
cities.Permutations()
.MinBy(
steps => steps.Aggregate(
new { Sum = 0, Previous = default(Point) },
(acc, c) =>
new
{
Sum = acc.Sum + (acc.Previous != null ? Distance(c, acc.Previous) : 0),
Previous = c
},
acc => acc.Sum));
El método Permutations
extensión se define como sigue:
public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> source)
{
var query =
from item in source
from others in source.SkipOnce(item).Permutations()
select new[] { item }.Concat(others);
return query.DefaultIfEmpty(Enumerable.Empty<T>());
}
public static IEnumerable<T> SkipOnce<T>(this IEnumerable<T> source, T itemToSkip)
{
bool skipped = false;
var comparer = EqualityComparer<T>.Default;
foreach (var item in source)
{
if (!skipped && comparer.Equals(item, itemToSkip))
skipped = true;
else
yield return item;
}
}
De Por supuesto, existen métodos mucho mejores para resolver este problema, pero este funciona ... La mayor parte se basa en una sola consulta, las únicas partes que se implementan por separado no son específicas del problema en cuestión y pueden reutilizarse para otras tareas.
EDITAR: oops, me acabo de dar cuenta de que también usé el método MinBy
no estándar; usted lo puede encontrar in the MoreLinq project
Thomas, @digEmAll, gracias a los dos. No pude elegir entre sus dos respuestas igualmente buenas, así que marqué el primero. – ChrisJJ
@ChrisJJ, en realidad la respuesta de digEmAll está más cerca de lo que preguntaste; mi algoritmo no utiliza la heurística del "vecino más cercano" (no usa ninguna heurística en absoluto, solo intenta todas las rutas posibles y devuelve la mejor) –
Si sólo necesita más cercano algoritmo del vecino en una consulta LINQ sola, puede hacerlo de esta manera:
var nearestNeighTour = cities.Skip(1).Aggregate(
new List<int>() { 0 },
(list, curr) =>
{
var lastCity = cities[list.Last()];
var minDist = Enumerable.Range(0, cities.Count).Where(i => !list.Contains(i)).Min(cityIdx => distance(lastCity, cities[cityIdx]));
var minDistCityIdx = Enumerable.Range(0,cities.Count).Where(i => !list.Contains(i)).First(cityIdx => minDist == distance(lastCity, cities[cityIdx]));
list.Add(minDistCityIdx);
return list;
});
Aunque yo creo que es más fácil de leer usando para-bucles
por "vecino más cercano", ¿piensa en "ir a la próxima ciudad más cercana"? Bueno, seguro que puedes hacer esto con una cadena lineal, pero esto casi se lee como * deberes * ... – Carsten