【问题标题】:Android Rubik's Cube Kociemba optimal solver memory shortageAndroid Rubik's Cube Kociemba 优化求解器内存不足
【发布时间】:2014-10-06 04:19:37
【问题描述】:

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

我想创建一个应用程序,用最佳解决方案解决魔方问题。我下载了以下库,据说它使用 Kociemba 的算法就是这样做的。

http://kociemba.org/twophase.jar

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

我在我的 onCreate 方法中这样调用它:

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

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

它很快就会耗尽内存。

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

由于我没有发现任何人在任何 android 应用程序中使用此库的任何证据,我想我会问比我更有经验的人。

有什么方法可以让这个在 Android 上运行,或者这有我想象的那么不可能吗?

【问题讨论】:

    标签: android memory-management rubiks-cube


    【解决方案1】:

    可能有点晚了,但是我最近在使用 Android 智能手机扫描魔方并计算解决方案的魔方求解机器人时也遇到了这个问题,所以我会把我发现的东西放在这里。


    有什么问题?

    让我们首先讨论导致该性能问题的问题的实际位置。

    之所以这么慢是因为 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 种不同的方法,其中第一种可能是最简单的(但也是最慢的执行方式......),并且也用于我的应用程序中。另外两个只是关于如何进一步提高性能的想法。

    方法一:

    第一种也是最简单的方法是手动以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 时,在我的Samsung Galaxy S3250 MB RAM free 上加载所有表需要大约40s。 (相比之下,自动加载它们时需要well over 2min,而且通常会导致崩溃......)

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

    方法 2:(未经测试)

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

    方法 3:(未经测试)

    嗯,最难也是几十年来工作成本最高的解决方案是,简单地在CC++ 中重写整个算法,然后通过JNI 在应用程序中调用它。 (Herbert Kociemba 据我所知,他的 C-sourcecode 还没有发布...)

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


    总而言之,方法 1 可能是一开始的最佳努力/收益最佳方法(对我来说也是如此),所以我建议你这样做,以防加载对于您的应用程序来说,时间并不是什么大问题。

    不过,我对自己的表现并不完全满意,所以我可能会在以后尝试方法 2,甚至可能尝试方法 3。如果我这样做了,我会用我的结果更新这篇文章。

    【讨论】:

    • 感谢您的详细解答。我确实尝试过整理桌子但收效甚微。但是,您的建议似乎在开发我的应用程序时对我有很大帮助。幸运的是,我找到了另一种在 android 上优化解决 3 x 3 立方体的方法。大约需要 2-3 秒,给予或接受,用大约 23 步来解决立方体。如果您在游戏中搜索 rubik's cube fridrich solver,您可以查看它。
    猜你喜欢
    • 2017-03-12
    • 2023-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    相关资源
    最近更新 更多