我目前正在开始研究人工智能课程的最终项目(作为我的计算机科学学士学位的一部分)。在这个项目中,我们需要在人工智能领域选择一个有趣的问题,从课堂上扩展一个或多个主题,并解决它。我们稍后撰写一份报告讨论我们的结果,并提交报告和我们编写的代码。
显然,我们不希望在经典问题的研究中与现有技术水平相当,而是要检查和解决(在很大程度上)一个不寻常的问题(大多数选择这种方法的人选择解决一些问题)简单的计算机或棋盘游戏,人工智能研究界尚未解决死亡)或以某种新颖的方式检查更常见的问题,或许建议一种新的有趣的启发式或对现有算法的一些修改。在后一种情况下,我们不会超越现代研究成果,只能提供一些新观点。
我的合作伙伴和我为该项目选择的主题是Sokoban,它将我们置于第二组(它尚未被研究过死亡,因为只有三分之二的常见测试集可以由最佳解算器解决,但这个问题的最先进的解决方案似乎太复杂了,我们希望通过一个兼职,两周的项目来到他们附近的任何地方。我们想尝试使用搜索问题方法解决推箱子问题。
无论如何,在开始实现我们的推箱子解算器之前,我开始想知道我们熟悉的几种语言(C,C ++,Java和Python)中哪一种更适合用于实现基于搜索的求解器意味着在一个非常大的搜索空间上进行搜索(推箱子有一个非常深的搜索树,有一些问题需要300多个移动来解决,而且一个非常高的分支因子[在某些问题上超过100];请注意这个高分支因子当只考虑石头\盒子移动而不是玩家移动时实现,所以在每个状态下我们可以将任何一块石头移动到四个方向中的任何一个。)
我开始考虑这个问题的主要原因是因为在人工智能的另一个课程 - 处理将AI技术应用于产品设计 - 我创建了一个自动化的房间设计师,它将通过搜索所有的状态空间来设计房间可能的房间设计(具有给定的房间大小和一套家具)并返回具有最高分数的州(通过一些启发式测量)。该程序是用Java编写的,在搜索了数万个搜索节点后,每次运行都会耗尽内存。我认为发生这种情况的主要原因是因为我为该项目选择了一种非常面向对象的方法;它是用Java编写的,每个搜索状态都由一个对象表示,每个这样的状态,当由一个搜索器对象到达时,被一个搜索节点包裹 - 另一个对象 - 这当然意味着程序的内存很快被填充有很多物品,因此跑得很快。
现在,我知道部分问题是使用内存密集型算法(A *),以及我选择实现它的方式,但我想知道使用Java是否也存在问题。所以这引出了两个问题:
1.在实现搜索问题和搜索算法时,哪种编程方法更适合? (面向对象,功能或其他)
2.在实现搜索问题和搜索算法,Java,C,C ++或Python时,哪种编程语言更适合? (其他语言也是可能的,但前提是它们的语法与上述语言之一非常相似)
具体来说,这些语言的哪些特性和属性可以用来实现一个问题解决方案,用于在内存(和运行时)有效的方式搜索非常大的搜索空间?
答案 0 :(得分:1)
我认识一些人,他们都在做像你在Java中描述的内存密集型算法。你可以使它工作,但你必须诉诸primitive collections和数组。
话虽如此,我认为这不是很聪明。如果要编写高效的Java代码,基本上可以编写类似于C / C ++代码的代码。然后你也可以采取最后一步,直接编写C / C ++,有机会进一步优化(可能在速度和内存中获得另一个因子2)。
这让我想到你的问题:
- 一般来说,哪种编程方法更适合实现搜索问题和搜索算法? (面向对象,功能或其他)
醇>
功能程序通常看起来非常好,看起来很适合算法问题。问题是大多数情况下命令式算法更快(并且更难以编写而没有错误)。面向对象,我不知道,我认为它势在必行。对象层次结构引入了您通常不想要的计算开销(与它购买的内容相比)。
- 在实现搜索问题和搜索算法,Java,C,C ++或Python时,哪种编程语言更适合? (其他语言也是可能的,但只有当它们的语法与上述语言之一非常相似时)
醇>
我认为Python在功能语言方面简洁(它具有一流的功能),但对于任何严肃的事情来说都太慢了。在你提出的语言中,我可能会选择C ++,因为Java不是更好但可能更慢。一种可能性是使用像Scala这样的东西,它允许简洁的编程(与Python相当),速度接近或等于Java。
答案 1 :(得分:0)
我的意见:
Java:过于冗长/丑陋的语法,特别是对于数学事物。并且使用了太多的内存/ CPU资源。
C:绝对可怕,如果你习惯于面向对象的无指针语言
C ++:强大而快速,但过于繁重/慢速编译,无法使用多种算法进行实验。
Python:它们中最慢的一个。可能很适合创建原型,但不适合大型计算。
答案 2 :(得分:0)
如果您只使用大多数人熟悉的语言,我可能会推荐使用c ++。
如果你想分支到AI领域常见的语言,我觉得相对容易实现求解器,我会使用prolog。我知道你可以在网上找到启发式算法的例子。 Minimax和alpha-beta修剪很常见,我相信你知道。
此外,我使用的语言与prolog相关联,用于游戏,称为GDL。见:http://en.wikipedia.org/wiki/Game_Description_Language。它包含您可能在lisp语法中称为prolog谓词的内容。
答案 3 :(得分:0)
通过使用C ++结构,可能在Java的Objects上节省一些空间,但问题的根源是您当前的算法实现:看起来您的内存需求呈指数级增长
伪计算:假设您的n = 10 000
。因此,您需要100 000 000
个对象,这些对象会转换为1 Gb
RAM。假设通过从Java切换到C ++(并针对未知问题交换已知问题),可以将其降低到0.5 GB
RAM。太好了,除非你加倍你的n,你的内存分配翻两番......所以对于n = 20 000,你需要4 GB
Java RAM和2 GB
C ++ RAM ......
如果相反,你专注于更多内存有效的算法实现,你会发现你突然使用MB而不是GB的大n。 I've experienced this quite drastically by implementing Just In Time Selection (trading a List based architecture for an Iterator based architecture).
答案 4 :(得分:0)
空间需求通常是最难通过最佳优先搜索(BFS)来管理的,因为您实际上存储了整个搜索空间,并且它通常具有指数级别。
可以通过丢弃看起来没有希望(低分数)的部分搜索空间来管理空间,或者导致放弃搜索的完整性(当“足够好”的答案都可以)或者通过重新搜索如果抛出部分搜索需要重新审视。这种想法的极端版本被称为“深度优先搜索”(DFS),它只保留当前最有前途的分支。 DFS和BFS之间的折衷是所谓的beam search。这种空间权衡方案独立于编程语言;你把它们连接到搜索算法中。
一旦您意识到您可以并且必须有效地管理空间,现在您必须管理搜索时间。通常,最好的算法总是获胜,而且通常不是原始搜索。在AI世界中,你经常不知道“最佳算法”,因此采用暴力搜索。对于原始强力搜索,您需要最佳启发式来解决您可以获得的问题,这不是算法问题;这是你必须以某种方式获得的领域知识。大多数人工智能可以说是试图捕获和利用领域知识。算法和听觉知识都是与语言无关的。
最后,您可以使用并行语言来帮助管理搜索时间(尽管这只会给您带来充分的N加速常数因素)。为此,您需要一种能够按需大量廉价生成并行计算的语言。我们的许多现代语言(Java,C#)都有笨拙的内置“线程”结构和笨重的调用。 C和C ++没有这个内置但你可以使用“fork”库;不幸的是,这些使用起来更加笨拙并且经常有限制(例如,你不能拥有比你的操作系统为你提供的线程更多的平行粒度,最多限制为几千,或者因为big-stack assumption。在这种情况下,实际的方案是像工人一样对待你的线程,每个人都扩展一部分边界的单位。
另一种方案是有足够的谷物来配合你的问题结构;您基本上使用并行性来管理搜索空间中的节点。为此你必须解决大堆栈问题或者你的堆栈空间不足:-(我们做一些基于搜索的算法,用一种名为PARLANSE的专有语言来支持软件工程工具,这使得孩子们几乎可以轻易地分离.PARLANSE是类似lisp的;它有一个(|| ABC)内置并行算子,用于分支子计算ABC等,以及偏序并行和通用并行(spawn X)< / strong> to fork子计算X.所有这些运算符都很便宜;它们只需几百个周期来分叉并行工作;这是可能的,因为运算符内置于语言中,编译器可以理解它们。你可以看到一个depth first search coded using PARLANSE.,你必须努力寻找(|| 运算符但它的位置(提示:找到内部循环!)。PARLANSE并行性的一个非常有用的属性是中止计算及其子项的能力;这允许搜索空间中的“最佳答案”传播其结果并杀死其他搜索孩子。
IBM的X-10 parallel supercomputing language具有产生动态子项(完成构造)的设施,这似乎很有希望。