我想就我遇到的以下问题寻求帮助。
我想创建一个使用最佳解决方案解决Rubik立方体的应用程序。我下载了以下库,假设使用Kociemba的算法就可以了。
http://kociemba.org/twophase.jar
显然它可以在0.5秒内解决多维数据集,但在我的应用程序中,由于内存问题,它从未返回解决方案。我知道它有效,我用错误的输入测试它,它返回记录的错误代码。
我在我的onCreate方法中这样称呼它:
resultTxt = Search.solution(data, 21, 10, true);
resultTxt是一个String变量,它应该包含解决方案。
它会迅速吞噬记忆。
我尝试使用IntentService但没有成功。我的意思是它并没有真正改变任何东西。
由于我没有找到任何人在任何Android应用程序中使用此库的证据,我想我会问一个比我更有经验的人。
我有什么方法可以在Android上完成这项工作,或者这是不可能像我想的那样?
答案 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 :(未经测试)
我认为将修剪表保存在file
或database
并从那里加载它们而不是总是重新计算它们会更快。我还没有测试过,它可能需要相当多的工作才能使保存和加载正常工作。 (也许因为访问时间而不是更快)
方法3 :(未经测试)
嗯,最简单的,也是几十年来最昂贵的解决方案,只需在C
或C++
中重写整个算法,然后通过JNI
在App中调用它。 ( Herbert Kociemba 尚未发表他的C-sourcecode
据我所知......)
这肯定是性能最快的解决方案。 (也用于解决程序本身)
总而言之方法1 可能是开始时的努力/效益最佳方法(也适用于我),所以我建议你继续使用,以防加载对于您的应用程序来说,时间不是一个大问题。
我对自己的表现并不完全满意,所以我可能会在未来的某个时间尝试接近2 甚至接近3 。如果我这样做,我会用我的结果更新这篇文章。