簡體   English   中英

Java中旅行商問題的蠻力算法

[英]Brute force algorithm for the Traveling Salesman Problem in Java

我正在學校的一個數學課上做項目,我選擇做“旅行推銷員問題”,這是我一直想做的事情。 但是,我的蠻力解決算法遇到了問題。

* 請轉到底部的更新以查看代碼的最新版本



如果您知道旅行商問題是什么,請跳過此段落:總而言之,TSP如下所示:您是一名推銷員,想要訪問區域中的每個城市(一個城市本質上就是地圖上的一個點) 。 在有界的x和y區域中有“ n”座城市,並且每個城市都與每個城市相連(假設是一條直線道路)。 您需要在允許訪問每個城市的城市中找到最短的路線。我想使用的算法之一(並且我將需要測試其他算法)是蠻力法,它會檢查所有可能的路線並返回最短的路線路線。 之所以不總是使用它,是因為它要求我們檢查(n-1)! 可能的路線,並且隨着“ n”的增加,這個數目變得越來越大-實際上,只有50個城市,那就是608281864034267560872252163321295376887552831379210240000000000要檢查的路線。

假設所有示例均假設我們將使用一個具有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");

    }

這是我的邏輯:(注意:城市對象具有一個名為“ visited”的“標志”布爾變量)

(如果未標記所有路線,並且該路線未包含每個可能的城市)

  • 首先從1個未標記的城市出發。
  • 標記“最后未標記的”城市(此城市為“樞軸”)
  • 找到每個“ NOT IN ROUTE R”的城市,並將其添加到新路線。
  • 在每條路由上遞歸調用BruteForce方法。

(如果未標記所有路線,但該路線包含每個城市)

  • 標記最后一個城市

(否則...這意味着該路線已標記每個城市並包含每個可能的城市)

  • 查看這是否是最短的路線-如果是,將其存儲在全局變量中

此圖像將幫助我解釋問題...因此,程序正確地沿着左側向下移動。 但是,在到達最低點之后,人們會期望遞歸會跳回到步驟4,它確實會這樣做。 但是,R現在沒有標記A城市而B沒有標記城市B,然后遞歸地在包含Aflag和B的“新路線”上調用自己,R現在包括了所有4個城市,並且標記了所有4個城市。 之所以失敗,是因為它再次將城市D添加到“ newRoute”中,然后再次遞歸調用自身,並且在另一種方法中,由於我的區域中沒有5個城市,但錯誤地在r路徑中有5個城市,我們得到了數組錯誤(A,B,C,D,D)。

遞歸樹結構的有用圖片

問題與在循環中調用遞歸有關,或者與在遞歸調用中引用路由“ r”有關。

如果您有任何想法,我將非常感謝您的幫助。

感謝任何會幫助我的人。 我會將整個項目發送給任何願意提供幫助的人。


更新

好吧,所以我試圖簡化和簡化我的原始方法,這就是我所擁有的:

public void BruteForceFindBestRoute(Route r, ArrayList<City> citiesNotInRoute)
    {
        if(!citiesNotInRoute.isEmpty())
        {
            for(int i = 0; i<citiesNotInRoute.size(); i++)
            {
                City justRemoved = (City) citiesNotInRoute.remove(0).clone();
                Route newRoute = (Route) r.clone();
                newRoute.addToRoute(justRemoved);

                BruteForceFindBestRoute(newRoute, citiesNotInRoute);
                citiesNotInRoute.add(justRemoved);
            }
        }
        else //if(citiesNotInRoute.isEmpty())
        {
            if(IsBestRoute(r))
                bestRoute = r;
        }

    }

問題是for循環中的變量i似乎失去了它的含義,即當我們退出遞歸時,循環不會繼續。 有想法嗎?

遞歸調用返回后,您應該從路線中刪除城市。 你做這個:

            Route newRoute = r;
            newRoute.addToRoute(citiesNotInRoute.get(i));
            BruteForceFindBestRoute(newRoute);

但絕不newRoute.removeFromRoute或類似的東西。

請注意,您正在編寫Java,在Java中,對象的分配是通過reference完成 這意味着rnewRoute同一對象 newRoute不是您可能期望的r的獨立副本。 因此,對newRoute任何修改也會更改r 您可能要在此處明確復制路線。 Java的術語是clone 確保克隆足夠深,即克隆所有相關的數據結構,而不是在原始克隆與其克隆之間共享它們。

注意:在很多地方都可以提高代碼效率,但是由於在任何情況下蠻力都遠不能提高效率,並且您只在談論幾個城市,也許您不必在意。 但是,如果您要研究替代方案,請考慮為所有未訪問的城市維護一個鏈接列表。 每次調用都將遍歷該列表,刪除一個元素,遞歸然后將該元素放回原處。 無需在每次調用時從頭開始構建此列表。 可以將跳舞鏈接的想法巧妙地應用於此任務,以替代預制的鏈接列表實現。

編輯:

您的代碼的以下變體對我有用:

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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM