-1

我正在尝试在 Java 中进行多线程模拟,并且我已经设法使用队列来完成它,但是执行时间很长,关于如何优化它的任何想法?使用递归可以节省时间吗?

输入必须是这样的:

  • 2 5 表示 5 个作业有两个线程(worker)
  • 1 2 3 4 5 这是整数的作业,表示处理该作业的时间成本,因此输出将是:
  • 0 0 两个线程尝试同时从列表中获取作业,所以实际上索引为 0 的线程
  • 1 0 接受第一份工作并在 0 时刻开始工作
  • 0 1 1 秒后,线程 0 处理完第一个作业并从列表中取出第三个作业,并在时间 1 立即开始处理它。
  • 1 2 一秒钟后,线程 1 完成了第二个作业并从列表中取出第四个作业,并在时间 2 立即开始处理它
  • 0 4 最后,再过 2 秒,线程 0 完成了第三个作业并从列表中取出第五个作业,并在时间 4 立即开始处理它

这是代码:

import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.StringTokenizer;

public class JobQueue {
    private int numWorkers;
    private int[] jobs;
    private int[] assignedWorker;
    private long[] startTime;

    private FastScanner in;
    private PrintWriter out;

    public static void main(String[] args) throws IOException {
        new JobQueue().solve();
    }

    private void readData() throws IOException {
        numWorkers = in.nextInt();
        int m = in.nextInt();
        jobs = new int[m];
        for (int i = 0; i < m; ++i) {
            jobs[i] = in.nextInt(); 
        }
    }

    private void writeResponse() {
        for (int i = 0; i < jobs.length; ++i) {
            out.println(assignedWorker[i] + " " + startTime[i]);
        }
    }

    private void assignJobs() {
        // TODO: replace this code with a faster algorithm.
        assignedWorker = new int[jobs.length];
         startTime = new long[jobs.length];
         PriorityQueue<Integer> nextTimesQueue = new PriorityQueue<Integer>();
         HashMap<Integer, Set<Integer>> workersReadyAtTimeT = new HashMap<Integer,Set<Integer>>();
         long[] nextFreeTime = new long[numWorkers];
         int duration = 0;
         int bestWorker = 0;
         for (int i = 0; i < jobs.length; i++) {
          duration = jobs[i];
          if(i<numWorkers) {
            bestWorker = i;
            nextTimesQueue.add(duration);
            addToSet(workersReadyAtTimeT, duration, i,0);
          }else {
            int currentTime = nextTimesQueue.poll();
            Set<Integer> workersReady = workersReadyAtTimeT.get(currentTime);
            if (workersReady.size()>1) { 
              bestWorker = workersReady.iterator().next();
              workersReady.remove(bestWorker);
              workersReadyAtTimeT.remove(currentTime);
              workersReadyAtTimeT.put(currentTime,workersReady);
              nextTimesQueue.add(currentTime);
            } else {
              bestWorker = workersReady.iterator().next();
              workersReadyAtTimeT.remove(currentTime);
              nextTimesQueue.add(currentTime+duration);
              addToSet(workersReadyAtTimeT, duration, bestWorker, currentTime);
            }
          }
          
          assignedWorker[i] = bestWorker;
          startTime[i] = nextFreeTime[bestWorker];
          nextFreeTime[bestWorker] += duration;
         }
        }
    
    private void addToSet(HashMap<Integer, Set<Integer>> workersReadyAtTimeT, int duration, int worker, int current) {
        if(workersReadyAtTimeT.get(current+duration)==null) {
          HashSet<Integer> s = new HashSet<Integer>();
          s.add(worker);
          workersReadyAtTimeT.put(current+duration, s);
        }else {
          Set<Integer> s = workersReadyAtTimeT.get(current+duration);
          s.add(worker);
          workersReadyAtTimeT.put(current+duration,s);
         }
        }

    public void solve() throws IOException {
        in = new FastScanner();
        out = new PrintWriter(new BufferedOutputStream(System.out));
        readData();
        assignJobs();
        writeResponse();
        out.close();
    }

    static class FastScanner {
        private BufferedReader reader;
        private StringTokenizer tokenizer;

        public FastScanner() {
            reader = new BufferedReader(new InputStreamReader(System.in));
            tokenizer = null;
        }

        public String next() throws IOException {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                tokenizer = new StringTokenizer(reader.readLine());
            }
            return tokenizer.nextToken();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }
    }
}
 
4

1 回答 1

1

在我看来,您的jobsList对象是完全多余的,它包含的所有内容也都在jobs数组中,当您获取前面的元素时,您会在jobs[i]. 为了加快一点速度,您可以将 int 的构造函数从循环中取出,并为它们分配新的数字。另一个优化是在第一个工作期间不搜索,numWorkers因为你知道在你耗尽你的池之前你仍然有空闲的工作人员。一旦你找到了一名优秀的工人,你就不必继续寻找,这样你就可以continue摆脱你的 for 循环。

public class JobQueue {
private int numWorkers;
private int[] jobs;
private int[] assignedWorker;
private long[] startTime;

    private void readData() throws IOException {
    numWorkers = in.nextInt();
    int m = in.nextInt();
    jobs = new int[m];
    for (int i = 0; i < m; ++i) {
        jobs[i] = in.nextInt();
    }
}

private void assignJobs() {
    assignedWorker = new int[jobs.length];
    startTime = new long[jobs.length];
    long[] nextFreeTime = new long[numWorkers];
    int duration = 0;
    int bestWorker = 0;
    for (int i = 0; i < jobs.length; i++) {
        duration = jobs[i];
        bestWorker = 0;
        if (i< numWorkers){
            bestWorker= i;
        } else{
            for (int j = 0; j < numWorkers; ++j) {
                if (nextFreeTime[j] < nextFreeTime[bestWorker])
                    bestWorker = j;
                    continue;
            }
        }
        assignedWorker[i] = bestWorker;
        startTime[i] = nextFreeTime[bestWorker];
        nextFreeTime[bestWorker] += duration;
    }
}

但是,您的解决方案和略微缩减的解决方案都需要 2 毫秒才能运行。我还研究了让 HashMap 来维护 NextWorker 标记,但在某些时候你赶上了它并最终每次都在寻找下一个并且不会赢得太多。

你可以尝试有一个有序的列表/队列,但是你有昂贵的插入而不是昂贵的搜索,你必须跟踪时间片。但是这样的版本可能如下所示:

private void assignJobs() {

 assignedWorker = new int[jobs.length];
 startTime = new long[jobs.length];
 PriorityQueue<Integer> nextTimesQueue = new PriorityQueue<Integer>();
 HashMap<Integer, Set<Integer>> workersReadyAtTimeT = new HashMap<Integer,Set<Integer>>();
 long[] nextFreeTime = new long[numWorkers];
 int duration = 0;
 int bestWorker = 0;
 for (int i = 0; i < jobs.length; i++) {
  duration = jobs[i];
  if(i<numWorkers) {
    bestWorker = i;
    nextTimesQueue.add(duration);
    addToSet(workersReadyAtTimeT, duration, i,0);
  }else {
    int currentTime = nextTimesQueue.poll();
    Set<Integer> workersReady = workersReadyAtTimeT.get(currentTime);
    if (workersReady.size()>1) { 
      bestWorker = workersReady.iterator().next();
      workersReady.remove(bestWorker);
      workersReadyAtTimeT.remove(currentTime);
      workersReadyAtTimeT.put(currentTime,workersReady);
      nextTimesQueue.add(currentTime);
    } else {
      bestWorker = workersReady.iterator().next();
      workersReadyAtTimeT.remove(currentTime);
      nextTimesQueue.add(currentTime+duration);
      addToSet(workersReadyAtTimeT, duration, bestWorker, currentTime);
    }
  }
  assignedWorker[i] = bestWorker;
  startTime[i] = nextFreeTime[bestWorker];
  nextFreeTime[bestWorker] += duration;
 }
}

private void addToSet(HashMap<Integer, Set<Integer>> workersReadyAtTimeT, int duration, int worker, int current) {
if(workersReadyAtTimeT.get(current+duration)==null) {
  HashSet<Integer> s = new HashSet<Integer>();
  s.add(worker);
  workersReadyAtTimeT.put(current+duration, s);
}else {
  Set<Integer> s = workersReadyAtTimeT.get(current+duration);
  s.add(worker);
  workersReadyAtTimeT.put(current+duration,s);
 }
}
于 2020-07-23T17:09:52.490 回答