【问题标题】:Finding bestfit with brute-force search使用蛮力搜索找到最佳匹配
【发布时间】:2023-03-25 11:53:01
【问题描述】:

您好,我是编程新手,需要指导。

我们在种植园。我有许多包含不同数量树木的字段。在每个领域都必须完成一系列任务。任务相同,但时间不同,因为字段大小不同。我想生成一个与当天分配的工作时间最匹配的任务列表。

我相信这是一个作业车间调度问题(NP-hard),但据我所知,由于数据集很小,它可以通过蛮力搜索来解决。如何在指定时间内生成所有组合并返回最佳组合?我试图查看一些伪代码,但坦率地说,我很迷茫,而且我的尝试很差:

//暴力搜索

// 1. first(P): generate a first candidate solution for P.
// 2. next(P,c): generate the next candidate for P after the current one c.
// 3. valid(P,c): check whether candidate c is a solution for P-
// 4. output(P,c): use the solution c of P as appropriate to the application.

public static ArrayList<Task> generatedList2(int totalTime) {  
    ArrayList<Task> bestFit = new ArrayList<>(); 
    ArrayList<Task> tmpFit = new ArrayList<>();
    int tmpTime = 0;
    int bestFitTime = -1;

    Task testTask = new Task("TestTask", 0);
    bestFit.add(testTask);                              //1

        for(Field f : fields) {                         //2
            for(Task t : f.getUndoneTasks()) {
                if(f.getTaskTime(t) < totalTime) {
                    tmpFit.add(t);
                    tmpTime += f.getTaskTime(t);
                    totalTime -= f.getTaskTime(t);
                }
            }
            if(tmpTime < bestFitTime) {                 //3
                bestFit = new ArrayList<Task>(tmpFit);  
                bestFitTime = tmpTime;
                tmpFit.clear();
            }
            else {
                tmpFit.clear();
            }
        }


    return bestFit;                                     //4
}

更新解决方案:

public static ArrayList<Task> RecursivelyGetAnswer(ArrayList<Task> listSoFar, 
ArrayList<Task> masterList, ArrayList<Task> bestList, int limit, int index) {

    for (int i = index; i < masterList.size(); i++) {

        Task task = masterList.get(i);
        double listSoFarTotal = getTotal(listSoFar) + task.getTaskLength();

        if (listSoFarTotal <= limit) {
            int bestListTotal = getTotal(bestList);

            listSoFar.add(task);

            if (listSoFarTotal > bestListTotal) {
                bestList = new ArrayList<Task>(listSoFar);
            }
            else if(100 - ((float) (limit - bestListTotal)/bestListTotal * 100) > 95) {
                break;
            }

            bestList = RecursivelyGetAnswer(listSoFar, masterList, bestList, limit, i+1);

            listSoFar.remove(task);
        }
    }

    return bestList;
}

【问题讨论】:

  • 您是否被限制在一天内工作一个领域?
  • 不,我不是,但是我的代码可能会暗示这一点。我不知道如何构造它,以便您可以在所有领域工作。

标签: java algorithm


【解决方案1】:

我想出了一个递归解决方案。就我的解决方案而言,我假设您只有一个任务列表,而不是字段内的任务。

import java.util.ArrayList;

class Task
{
    public int taskLength;

    public Task(int taskLength)
    {
        this.taskLength = taskLength;
    }

    @Override
    public String toString()
    {
        return "T" + taskLength;
    }
}

public class Answers 
{   
    public static void main(String args[])
    {
        ArrayList masterList = new ArrayList();
        //Add some sample data
        masterList.add(new Task(555));
        masterList.add(new Task(1054));
        masterList.add(new Task(888));
        masterList.add(new Task(5923));
        masterList.add(new Task(2342));
        masterList.add(new Task(6243));
        masterList.add(new Task(9227));
        masterList.add(new Task(4111));
        masterList.add(new Task(4322));
        masterList.add(new Task(782));

        final int limit = 9999;

        ArrayList<Task> bestList = RecursivelyGetAnswer(new ArrayList<>(), masterList, new ArrayList<>(), limit, 0);

        System.out.println(bestList.toString());
        System.out.println(getTotal(bestList));
    }

    public static ArrayList<Task> RecursivelyGetAnswer(ArrayList<Task> listSoFar, ArrayList<Task> masterList, ArrayList<Task> bestList, int limit, int index)
    {
        for (int i = index; i < masterList.size(); i++)
        {
            Task task = masterList.get(i);
            if (getTotal(listSoFar) + task.taskLength <= limit)
            {
                listSoFar.add(task);
                if (getTotal(listSoFar) > getTotal(bestList))
                {
                    bestList = new ArrayList(listSoFar);
                }

                bestList = RecursivelyGetAnswer(listSoFar, masterList, bestList, limit, i+1);

                listSoFar.remove(task);
            }
        }

        return bestList;
    }

    // Given a list of tasks, get the sum of the lengths of the tasks.
    public static int getTotal(ArrayList<Task> myList)
    {
        int sum = 0;
        for (Task t:myList)
            sum += t.taskLength;
        return sum;
    }
}

【讨论】:

  • 非常感谢您的回答,这是一个非常干净的解决方案。如果我实际上有一个任务字段的数组列表,我将如何进行?
  • 我有一个“索引”参数;您可能需要两个 - 一个用于字段索引,一个用于字段内任务的索引。主要的是递归不会尝试查看它已经完成的事情。
  • 谢谢。如果您不介意,我有一个后续问题。如果每个任务都是一个时间元素,这意味着假设 10 个任务的所有可能组合是 10!。据我了解,这是蛮力搜索背后的概念。您找到所有组合,然后选择最佳组合。在我的情况下,我们有一个约束说它必须是
  • 是的,代码使用了快捷方式。如果限制是 9999,并且您看到前两个任务的总和已经达到 12000,则无需检查 1+2+x 将是哪些任务 - 您已经知道它将超过限制。所以它实际上并没有检查x! 组合——在大多数情况下,这个数字通常会小一些。我猜它每次运行getTotal 时都会以一种或另一种方式检查组合...
  • 嗯,调用getTotal 一次而不是两次可能会提高代码的效率,想想吧。
猜你喜欢
  • 2014-02-23
  • 2017-08-07
  • 2021-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-11
  • 2013-09-14
相关资源
最近更新 更多