Java中旅行商问题的蛮力算法
我正在为学校的数学课做一个项目,我选择了旅行推销员问题,这是我一直想调查的问题。 然而,我的暴力解决算法有问题 *请转到底部的更新以查看代码的最新版本Java中旅行商问题的蛮力算法,java,algorithm,recursion,traveling-salesman,Java,Algorithm,Recursion,Traveling Salesman,我正在为学校的数学课做一个项目,我选择了旅行推销员问题,这是我一直想调查的问题。 然而,我的暴力解决算法有问题 *请转到底部的更新以查看代码的最新版本 如果你知道旅行推销员的问题是什么,就跳过这一段: 尽可能概括地说,TSP是这样的:你是一个推销员,想要访问一个地区的每个城市(一个城市本质上是地图上的一个点)。在有边界的x和y区域中有“n”个城市,每个城市都与每个城市相连(假设有一条直路)。你需要在所有城市中找到最短的路线,以便访问每个城市。我想使用的算法之一(我需要测试其他算法)是蛮力算法
如果你知道旅行推销员的问题是什么,就跳过这一段: 尽可能概括地说,TSP是这样的:你是一个推销员,想要访问一个地区的每个城市(一个城市本质上是地图上的一个点)。在有边界的x和y区域中有“n”个城市,每个城市都与每个城市相连(假设有一条直路)。你需要在所有城市中找到最短的路线,以便访问每个城市。我想使用的算法之一(我需要测试其他算法)是蛮力算法,它检查所有可能的路线并返回最短路线。之所以不总是使用此选项,是因为它要求我们检查(n-1)!可能的路线,而且随着“n”的增加,这个数字变得越来越大——事实上,只有50个城市,这将是60828186403426756087225213321295376887552831792102400000000000条要检查的路线 假设在本文讨论的所有示例中,我们将使用一个包含4个城市的任意区域(即使该算法可以处理n个城市。此外,我们不关心距离-我们希望以暴力攻击每一条可能的路线) 下面是一张简单的图片,演示了我所说的内容(我从4个城市开始检查流程是否正常工作) 以下是蛮力算法(假设所有其他调用的方法都能正确工作,因为它们确实能正常工作): (请查看下面的详细说明) [守则]
public void BruteForceFindBestRoute(Route r) //Must start r having 1 unflagged city to begin with
{
if(!r.allFlagged() && r.route.size() != m.cities.size())
{
/*STEP 1 Begin with last unflagged city*/
City pivot = r.lastCityAdded();
/*STEP 2: Flag city*/
pivot.visited = true;
/*STEP 3: Find cities "NOT IN ROUTE"*/
ArrayList<City> citiesNotInRoute = new ArrayList<City>();
for(int i = 0; i<m.cities.size(); i++)
{
if(!r.isCityInRoute(m.cities.get(i).name))
{
citiesNotInRoute.add(m.cities.get(i));
}
}
/*STEP 4: Recursively call BruteForceFindBestRoute() using these cities added to the end of our original route*/
for(int i = 0; i<citiesNotInRoute.size(); i++)
{
Route newRoute = r;
newRoute.addToRoute(citiesNotInRoute.get(i));
BruteForceFindBestRoute(newRoute);
}
}
/*STEP 5: If the route is full but the last city isn't flagged, then flag it call BruteForceFindBestRoute() again, with the last city flagged*/
else if(!r.allFlagged() && r.route.size() == m.cities.size())
{
if(r.allFlaggedButLast())
{
Route x = r;
x.flagLastCity();
BruteForceFindBestRoute(x);
}
}
/*STEP 6: If all cities are flagged, the route is full. Check to see if it's the best route.*/
else if(r.allFlagged())
{
if(IsBestRoute(r))
bestRoute = r;
}
else
System.err.println("Error: somehow all cities got flagged, but the route isn't full");
}
public void BruteForceFindBestRoute(路由r)//必须从r开始,并从1个无障碍城市开始
{
如果(!r.allFlagged()&&r.route.size()!=m.cities.size())
{
/*第1步从最后一个无障碍城市开始*/
城市轴=r.lastCityAdded();
/*第二步:旗城*/
pivot.visted=true;
/*步骤3:查找“不在路线中”的城市*/
ArrayList citiesNotInRoute=新的ArrayList();
对于(int i=0;i,在递归调用返回后,您应该从路由中删除城市。您可以执行以下操作:
Route newRoute = r;
newRoute.addToRoute(citiesNotInRoute.get(i));
BruteForceFindBestRoute(newRoute);
但决不能使用新建路由。从路由中删除或类似的路由
请注意,您正在编写Java,而在Java中,对象的赋值是通过引用完成的。这意味着r
和newRoute
是同一个对象。newRoute
并不像您预期的那样是r
的独立副本。因此对newRoute
的任何修改也将改变r
。您可以可能需要在那里明确复制路由。Java术语是。确保克隆足够深,即克隆所有相关数据结构,而不是在原始数据结构和克隆数据结构之间共享它们
注意:有很多地方可以让你的代码更高效,但是在任何情况下,蛮力都是远远不够的,你只会谈论很少的城市,也许你不必在意。但是,如果你想研究替代方案,考虑一下保持所有未访问城市的单链表。帽子列表,删除元素,递归并放回元素。无需在每次调用中从头开始构建此列表。的思想可以灵活地应用于此任务,作为预制链表实现的替代方案
编辑:
您的代码的以下变体适用于我:
import java.util.*;
class SO11703827 {
private static ArrayList<Integer> bestRoute;
public static void bruteForceFindBestRoute
(ArrayList<Integer> r,
ArrayList<Integer> citiesNotInRoute)
{
if(!citiesNotInRoute.isEmpty())
{
for(int i = 0; i<citiesNotInRoute.size(); i++)
{
Integer justRemoved =
(Integer) citiesNotInRoute.remove(0);
ArrayList<Integer> newRoute =
(ArrayList<Integer>) r.clone();
newRoute.add(justRemoved);
bruteForceFindBestRoute(newRoute, citiesNotInRoute);
citiesNotInRoute.add(justRemoved);
}
}
else //if(citiesNotInRoute.isEmpty())
{
if(isBestRoute(r))
bestRoute = r;
}
}
private static boolean isBestRoute(ArrayList<Integer> r) {
System.out.println(r.toString());
return false;
}
public static void main(String[] args) {
ArrayList<Integer> lst = new ArrayList<Integer>();
for (int i = 0; i < 6; ++i)
lst.add(i);
ArrayList<Integer> route = new ArrayList<Integer>();
bruteForceFindBestRoute(route, lst);
}
}
import java.util.*;
SO11703827类{
私有静态数组列表最佳路径;
公共静态无效bruteForceFindBestRoute
(ArrayList r,
ArrayList citiesNotInRoute)
{
如果(!citiesNotInRoute.isEmpty())
{
对于(int i=0;这正是我想要做的——但是当递归调用返回时,我的路由(称为r)中的每个城市都在其中,这是我不理解的。我希望在第一个递归调用的第一次返回时,我会得到类似于r=[Af,Bf,C]的东西。然后在第一个递归调用的第二次返回时,r=[Af,B]然而,我在第一次递归调用的第一次返回时得到了这个结果:r=[Af,Bf,Cf,Df]这没有意义。如果我不清楚,请告诉我,我可以试着重新解释。谢谢你的快速回复。嗯,我可能会尝试你的链接列表想法。我将不得不考虑一下,但这听起来像是一个不错的替代方案。我一直在干扰我的第一个实现想法,6个多小时都没有用,所以我对n持开放态度新想法。@Matt,我的答案似乎不够清楚。我想我错过了你困惑的主要原因。现在编辑代码以指出共享对象。好的,我已经编写了正确的克隆()方法,我重新编写了蛮力方法以利用这些方法。它仍然不起作用。我重新编写了蛮力方法,试图使其更简单-请再次查看操作以查看底部的更新代码。再次感谢,我真的非常感谢您的帮助。希望这次更容易阅读Matt@Matt,您使用数组列表作为循环缓冲区的方式让我困惑了一会儿,但应该可以。一个ArrayDeque
将是该列表的更好实现,但这只与性能有关。克隆城市应该是不必要的。除此之外,事情对我来说是可行的,所以我假设t有问题他是你实现克隆的方式。@nhgrif是的,这个问题已经得到了回答,代码确实有效。问题在于我实现的“克隆”,这里甚至没有显示;在“更新”之后的暴力算法确实有效,因为它是
import java.util.*;
class SO11703827 {
private static ArrayList<Integer> bestRoute;
public static void bruteForceFindBestRoute
(ArrayList<Integer> r,
ArrayList<Integer> citiesNotInRoute)
{
if(!citiesNotInRoute.isEmpty())
{
for(int i = 0; i<citiesNotInRoute.size(); i++)
{
Integer justRemoved =
(Integer) citiesNotInRoute.remove(0);
ArrayList<Integer> newRoute =
(ArrayList<Integer>) r.clone();
newRoute.add(justRemoved);
bruteForceFindBestRoute(newRoute, citiesNotInRoute);
citiesNotInRoute.add(justRemoved);
}
}
else //if(citiesNotInRoute.isEmpty())
{
if(isBestRoute(r))
bestRoute = r;
}
}
private static boolean isBestRoute(ArrayList<Integer> r) {
System.out.println(r.toString());
return false;
}
public static void main(String[] args) {
ArrayList<Integer> lst = new ArrayList<Integer>();
for (int i = 0; i < 6; ++i)
lst.add(i);
ArrayList<Integer> route = new ArrayList<Integer>();
bruteForceFindBestRoute(route, lst);
}
}