You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java回溯算法优化:Assignatura调度问题性能提升求助

Java回溯算法优化:Assignatura调度问题性能提升求助

看起来你已经在回溯里做了基础剪枝,但面对真实场景还是不够快,我结合你的代码和调度场景,给你几个针对性的优化方向,应该能显著提升性能:


1. 立即启用成本剪枝,砍掉无意义分支

你现在没有利用当前成本和最优成本的对比来剪枝——如果当前路径的成本已经超过了已知的最优解,继续递归下去毫无意义,直接终止即可。在backMillorSolucio方法开头加上:

// 剪枝:当前成本已经超过最优解,无需继续探索
if (cost_actual >= millor_cost) {
    return;
}

这一步能快速砍掉大量不可能成为最优解的分支,是性价比最高的优化之一。

2. 预计算成本矩阵,避免重复计算

你每次调用Assignatura.getPerjudicats()都要重新计算成本,这会带来额外的开销。可以在构造函数里预先把所有两两Assignatura的组合成本计算好,存到二维数组里,后续直接查表:

// 在类里添加成员变量
private int[][] perjudicatsMatrix;

// 在构造函数中初始化
public SolucioCalendari(int nDays, int nAssigInADay) {
    // ... 原有代码 ...
    int totalAssign = Assignatura.getPlaEstudis().length;
    perjudicatsMatrix = new int[totalAssign][totalAssign];
    for (int i = 0; i < totalAssign; i++) {
        for (int j = 0; j < totalAssign; j++) {
            perjudicatsMatrix[i][j] = Assignatura.getPerjudicats(i, j);
        }
    }
}

之后在计算成本时直接用perjudicatsMatrix[prevIdx][currIdx]代替原方法调用,节省重复计算的时间。

3. 优化空任务的分支逻辑,避免无效探索

你的约束是每天至少一个任务,但当前代码允许任意位置为空,这会产生很多无效分支(比如某一天两个位置都空)。可以针对每天的两个位置做分支限制:
当处理到每天的第一个位置(dia % 2 == 0)时,只考虑三种合法情况:

  • 只填第一个位置,第二个位置留空(需确保剩余任务能分配完)
  • 填第一个位置,然后处理第二个位置
  • 第一个位置留空,填第二个位置(同样要满足剩余任务分配条件)
    这样能直接砍掉“某天全空”的无效分支,减少递归次数。

4. 调整任务选择顺序,快速找到较优解

回溯算法的剪枝效率很大程度依赖于尽早找到一个较好的初始解。你可以调整任务的遍历顺序,优先尝试那些能带来更低成本的Assignatura:
比如先对Assignatura排序,优先选择单独放置时成本低、或与当前已选任务组合成本低的选项。这样能更快找到一个接近最优的解,后续的剪枝就能更早生效,砍掉更多分支。

5. 启用状态缓存(Memoization),避免重复探索相同状态

你已经声明了memoization变量但没用到,可以用它缓存已经探索过的状态,避免重复计算。这里的状态可以用当前处理到的位置(dia)+ 已分配任务的集合来表示:

  • 如果Assignatura数量不多(≤32),可以用一个整数(bitmask)来表示已分配的任务(每一位代表一个任务是否被分配)
  • 缓存的key可以是dia + "_" + bitmask,value是该状态下的最小成本
  • 在递归前检查:如果当前状态的成本已经高于缓存中记录的最小成本,直接终止递归;否则更新缓存。

比如可以把assignada数组换成一个int bitmask变量,每次分配任务i时,执行bitmask |= (1 << i),取消分配时执行bitmask &= ~(1 << i),这样生成状态key会更高效。

6. 优化数据结构,减少对象操作开销

当前solucio数组存的是Assignatura对象引用,你可以改成存任务的索引(int[] solucio),这样赋值、复制数组的开销会更小,后续需要访问任务属性时,再通过索引从Assignatura.getPlaEstudis()中获取。比如:

private int[] solucio;
private int[] millor_solucio;

// 构造函数中初始化
solucio = new int[nAssigInADay * nDays];
Arrays.fill(solucio, -1); // 用-1表示空位置

这样数组复制(Arrays.copyOf)会更快,对象操作的开销也会降低。


这些优化建议可以按优先级逐步实施:先做成本剪枝和预计算矩阵,这两步改动小见效快;再调整任务选择顺序和空分支逻辑;最后考虑状态缓存和数据结构优化。应该能让你的回溯算法在真实场景下的运行速度大幅提升。

备注:内容来源于stack exchange,提问作者Norhther

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 11:28:03