求解一个约束优化问题

题目描述 

以下问题是模拟如何节省工料的约束优化问题,其中集合 A 是工料集合,集合B 是部
件集合,其中的数值代表工料和部件长度,问题是用集合 A 里面的工料生产集合B 里面的
部件,目标是节省工料:

 

输入文件为input.txt,共 4 行,第一行为A 的元素个数m,第 2 行为m 个值,用英文
逗号隔开,代表A 的元素,第 3 行为B 的元素个数n,第 4 行为n 个值,用英文逗号隔开,
代表B 的元素。m 和n 的最大值 20

例如输入文件:
4
3.5, 6, 9.2,10
3
1, 2, 4.5

输出文件为output.txt,共 n 行,每行 3 个值,其中第i 行的第一个值是B 里面第i 个
值,第二个值是一个索引值 j,表示将B 里面的第i 个值映射到A 的第j 个值,第三个值是A
里面的第j 个值的具体数值。i 和 j 的取值都是从 1 开始。每行的值用英文逗号隔开。 

解题思路:

本题中我们需要找出所有满足A集合中符合大于B集合中各个元素,并且和最小的元素集合,值得注意的是,映射可以是一对一映射也可以是多对一,故可以将此问题抽象为CSP中的回溯搜索问题,采用最少剩余值(MRV)变量优先的方法:

  1. 首先对A和B集合进行排序,找出各个集合中剩余值最小的数(其中A集合剩余值最小的为4.5,B集合中剩余值最小的为6),进行第一轮计算。
  2. 将6减去4.5,剩余1.5,替代原B集合中的6,然后在A集合中找出符合的最大的数,发现是1,递归重复此步骤,得出0.5,发现没有符合的数。

此时重复上一步,得出2对应的值为3.5,答案为(1-6, 2-3.5, 4.5-6),所以y的值为6+3.5=9.5,从剩余值最小的往上遍历,重复以上步骤,遇到y的值更小的进行更新。

同时我也进行性能优化,在每次递归时从A集合剩余最小的元素,到刚好能满足B集合中所有元素之和的元素之间进行选择,减少了递归对比次数,对性能有一定的优化。

最后我们在读写文件时,要注意输入输出的格式、顺序。

本实验采取了CSP回溯问题中的最少剩余值(MRV)变量优先算法并给出了代码实现。在与几位同学交换测试用例后,也均能得出正确结论,可以验证此算法的正确性。虽然无法证明此算法为最优算法,但相较于暴力破解,还是做出了很多的优化,降低了部分的算法复杂度。

代码部分

首先创建一个工具类,用来保存答案。 

import java.util.ArrayList;

public class Result {
    private double y ;
    private ArrayList<ArrayList> resultArray;

    public  Result(){
        y = 0;
        resultArray = new ArrayList<>();
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public ArrayList<ArrayList> getResultArray() {
        return resultArray;
    }

    public void setResultArray(ArrayList<ArrayList> result) {
        this.resultArray = result;
    }

}

然后利用回溯法,进行求解。

private static void findMinY(ArrayList<Double> arrayA, ArrayList<Double> arrayB, Result resultTemp, Result result) {
        if(arrayA.size() == 0 || arrayB.size() == 0) {
            if(result.getY() == 0 ||result.getY() > resultTemp.getY()){
                result.setY(resultTemp.getY());
                result.setResultArray(resultTemp.getResultArray());
                resultTemp.setResultArray(new ArrayList<>());
                resultTemp.setY(0);
                return;
            }
            return;
        }
        int starIndex = 0, finalIndex = arrayA.size() - 1;
        while(starIndex < arrayA.size() - 1 && arrayA.get(starIndex) < arrayB.get(arrayB.size() - 1)){
            starIndex++;
        }
        if(starIndex >= arrayA.size()) starIndex--;

        double sumB = 0;
        for(int i = 0; i < arrayB.size(); i++){
            sumB = sumB + arrayB.get(i);
        }
        while(finalIndex > -1 && arrayA.get(finalIndex) > sumB){
            finalIndex--;
        }
        if(finalIndex < arrayA.size() - 1) finalIndex++;
        int middleIndex = starIndex;

        while(middleIndex >= starIndex && middleIndex <= finalIndex){
            double rest = arrayA.get(middleIndex) - arrayB.get(arrayB.size() - 1);
            ArrayList<Double> temp = new ArrayList<>();
            ArrayList<ArrayList> arrayTemp = resultTemp.getResultArray();
            double yTemp = resultTemp.getY();
            yTemp = yTemp + arrayA.get(middleIndex);
            resultTemp.setY(yTemp);
            temp.add(arrayB.get(arrayB.size() - 1));
            temp.add(arrayA.get(middleIndex));
            arrayTemp.add(temp);
            deleteArrayB.add(arrayB.get(arrayB.size() - 1));
            arrayB.remove(arrayB.size() - 1);

            for(int i = arrayB.size() - 1; i >= 0; i--){
                if(rest > arrayB.get(i)){
                    ArrayList<Double> tempI = new ArrayList<>();
                    tempI.add(arrayB.get(i));
                    tempI.add(arrayA.get(middleIndex));
                    arrayTemp.add(tempI);
                    rest = rest - arrayB.get(i);
                    deleteArrayB.add(arrayB.get(i));
                    arrayB.remove(i);
                }
            }
            deleteArrayA.add(arrayA.get(middleIndex));
            arrayA.remove(middleIndex);
            resultTemp.setResultArray(arrayTemp);

            if((arrayB.size() == 0 && result.getY() == 0) || (arrayB.size() == 0 && result.getY() > resultTemp.getY())){
                result.setY(resultTemp.getY());
                result.setResultArray(resultTemp.getResultArray());
                resultTemp.setResultArray(new ArrayList<>());
                resultTemp.setY(0);
                return;
            }

            findMinY(arrayA, arrayB, resultTemp, result);
            middleIndex++;
            while(!deleteArrayA.isEmpty()){
                arrayA.add(deleteArrayA.remove(0));
            }
            Collections.sort(arrayA);
            while(!deleteArrayB.isEmpty()){
                arrayB.add(deleteArrayB.remove(0));
            }
            Collections.sort(arrayB);
            deleteArrayA.clear();
            deleteArrayB.clear();
        }
        return;
    }

主函数

import java.util.ArrayList;
import java.util.Arrays;
import java.io.*;
import java.util.Collections;

public class ConstraintOptimization {

    /**
     * 查找排序后的数值在原数组中的索引位置
     * */
    public static int Seek(double[] arr, double number){
        for (int index = 0;index<arr.length;index++) {
            if (arr[index] == number) {
                //找到数值,返回索引
                return index;
            }
        }return -1;
    }

    /**
     * 读取txt文件
     */
    public static ArrayList<String> readFile(String pathname) {
        ArrayList<String> input = new ArrayList<>();
        //防止文件建立或读取失败,用catch捕捉错误并打印,也可以throw;
        //不关闭文件会导致资源的泄露,读写文件都同理
        try (FileReader reader = new FileReader(pathname);
             BufferedReader br = new BufferedReader(reader) // 建立一个对象,它把文件内容转成计算机能读懂的语言
        ) {
            String line;
            while ((line = br.readLine()) != null) {
                // 一次读入一行数据
                input.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return input;
    }

    /**
     * 写txt文件
     * */
    public static void writeFile(String writename, ArrayList<ArrayList> result) {
        try {
            File writeName = new File(writename);
            writeName.createNewFile(); // 创建新文件,有同名的文件的话直接覆盖
            try (FileWriter writer = new FileWriter(writeName);
                 BufferedWriter out = new BufferedWriter(writer)
            ) {
                for(int i = 0; i < result.size(); i++){
                    String str = result.get(i).toString();
                    out.write(str + "\r");
                }
                out.flush(); // 把缓存区内容写入文件
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 进行约束优化 返回一个矩阵
     * */

    public static void Constraint_Optimization(double[] A, double[] B, Result result){
        double[] Asort =A.clone();
        //对A和B数组进行排序 方便找到最适合组件的原料
        Arrays.sort(Asort);
        double[] Bsort =B.clone();
        Arrays.sort(Bsort);
        ArrayList<Double> arrayA = toArraylist(Asort);
        ArrayList<Double> arrayB = toArraylist(Bsort);

        Result resultTemp = new Result();
        findMinY(arrayA,arrayB, resultTemp ,result);
        return;
    }

    static ArrayList<Double> deleteArrayA = new ArrayList<>();
    static ArrayList<Double> deleteArrayB = new ArrayList<>();
    private static void findMinY(ArrayList<Double> arrayA, ArrayList<Double> arrayB, Result resultTemp, Result result) {
        if(arrayA.size() == 0 || arrayB.size() == 0) {
            if(result.getY() == 0 ||result.getY() > resultTemp.getY()){
                result.setY(resultTemp.getY());
                result.setResultArray(resultTemp.getResultArray());
                resultTemp.setResultArray(new ArrayList<>());
                resultTemp.setY(0);
                return;
            }
            return;
        }
        int starIndex = 0, finalIndex = arrayA.size() - 1;
        while(starIndex < arrayA.size() - 1 && arrayA.get(starIndex) < arrayB.get(arrayB.size() - 1)){
            starIndex++;
        }
        if(starIndex >= arrayA.size()) starIndex--;

        double sumB = 0;
        for(int i = 0; i < arrayB.size(); i++){
            sumB = sumB + arrayB.get(i);
        }
        while(finalIndex > -1 && arrayA.get(finalIndex) > sumB){
            finalIndex--;
        }
        if(finalIndex < arrayA.size() - 1) finalIndex++;
        int middleIndex = starIndex;

        while(middleIndex >= starIndex && middleIndex <= finalIndex){
            double rest = arrayA.get(middleIndex) - arrayB.get(arrayB.size() - 1);
            ArrayList<Double> temp = new ArrayList<>();
            ArrayList<ArrayList> arrayTemp = resultTemp.getResultArray();
            double yTemp = resultTemp.getY();
            yTemp = yTemp + arrayA.get(middleIndex);
            resultTemp.setY(yTemp);
            temp.add(arrayB.get(arrayB.size() - 1));
            temp.add(arrayA.get(middleIndex));
            arrayTemp.add(temp);
            deleteArrayB.add(arrayB.get(arrayB.size() - 1));
            arrayB.remove(arrayB.size() - 1);

            for(int i = arrayB.size() - 1; i >= 0; i--){
                if(rest > arrayB.get(i)){
                    ArrayList<Double> tempI = new ArrayList<>();
                    tempI.add(arrayB.get(i));
                    tempI.add(arrayA.get(middleIndex));
                    arrayTemp.add(tempI);
                    rest = rest - arrayB.get(i);
                    deleteArrayB.add(arrayB.get(i));
                    arrayB.remove(i);
                }
            }
            deleteArrayA.add(arrayA.get(middleIndex));
            arrayA.remove(middleIndex);
            resultTemp.setResultArray(arrayTemp);

            if((arrayB.size() == 0 && result.getY() == 0) || (arrayB.size() == 0 && result.getY() > resultTemp.getY())){
                result.setY(resultTemp.getY());
                result.setResultArray(resultTemp.getResultArray());
                resultTemp.setResultArray(new ArrayList<>());
                resultTemp.setY(0);
                return;
            }

            findMinY(arrayA, arrayB, resultTemp, result);
            middleIndex++;
            while(!deleteArrayA.isEmpty()){
                arrayA.add(deleteArrayA.remove(0));
            }
            Collections.sort(arrayA);
            while(!deleteArrayB.isEmpty()){
                arrayB.add(deleteArrayB.remove(0));
            }
            Collections.sort(arrayB);
            deleteArrayA.clear();
            deleteArrayB.clear();
        }
        return;
    }


    private static ArrayList<Double> toArraylist(double[] array) {
        ArrayList<Double> arraylist = new ArrayList<>();
        for(int i = 0; i < array.length; i++){
            arraylist.add(array[i]);
        }
        return arraylist;
    }

    /**
     * 将读取的文件数据流中的string 类型转化为题目所需的 double类型
     * */
    public static double[] StringToDouble(String[] string){
        double[] dou = new double[string.length];
        for(int i = 0; i < string.length; i++){
            dou[i] = Double.parseDouble(string[i]);
        }
        return dou;
    }

    /**
     * 主函数
     * */
    public static void main(String[] args){
        ArrayList<String> input = readFile("C:\\Users\\ricar\\Desktop\\input.txt");
        //合法性判断
        if(input.size() != 4){
            System.out.println("输入文件错误!请重新输入");
            return;
        }
        int n = Integer.parseInt(input.get(0));
        String[] Astring = new String[n];
        Astring = input.get(1).split(",");
        double[] A = StringToDouble(Astring);
        int m = Integer.parseInt(input.get(2));
        double[] B = StringToDouble(input.get(3).split(","));
        //进行合法性判断
        if(n != A.length || m != B.length){
            System.out.println(" 输入数组的个数错误 ");
            return;
        }
        Result result = new Result();
        Constraint_Optimization(A, B ,result);
        ArrayList<ArrayList> resultArray = seekIndex(result.getResultArray(), A, B);
        writeFile("C:\\Users\\ricar\\Desktop\\output.txt", resultArray);
    }

    private static ArrayList<ArrayList> seekIndex(ArrayList<ArrayList> resultArray, double[] a, double[] b) {
        ArrayList<ArrayList> result = new ArrayList<>();
        for(int i = 0; i < b.length; i++){
            for(int j = 0; j < resultArray.size(); j++){
                if(resultArray.get(j).get(0).equals(b[i])){
                    ArrayList<Double> resultList = new ArrayList<>();
                    resultList.add(b[i]);
                    resultList.add((double) Seek(a, (Double) resultArray.get(j).get(1)));
                    resultList.add((Double) resultArray.get(j).get(1));
                    result.add(resultList);
                    break;
                }
            }
        }
        return result;
    }
}

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值