减少此算法的时间复杂度

时间:2014-03-16 20:10:55

标签: performance algorithm time-complexity

我正在玩一款拥有武器锻造组件的游戏,你可以将两种武器结合起来获得新武器。武器组合的绝对数量(参见http://www.gamefaqs.com/ps/914326-vagrant-story/faqs/8485中的“6.1。刀片组合表”)使得通过重复锻造很难弄清楚你最终可以通过重复锻造产生什么,所以我尝试编写一个程序,这样做对我来说。我给它列出了我目前拥有的武器,例如:

  • 弗朗西斯
  • tabarzin
  • 克里斯

它给了我所有可以伪造的武器清单:

  • ball mace
  • chamkaq
  • 德克
  • 弗朗西斯
  • 大新月
  • 扔刀

问题是我使用的蛮力算法非常差;计算7种起始武器的所有可能武器需要大约15秒,而计算8种起始武器需要几分钟。我希望它能够计算多达64种武器(你可以同时拥有的最大数量),但我认为我没有足够长的时间来看到结果。

function find_possible_weapons(source_weapons)
{
    for (i in source_weapons)
    {
        for (j in source_weapons)
        {
            if (i != j)
            {
                result_weapon = combine_weapons(source_weapons[i], source_weapons[j]);

                new_weapons = array();
                new_weapons.add(result_weapon);
                for (k in source_weapons)
                {
                    if (k != i && k != j)
                        new_weapons.add(source_weapons[k]);
                }
                find_possible_weapons(new_weapons);
            }
        }
    }
}

英文:我从源头武器列表中尝试了两种武器的组合。对于这些组合中的每一个,我创建了一个新的列表,列出了我在该组合之后所拥有的所有武器(也就是说,新组合的武器加上除了我合并的两个以外的所有武器),然后我重复这些新列表的步骤。

有更好的方法吗?

请注意,以相反的顺序组合武器可以改变结果(剑杆+ Firangi =短剑,但是Firangi + Rapier = Spatha),所以我不能跳过j循环中的那些逆转。

编辑:以下是我上面给出的测试示例的细分,以显示算法正在做什么。括号中的一行显示组合的结果,以下行是作为结果创建的新武器列表:

francisca,tabarzin,kris
[francisca + tabarzin = chamkaq]
    chamkaq,kris
    [chamkaq + kris = large crescent]
        large crescent
    [kris + chamkaq = large crescent]
        large crescent
[francisca + kris = dirk]
    dirk,tabarzin
    [dirk + tabarzin = francisca]
        francisca
    [tabarzin + dirk = francisca]
        francisca
[tabarzin + francisca = chamkaq]
    chamkaq,kris
    [chamkaq + kris = large crescent]
        large crescent
    [kris + chamkaq = large crescent]
        large crescent
[tabarzin + kris = throwing knife]
    throwing knife,francisca
    [throwing knife + francisca = ball mace]
        ball mace
    [francisca + throwing knife = ball mace]
        ball mace
[kris + francisca = dirk]
    dirk,tabarzin
    [dirk + tabarzin = francisca]
        francisca
    [tabarzin + dirk = francisca]
        francisca
[kris + tabarzin = throwing knife]
    throwing knife,francisca
    [throwing knife + francisca = ball mace]
        ball mace
    [francisca + throwing knife = ball mace]
        ball mace

另请注意,武器列表中的重复项目很重要,无法删除。例如,如果我将第二个kris添加到我的起始武器列表中,以便我有以下列表:

  • 弗朗西斯
  • tabarzin
  • 克里斯
  • 克里斯

然后我可以伪造以下物品:

  • ball mace
  • 战斧
  • 战刀
  • chamkaq
  • 德克
  • 弗朗西斯
  • 克里斯
  • 库地
  • 大新月
  • scramasax
  • 扔刀

添加一个重复的kris让我可以伪造四件我以前无法完成的新物品。它还将锻造测试的总数增加到了四个项目列表的252个,而三个项目列表则为27个。

编辑:我觉得解决这个问题需要比我更多的数学和计算机科学知识,所以我会放弃它。起初这似乎是一个简单的问题,但是,旅行推销员也是如此。我接受David Eisenstat的答案,因为记住和跳过重复项目列表的建议在执行时间方面造成了如此巨大的差异,似乎它适用于很多类似的问题。

4 个答案:

答案 0 :(得分:11)

memoizing开始蛮力解决方案,即排序source_weapons,使其可以删除(例如,通过加入逗号转换为字符串),并在输入的地图中查找/输出对。如果不存在,则按正常方式进行计算并将结果添加到地图中。这通常会带来很大的成功。

或者,您可以进行向后搜索。鉴于多种武器,通过用一种可能的方式用两种武器替换其中一种武器来形成前体。从包含由目标武器组成的单例多重集构成的单例列表开始,通过列表元素的前任重复扩展列表,然后剔除作为其他人的超集的多重集。当你到达一个固定点时停止。


如果线性编程是一种选择,那么有一些系统的方法来修剪搜索树。特别是,通过(i)允许无限供应“催化剂”(这里可能不需要?)(ii)允许“分数”锻造,例如,如果X + Y = > Z,然后0.5 X + 0.5 Y => 0.5 Z.然后是LP配方如下。对于所有i + j => k(i和j forge k),变量x_{ijk}是执行此伪造的次数。

minimize sum_{i, j => k} x_{ijk} (to prevent wasteful cycles)
for all i:   sum_{j, k: j + k => i} x_{jki}
           - sum_{j, k: j + i => k} x_{jik}
           - sum_{j, k: i + j => k} x_{ijk} >= q_i,
for all i + j => k: x_{ijk} >= 0,

如果q_i是目标项,则1i,否则减去最初可用的i数。这个简单的版本有高效的解决方案。由于反应总是2 => 1,您始终可以为整数解决方案恢复可行的锻造计划。因此,我建议针对此问题进行整数编程。以下段落可能仍有意义。

我知道运送LP解算器可能不方便,所以这里有一个让你不用的洞察力。当且仅当其双重有界时,此LP是可行的。直观地说,双重问题是为每个项目分配一个“值”,这样,无论你伪造,你的库存总价值都不会增加。如果目标项的价值超过可用库存,那么您无法伪造它。您可以使用您可以想到的任何方法来分配这些值。

答案 1 :(得分:8)

我认为你不太可能对这个问题得到一个很好的答案,因为 如果有一个有效的算法来解决你的问题,那么它也能够解决NP完全问题。

例如,考虑在二进制矩阵中找到最大独立行数的问题 这是一个已知的NP完全问题(例如,通过显示maximum independent set problem的等价性)。

我们可以通过以下方式将此问题减少到您的问题:

我们可以开始为二进制矩阵中的每一列保留一个武器,然后我们想象每一行描述制作新武器的另一种方法(比如战斧)。 我们构建武器翻译表,使用方法i制作战斧,我们需要所有武器j,使得M [i,j]等于1(这可能涉及发明一些额外的武器)。

然后我们构建了一系列超级武器,可以通过组合不同数量的战斧来制造。

例如,巨型终极战斧可能需要组合4个战斗轴。

如果我们能够找出可以从你的起始武器构建的最好的武器,那么我们已经解决了在原始二进制矩阵中找到最大数量的独立行的问题。

答案 2 :(得分:4)

这不是一个巨大的节约,但是在查看源文件时,有时候组合武器会产生与组合武器相同的武器。我假设你不会这样做,因为你最终会减少武器。

因此,如果您添加了一个检查,看看result_weapon是否与其中一个输入的类型相同,并且没有继续并递归调用find_possible_weapons(new_weapons),那么您可以稍微调整搜索范围。

我能想到的另一件事是,你没有完成工作的跟踪,所以如果从find_possible_weapons(new_weapons)返回通过组合其他武器返回你已经获得的相同武器,你可能会表现得很好多次使用相同的搜索分支。

e.g。如果你有a,b,c,d,e,f,g,如果a + b = x,并且c + d = x,那么你的算法将执行两次比较x与e,f和g的比较。因此,如果你跟踪你已经计算过的东西,你就会成为赢家......

基本上,您必须修剪搜索树。有许多不同的技术可以做到这一点:it's called search。如果您需要更多建议,我建议您参加计算机科学堆栈交换。

如果你还在苦苦挣扎,那么你总是可以开始加权项目/结果项目,而只关注对“高增益”对象进行计算...

答案 3 :(得分:2)

您可能希望首先创建一个Weapon [] []矩阵,以显示伪造每对的结果。您可以将武器的名称映射到矩阵轴的索引,并且可以在恒定时间内查找武器组合的结果。