题目描述
以下问题是模拟如何节省工料的约束优化问题,其中集合 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)变量优先的方法:
- 首先对A和B集合进行排序,找出各个集合中剩余值最小的数(其中A集合剩余值最小的为4.5,B集合中剩余值最小的为6),进行第一轮计算。
- 将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;
}
}