Android Rubik的Cube Kociemba最佳求解器内存不足

时间:2014-05-11 22:10:48

标签: android memory-management rubiks-cube

我想就我遇到的以下问题寻求帮助。

我想创建一个使用最佳解决方案解决Rubik立方体的应用程序。我下载了以下库,假设使用Kociemba的算法就可以了。

http://kociemba.org/twophase.jar

显然它可以在0.5秒内解决多维数据集,但在我的应用程序中,由于内存问题,它从未返回解决方案。我知道它有效,我用错误的输入测​​试它,它返回记录的错误代码。

我在我的onCreate方法中这样称呼它:

resultTxt = Search.solution(data, 21, 10, true); 

resultTxt是一个String变量,它应该包含解决方案。

它会迅速吞噬记忆。

我尝试使用IntentService但没有成功。我的意思是它并没有真正改变任何东西。

由于我没有找到任何人在任何Android应用程序中使用此库的证据,我想我会问一个比我更有经验的人。

我有什么方法可以在Android上完成这项工作,或者这是不可能像我想的那样?

1 个答案:

答案 0 :(得分:1)

可能有点晚了,但是当我使用Android-Smartphone扫描立方体并计算解决方案时,我正在使用Rubik's-Cube解决机器人工作时也遇到了这个问题,所以我会把我发现的东西放在这里。


有什么问题?

让我们首先讨论导致性能问题的问题实际位于何处。

这么慢的原因是班级CoordCube,它看起来(非常简化)像这样:

class CoordCube {
    short[] pruneTables;

    static {
        /* compute and save ~50MB data in `pruneTables` */
    }
}

它的基本功能是将大量数据加载到快速求解过程所需的查找表中。首次实例化此类后,此JVM将自动执行此加载。这发生在line 159中的Search.solution()

/* simplified code of solution() */
if (cube.isValid()) {
    CoordCube c = new CoordCube(); // pruning tables will be loaded on this line

这也是为什么只要传递的多维数据集无效,此方法在可忽略的时间内执行的原因,因为它永远不会加载表。


可能的解决方案:

现在我们已经确定了问题的位置,让我们关注如何解决问题。

我提出了3种不同的方法,其中第一种方法可能是最简单的(但也是最慢的执行方式......),并且也在我的应用程序中使用。另外两个只是关于如何进一步提高性能的想法。

方法1:

第一个也是最简单的方法是只用LoadingActivity手动预加载查找表,其中ProgressBar显示我们当前的进度。为此,我们首先希望能够手动控制何时加载哪些表(当第一次实例化的类不够精确时),如下所示:

loadTable1() {
     /* load pruning table number 1 */
}

为此我写了一些简单的实用程序here(代码太长而无法粘贴)。请务必查看我的说明,了解如何在您的应用中正确导入该代码。

此外,我们可能希望在后台进行加载,即AsyncTask。这就是我在我的应用程序中完成它的方式(PruneTableLoader包含在上一个链接中):

private class LoadPruningTablesTask extends AsyncTask<Void, Void, Void> {
    private PruneTableLoader tableLoader = new PruneTableLoader();

    @Override
    protected Void doInBackground(Void... params) {
        /* load all tables if they are not already in RAM */
        while (!tableLoader.loadingFinished()) { // while tables are left to load
            tableLoader.loadNext(); // load next pruning table
            publishProgress(); // increment `ProgressBar` by one
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        /* increment `ProgressBar` by 1 */
    }
}

使用我的PruneTableLoader时,40s Samsung Galaxy S3上所有表的加载大约需要250 MB RAM free。 (相比之下,当自动加载它们时需要well over 2min,此外经常会导致崩溃......)

考虑到它在PC上需要< 1s,这听起来可能仍然很慢,但至少你必须这样做一次,因为Android会缓存静态变量而你不必在每次启动时都加载它们你的应用程序。

方法2 :(未经测试)

我认为将修剪表保存在filedatabase并从那里加载它们而不是总是重新计算它们会更快。我还没有测试过,它可能需要相当多的工作才能使保存和加载正常工作。 (也许因为访问时间而不是更快)

方法3 :(未经测试)

嗯,最简单的,也是几十年来最昂贵的解决方案,只需在CC++中重写整个算法,然后通过JNI在App中调用它。 ( Herbert Kociemba 尚未发表他的C-sourcecode据我所知......)

这肯定是性能最快的解决方案。 (也用于解决程序本身)


总而言之方法1 可能是开始时的努力/效益最佳方法(也适用于我),所以我建议你继续使用,以防加载对于您的应用程序来说,时间不是一个大问题。

我对自己的表现并不完全满意,所以我可能会在未来的某个时间尝试接近2 甚至接近3 。如果我这样做,我会用我的结果更新这篇文章。