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

Algorithm 优化算术表达式序列的快速算法,algorithm,optimization,numbers,set,Algorithm,Optimization,Numbers,Set,编辑:澄清问题描述 有没有一种快速算法可以解决以下问题? 而且,也是针对这个问题的扩展版本 这将自然数替换为Z/(2^nz)?(我认为,这个问题太复杂,无法在一个地方添加更多的问题。) 问题: 对于给定的一组自然数,如{7,20,17,100},需要算法 返回加法、多应用程序和计算幂的最短序列 所有给定的数字。 序列的每一项都是(正确的)符合以下模式的方程式: <number> = <number> <op> <number> 我用Haskel

编辑:澄清问题描述

有没有一种快速算法可以解决以下问题? 而且,也是针对这个问题的扩展版本 这将自然数替换为Z/(2^nz)?(我认为,这个问题太复杂,无法在一个地方添加更多的问题。)

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

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

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

(,, )

我当前的(伪)代码:
——生成计算n所需的集合集。
--集合上的运算符(+)设置为并集。
所需数字0={{}
requiredNumbers 1={{}
所需数字=
{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}
--记住最小的一组“计算”数字
最佳集:={i | 1=|最佳集|)
--剪枝
返回
其他的
m:=最小值(从)
from':=deleteMin(from)
foreach(请求输入(所需数字m))
闭包(从“+(req-to))(到+{m})
--recoverEquation是一个函数,用于将数字集转换为方程集。
--这很容易做到。
输出=恢复方程(闭包输入{})
补充说明: 答案像

  • 没有快速算法,因为
  • 有一个启发式算法,它是
现在我觉得没有快速准确的算法


答案#1可以作为一种启发,我认为。

如果你从排序输入中的最高数字开始反向工作,检查是否/如何在其构造中使用较小的数字(以及正在引入的数字),会怎么样

例如,虽然这不能保证最短的序列

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

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

  • 对于每个数字,计算(并存储)运算的最短路径。从技术上讲,一步就足够了:对于每个数字,可以存储运算和两个操作数(左和右,因为幂运算是不可交换的),以及权重(“节点”
  • 最初,您以0的权重注册
    1
  • 每次注册一个新数字时,您必须使用该数字(所有加法、乘法、幂)和所有已注册的数字生成所有计算。(“边”
    • 计算过滤器:如果计算结果已经注册,则不应存储该值,因为有一种更容易获得该值的方法
    • 对于交换操作(1+2=2+1),仅存储1个操作
    • 预过滤电源操作,因为这甚至可能导致溢出
  • 必须将此列表排序为最短的求和路径(边的权重)。权重=(操作数1的权重)+(操作数2的权重)+(1,即操作的权重)
    • 您可以排除所有大于我们必须找到的最大数的结果数(例如,如果我们已经找到100,则可以排除大于20的任何结果数)-这可以改进,以便您也可以检查操作的成员
  • 如果您达到了一个目标数字,那么您找到了计算其中一个目标数字的最短方法,您必须重新开始生成:
    • 重新计算目标编号的最大值
    • 返回当前找到的数字的路径,将它们的权重设置为0(从现在起将给出它们,因为它们的成本已经支付)
    • 重新计算生成列表中操作的权重,因为源操作数权重可能已更改(这将导致在末尾重新排序)-此处可以排除任何一个操作数大于新最大值的操作数
  • 如果所有的数字都被点击,那么搜索就结束了
可以使用每个目标数的“反向链接”(操作,左操作数和右操作数)构建表达式

主要的一点是,我们始终关注目标函数,即操作的总数必须是尽可能最小的。为了实现这一点,我们总是计算到某个数的最短路径,然后考虑该数(以及路上的所有其他数)根据给定的数字,然后将搜索范围扩展到剩余的目标


理论上,该算法只对每个数字进行一次处理(注册)。应用适当的过滤器会减少不必要的分支,因此不会计算两次任何内容(队列中元素的权重除外)

是一个更好的提问地点。也许你可以利用这样一个事实,即你所有的运算都在增加,部分计算的数字序列也在增加。谢谢Basile。我应该在将问题发布到那里之前删除这篇文章,还是将其作为一个指针留在这里?也许使用Prolog,你可以获得更好的回溯结果。否则,我不知道我认为有一个算法解决方案。为什么你的输出也能解决不是你输入一部分的数字?你能详细说明需要做什么吗?@GaborSch,我同意,答案是“不”,我认为没有一个快速算法。无论你从“1”开始,搜索空间都会受到组合爆炸的影响或者从输入向后。似乎没有任何方法可以合理地减少搜索空间,例如,您如何为算法(如*搜索)提出合理的“距离”度量。不过,尝试解决一个更简单的问题会很有趣
-- 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 {})
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