模拟退火代码的OutOfMemory问题

时间:2014-01-07 15:30:09

标签: java eclipse out-of-memory simulated-annealing

我正在使用this code模拟退火算法来解决旅行商问题。城市数量相对较少,即约30-40。问题是在第1000次迭代时我收到OutOfMemory错误消息(在功能“GO”中)。为什么会这样?如何解决这个问题?

package tsptw.logic;

import java.util.ArrayList;
import java.util.Random;

import tsptw.logic.objects.Point;
import tsptw.logic.objects.Solution;

public class SimulatedAnnealing {

    private static final double COOLING_ALPHA = 0.95;
    private static Random rand = new Random();
    private static int i = 0;

    public static Solution solve(Point start, ArrayList<Point> points) {
        Solution sol = Solution.randomSolution(start, points);
        double t = initialTemperature(sol, 1000);
        int frozen = 0;
        System.out.println("-- Simulated annealing started with initial temperature " +
                t + " --");
        return go(sol, t, frozen);
    }

    private static Solution go(Solution solution, double t, int frozen) {
        if (frozen >= 3) {
            return solution;
        }
        i++;

        Solution bestSol = solution;
        System.out.println(i + ": " + solution.fitness() + " " + solution.time() + " "
                + solution.penalty() + " " + t);
        ArrayList<Solution> nHood = solution.nHood();

        int attempts = 0;
        int accepted = 0;

        while (!(attempts == 2 * nHood.size() || accepted == nHood.size())) {
            Solution sol = nHood.get(rand.nextInt(nHood.size()));
            attempts++;

            double deltaF = sol.fitness() - bestSol.fitness();
            if (deltaF < 0 || Math.exp(-deltaF / t) > Math.random()) {
                accepted++;
                bestSol = sol;
                nHood = sol.nHood();
            }
        }

        frozen = accepted == 0 ? frozen + 1 : 0;

        double newT = coolingSchedule(t);
        return go(bestSol, newT, frozen);
    }

    private static double initialTemperature(Solution solution, double t) {
        ArrayList<Solution> nHood = solution.nHood();
        int accepted = 0;
        int attempts = nHood.size();
        for (Solution sol : nHood) {
            double deltaF = sol.fitness() - solution.fitness();
            if (deltaF < 0 || Math.exp(-deltaF / t) > Math.random()) {
                accepted++;
            }
        }
        double r = ((double)accepted) / attempts;

        if (r >= 0.94 && r <= 0.96) {
            return t;
        }
        return initialTemperature(solution, r > 0.95 ? t/2 : 2*t);
    }

    private static double coolingSchedule(double t) {
        return COOLING_ALPHA * t;
    }
}

6 个答案:

答案 0 :(得分:1)

看起来递归正在产生一个大堆。如果你的代码是正确的并且你想保持递归,你可能会尝试清理一下。我的第一个候选人是nHood.clear()。使用分析器识别您的记忆中心。

增加VM的堆大小应该是最后的选择。我宁愿建议将递归重构为循环。

答案 1 :(得分:1)

除了增加JVM的堆大小之外,有一点可能有用(可能很多次,当你多次进入函数时)是移出变量的创建,例如{{1} },nHoodsol在函数本身之外。每次进入函数时创建新对象比为以前创建的对象分配新值需要更多的时间和空间。使用递归函数,在到达最终函数调用之前,通常在中间步骤中创建的所有变量将保留在内存中,因此即使创建变量bestSolattempts类变量,也只需重新设置他们在那个地方的价值观,而不是创造新的价值观,会有所帮助。

答案 2 :(得分:1)

这看起来很糟糕:

ArrayList<Solution> nHood = solution.nHood();
// How many solutions do you have in memory?
// How much memory does 1 solution take?

在实施模拟退火时,请查看 just in time random selection 。特别是当每个街区的移动次数爆炸时(随着城市数量的增加),这也可以防止你的记忆爆炸。

答案 3 :(得分:0)

运行复杂算法时,这是一个常见问题。尝试增加堆内存大小,请参阅:Increasing Java heap size in Eclipse - using virtual memory

答案 4 :(得分:0)

在Eclipse中,转到程序的“运行配置”:

运行&gt; RunConfiguration ...&gt;参数&gt; VM参数:然后键入行:

-Xmx500m

这将配置虚拟机以允许最多500M的内存。希望那会是 足够

P.S。 - 除了处理每个城市的大量数据之外,您不应该遇到这个问题。

答案 5 :(得分:0)

您是否尝试增加最大堆大小? 有些环境在默认情况下将其设置得非常有限(Matlab)。

您可以使用参数-Xmx4g将限制设置为4GB的堆内存。

Increase heap size in Java http://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html