1. 程式人生 > 其它 >201971010230-孟姣姣 實驗二 軟體工程個人專案 {0-1}揹包問題專案報告

201971010230-孟姣姣 實驗二 軟體工程個人專案 {0-1}揹包問題專案報告

課程班級部落格連結 https://edu.cnblogs.com/campus/xbsf/2019nwnucs
這個作業要求連結 https://edu.cnblogs.com/campus/xbsf/2019nwnucs/homework/12527
我的課程學習目標 <(1)掌握軟體專案個人開發流程。(2)掌握Github釋出軟體專案的操作方法>
這個作業在哪些方面幫助我實現學習目標 <專案設計開發以及提交到Github>
專案Github的倉庫連結地址 https://github.com/MJJBQ/software_project

1.需求分析
本專案需要設計一個可以使用動態規劃演算法、貪心演算法以及回溯演算法實現{0-1}揹包問題求解的軟體。揹包問題是NP Complete問題,也是一個經典的組合優化問題,有著廣泛而重要的應用背景。{0-1}揹包問題是最基本的KP問題形式,比起揹包問題不需要考慮每一種物品該放入多少,而是隻考慮放入不放入,實現起來相對容易。需求中一共給出了9組陣列,這些資料存在18個檔案中,18個檔案分為2組,其中每一組裡面的.in檔案中儲存了該組每個物品的重量和價值,.out檔案中儲存了揹包容量。專案要求對任意一組資料都可以使用動態規劃演算法、貪心演算法以及回溯演算法進行求解,獲得求解結果和求解過程所需要的時間。最後將求解結果寫入到txt或者excel檔案中。
2.功能設計
2.1基本功能
本專案基本功能主要有資料讀入、資料散點圖繪製、資料按照重量非遞增排序、動態規劃演算法解決0-1揹包問題、貪心演算法解決0-1揹包問題、回溯演算法解決0-1揹包問題以及結果儲存。
2.2擴充套件功能
本專案擴充套件功能主要有多演算法排序和散點圖座標繪製。多演算法排序根據資料排列順序、使用不同排序演算法進行排序,這裡主要使用了氣泡排序、選擇排序和插入排序。散點圖座標繪製通過計算座標軸長度和資料量個數等間隔劃分座標軸。
3.設計實現
本專案一共包括KnapsackMain、Picture、DP、Greedy、Back和FileBean六個類,KnapsackMain是主類,裡面有程式入口main方法,還有根據重量非遞增排序方法sortByWeight。這裡使用三種排序方式實現。檔案資料讀取方法readDataFromFile,該類使用FileReader和BufferReader讀取檔案資料。結果回寫檔案方法writeData2Fime,該類使用FileWriter和BufferedWriter回寫檔案資料。Picture類用來繪製散點圖,該類繼承Jpanel類,主要對paintComponent方法進行了重寫。DP類負責使用動態規劃演算法解決0-1揹包問題,Greedy類負責使用貪心演算法解決0-1揹包問題,Back類負責使用回溯演算法解決0-1揹包問題,FileBean是對檔案資料的封裝,主要包括檔名、檔案資料和揹包容量。Picture、DP、Greedy、Back和FileBean都是KnapsackMain類的靜態內部類,類之間關係結構如圖所示。

4.測試執行
4.1 檔案讀寫功能測試,如下圖所示,讀取所有檔案資料,並顯示檔名列表。

4.2 散點圖繪製功能測試,如下圖所示,繪製所選資料來源的散點圖,並顯示擴充套件的座標刻度。

4.3 按重量非遞增排序功能測試,如下圖所示,對選擇資料來源進行排序,並顯示排序後的資料。

4.4 動態規劃求解功能測試,如下圖所示,對選擇資料來源使用動態規劃演算法求解,並顯示求解結果。

4.5 動態規劃求解功能測試,如下圖所示,對選擇資料來源使用動態規劃演算法求解,並顯示求解結果。

4.6 動態規劃求解功能測試,如下圖所示,對選擇資料來源使用動態規劃演算法求解,並顯示求解結果。

4.7 結果回寫功能測試,如下圖所示,每次對問題求解後都會寫入對應檔案。


5.貼上自己覺得比較獨特的或滿意的程式碼片段,用部落格園的程式碼控制元件來顯示。
5.1檔案資料讀取程式碼

點選檢視程式碼
 private static ArrayList<FileBean> readDataFromFile(String root) {
    ArrayList<FileBean> fileLists = new ArrayList<FileBean>();
    File rootPath = new File(root);
    if (rootPath.exists()) {
        File files[] = rootPath.listFiles();
        if (files == null || files.length == 0) {
            System.out.println("資料夾為空");
            return null;
        } else {
            String realName = null;
            Map realMap = null;
            for (File item : files
            ) {
                if (item.isFile()) {
                    String name = item.getName();
                    String type = name.substring(name.lastIndexOf(".") + 1);
                    int totalWeight = 0;
                    if (type.equals("in")) {
                        try {
                            realName = name;
                            BufferedReader br = new BufferedReader(new FileReader(item));
                            String line;
                            List lists = new ArrayList<>();
                            while ((line = br.readLine()) != null) {
                                lists.add(line);
                            }
                            br.close();
                            int i = 0;
                            int data[][] = new int[lists.size()][2];
                            Iterator<String> iterator = lists.iterator();
                            while (iterator.hasNext()) {
                                String array[] = iterator.next().split(" ");
                                data[i][0] = Integer.valueOf(array[0]);
                                data[i][1] = Integer.valueOf(array[1]);
                                i++;
                            }
                            Map map = new HashMap<>();
                            map.put(name, data);
                            realMap = map;
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            return null;
                        } catch (IOException e) {
                            e.printStackTrace();
                            return null;
                        }
                    }
                    if (type.equals("out")) {
                        try {
                            BufferedReader br = new BufferedReader(new FileReader(item));
                            String line;
                            while ((line = br.readLine()) != null) {
                                totalWeight = Integer.valueOf(line.toString().trim());
                            }
                            br.close();
                            FileBean bean = new FileBean();
                            bean.setData(realMap);
                            bean.setName(realName);
                            bean.setTotalWeight(totalWeight);
                            fileLists.add(bean);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            return null;
                        } catch (IOException e) {
                            e.printStackTrace();
                            return null;
                        }
                    }
                }
            }
            return fileLists;
        }
    }
    return null;

}
5.2繪製散點圖、主要對paintComponent方法進行了重寫,並擴充套件了座標軸劃分,重寫後代碼如下
點選檢視程式碼
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D draw = (Graphics2D) g;
        draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        int w = getWidth();
        int h = getHeight();
        draw.draw(new Line2D.Double(DIS, DIS, DIS, h - DIS));
        draw.drawString("價值", DIS - 10, DIS);
        draw.draw(new Line2D.Double(DIS, h - DIS, w - DIS, h - DIS));
        draw.drawString("重量", w - DIS, h - DIS);
        int ymin = (h - 2 * DIS) / data.length;
        int xmin = (w - 2 * DIS) / data.length;
        draw.setPaint(Color.black);
        int ydis = ymin;
        int xdis = xmin;
        for (int i = 0; i < data.length; i++) {
            int x = data[i][0];
            int y = data[i][1];
            if (i != data.length - 1) {
                draw.drawString(xdis + "", xdis + DIS, h - DIS + 15);
                draw.drawString(ydis + "", DIS - 20, h - (ydis + DIS));
                xdis += xmin;
                ydis += ymin;
            }
            draw.fill(new Ellipse2D.Double(x + DIS, h - (y + DIS), 4, 4));

        }

    }
5.3按照重量非遞增排序程式碼如下:
點選檢視程式碼
    for (int i = 0; i < data.length; i++) {
        //選擇排序
        //首先在未排序的數列中找到最小(or最大)元素,然後將其存放到數列的起始位置;
        //接著,再從剩餘未排序的元素中繼續尋找最小(or最大)元素,然後放到已排序序列的末尾。
        //以此類推,直到所有元素均排序完畢。
        for (int j = i + 1; j < data.length; j++) {
            if (data[j][0] > data[i][0]) {
                int temp1 = data[j][0];
                data[j][0] = data[i][0];
                data[i][0] = temp1;
                int temp2 = data[j][1];
                data[j][1] = data[i][1];
                data[i][1] = temp2;
            }
        }
    }

    for (int i = 1; i < data.length; i++) {
        //插入排序
        for (int j = i ; j > 0; j--) {
            if (data[j][0] > data[i][0]) {
                int temp1 = data[j][0];
                data[j][0] = data[i][0];
                data[i][0] = temp1;
                int temp2 = data[j][1];
                data[j][1] = data[i][1];
                data[i][1] = temp2;
            }
        }
    }

    for (int i = 0; i < data.length; i++) {
        //氣泡排序
        for (int j = 0 ; j < data.length-1-i ; j++) {
            if (data[j][0] > data[i][0]) {
                int temp1 = data[j][0];
                data[j][0] = data[i][0];
                data[i][0] = temp1;
                int temp2 = data[j][1];
                data[j][1] = data[i][1];
                data[i][1] = temp2;
            }
        }
    }
5.4結果資料回寫方法writeData2Fime程式碼如下:
點選檢視程式碼
 private static void writeData2Fime(String path, int[][] data, ArrayList<Integer> list, int totalWeight, long time, String model) {
    File file = new File(path + ".txt");
    sortByWeight(data);
    if (!file.exists()) {
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try {
        BufferedWriter bw = new BufferedWriter(new FileWriter(file));
        bw.write(model+"演算法解決0-1揹包問題總耗時為"+time+"秒");
        bw.newLine();
        bw.write("商品資訊如下:");
        bw.newLine();
        bw.write("重量:");
        for (int j = 0; j < data.length; j++) {
            bw.write(data[j][0] + "\t");
        }
        bw.newLine();
        bw.write("價值:");
        for (int j = 0; j < data.length; j++) {
            bw.write(data[j][1] + "\t");
        }
        bw.newLine();
        bw.write("放入揹包的物品:");
        for (int j = 0; j < list.size(); j++) {
            bw.write(list.get(j) + "\t");
        }
        bw.newLine();
        bw.write("放入揹包的物品重量:");
        int weight = 0;
        for (int j = 0; j < list.size(); j++) {
            bw.write(data[list.get(j) - 1][0] + "\t");
            weight += data[list.get(j) - 1][0] - 1;
        }
        bw.write("總重量:" + weight);
        bw.newLine();
        bw.write("放入揹包的物品價值:");
        int value = 0;
        for (int j = 0; j < list.size(); j++) {
            bw.write(data[list.get(j) - 1][1] - 1 + "\t");
            value += data[list.get(j) - 1][1] - 1;
        }
        bw.write("總價值:" + value);
        bw.newLine();
        bw.write("揹包剩餘容量:" + (totalWeight - weight));
        bw.flush();
        bw.close();
        BufferedReader br = new BufferedReader(new FileReader(file));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        System.out.println("結果已寫入" + file.getAbsolutePath() + "檔案中");
    } catch (IOException e) {
        e.printStackTrace();
    }

 }
7.總結: 在設計軟體時,為了更好地遵循“模組化”設計思想,我對同一類問題,使用相同的方法解決,比如每一求解完成後,都對資料按重量非遞增排序都通過呼叫sortByWeight方法實現。對於相似的操作,全部分裝在一個類中,比如和動態規劃相關的演算法,全部用封裝在DP類中。 展示PSP,這個環節重要的是讓自己看到自己的估計和實際消耗時間,哪個環節耗時最多,哪個環節估計和實踐相差巨大?為什麼? 貪心演算法的實現耗時最多,對遞迴程式出口沒弄清楚,程式一直呼叫,造成棧溢位。檔案讀寫環節估計和實踐相差較大,原因在於理論邏輯清除,程式設計技術不熟練。 8.如果所有環節你都認真做了,此處你應該有很多經驗願意與大家分享。 本次專案對我來說很有意義,通過一週多的時間寫了600多行程式碼,在編碼能力提升的同時,對常見的排序演算法和優化演算法有了更多的瞭解,特別在貪心演算法使用遞迴實現時,對 程式出口條件的不清楚,使得程式一直遞迴呼叫,造成棧溢位。通過本次專案,在以後的實驗中我可能會更加用心。