快速算法优化算术表达式序列

时间:2013-04-21 06:45:08

标签: algorithm optimization numbers set

编辑:澄清问题描述

是否有快速算法解决以下问题? 而且,也是针对这个问题的extendend版本 将自然数替换为Z /(2 ^ n Z)?(这个问题太复杂了,无法在一个地方添加更多问题,IMO。)

问题:

对于一组给定的自然数,如{7,20,17,100},需要算法 返回最短序列的加法,多重和幂计算 所有给定的数字。 每个序列项都是(正确的)等式,符合以下模式:

<number> = <number> <op> <number>

其中&lt; number&gt;是一个自然数,&lt; op&gt;是{+,*,^}之一。

在序列中,&lt; op&gt;的每个操作数。

应该是其中之一
  • 1
  • 已经出现在相等左侧的数字。

实施例

Input: {7, 20, 17, 100}
Output:
2 = 1 + 1
3 = 1 + 2
6 = 2 * 3
7 = 1 + 6
10 = 3 + 7
17 = 7 + 10
20 = 2 * 10
100 = 10 ^ 2

我在Haskell中写了回溯算法。 它适用于像上面这样的小输入,但我的真实查询是 在[0,255]中随机分布~30个数字。 对于真正的查询,以下代码在我的电脑中需要2~10分钟。

Actual code,  very simple test

我当前的(伪)代码:

-- generate set of sets required to compute n.
-- operater (+) on set is set union.
requiredNumbers 0 = { {} }
requiredNumbers 1 = { {} }
requiredNumbers n =
      { {j, k} | j^k == n, j >= 2, k >= 2 }
    + { {j, k} | j*k == n, j >= 2, k >= 2 }
    + { {j, k} | j+k == n, j >= 1, k >= 1 }

-- remember the smallest set of "computed" number
bestSet := {i | 1 <= i <= largeNumber}

-- backtracking algorithm
-- from: input
-- to:   accumulator of "already computed" number
closure from to =
    if (from is empty)
        if (|bestSet| > |to|)
            bestSet := to
            return
    else if (|from| + |to| >= |bestSet|)
        -- cut branch
        return
    else
        m := min(from)
        from' := deleteMin(from)
        foreach (req in (requiredNumbers m))
            closure (from' + (req - to)) (to + {m}) 

-- recoverEquation is a function converts set of number to set of equation.
-- it can be done easily.
output = recoverEquation (closure input {})

附加说明:

这样的答案
  • 没有快速算法,因为......
  • 有一个启发式算法,它是......

也受到欢迎。现在我觉得没有快速准确的算法......

我认为答案#1可以用作启发式。

2 个答案:

答案 0 :(得分:2)

如果您从排序输入中的最高数字向后工作,检查是否/如何在其构造中使用较小的数字(以及正在引入的数字),该怎么办?

例如,虽然这可能无法保证最短的序列......

input: {7, 20, 17, 100}

(100) = (20) * 5 => 
(7) = 5 + 2      => 
(17) = 10 + (7)  =>
(20) = 10 * 2    =>
10 = 5 * 2       =>
5 = 3 + 2        =>
3 = 2 + 1        =>
2 = 1 + 1

答案 1 :(得分:2)

我建议将其转换为某种图形最短路径算法。

  • 对于每个号码,您计算(并存储)最短操作路径。从技术上讲,一步就够了:对于每个数字,你可以存储操作和两个操作数(左和右,因为电源操作不可交换),还有重量(&#34;节点&#34;
  • 最初注册1的权重为零
  • 每次注册新号码时,都必须使用所有已注册的号码生成所有带有该号码的计算(所有加法,乘法,幂)。 (的&#34;边缘&#34;
    • 过滤计算:计算结果已经注册,您不应该存储,因为有更简单的方法可以获得该数字
    • 仅存储1个可交换的操作(1 + 2 = 2 + 1)
    • 预先过滤电源操作,因为这甚至可能导致溢出
  • 您必须将此列表排序为最短和路径(边缘权重)。重量=(操作数1的重量)+(操作数2的重量)+(1,操作的重量)
    • 您可以排除所有结果数字,这些数字大于我们必须找到的最大数字(例如,如果我们已经找到100,可以排除20以上的任何数字) - 这可以进行细化,以便您可以检查成员这些行动也是如此。
  • 如果您点击了一个目标号码,那么您找到了计算目标号码的一个的最短路径,您必须重新启动几代:
    • 重新计算目标数字的最大值
    • 返回当前找到的号码的路径,将其权重设置为0(从现在开始 ,因为已经支付了费用)
    • 重新计算生成列表中操作的权重,因为源操作数权重可能已更改(这会导致最后重新排序) - 此处可以排除操作数大于新最大值的那些
  • 如果所有数字都被点击,则搜索结束

您可以使用&#34;反向链接&#34;来构建表达式。 (每个目标数字的操作,左右操作数)。

重点是我们始终关注目标功能,即操作总数必须尽可能。为了得到这个,我们总是计算到一定数量的最短路径,然后将该数字(以及途中的所有其他数字)视为给定数字,然后将搜索扩展到剩余目标。

理论上,该算法只处理(登记)每个数字一次。应用适当的过滤器会削减不必要的分支,因此不会计算任何内容(除了队列内元素的权重)