添加和减去一组数字以达到值

时间:2017-01-02 13:42:52

标签: c++ algorithm search optimization

从浮点数的向量

std::vector<float> v { 0.32, 0.0004, 12.78, -9.2, 1.1 };

我试图找出可以在每个浮点数前面放置的“+”和“ - ”系列,以获得尽可能接近值GOAL的结果。请注意,不能省略任何数字(必须使用所有值)。

float GOAL = 4.9;

对于这个例子,我认为最好的解决方案是

+ 0.32 - 0.0004 + 12.78 + (-9.2) + 1.1 = 4.9996

true表示“+”而false表示“ - ”,最佳解决方案可以表示为

std::vector<bool> solution { true, false, true, true, true }

可以迭代所有可能的组合。如果n的大小为v,则有2 ^ n种可能的组合。当n变大时,过程变得非常慢(2 ^1000≈10^ 301)。

我怎样才能编写一个搜索算法,在多项式时间内输出的效果不是最好但是下降的解决方案?

仅供参考,我对搜索算法只有基本的了解。我理解启发式算法,贪心算法,爬山,搜索树,极小极大游戏树等概念。

5 个答案:

答案 0 :(得分:2)

我只是给出了一个基本算法来实现这一目标。

1)计算可用浮点数的长度。 (我假设长度是固定的)。

2)有一个数组(长度为1)。全零。 3)然后尝试在浮点数之间执行操作。(零指负值)。 4)如果它与GOAL不匹配,则通过假设数组为二进制数来递增数字。 5)重复步骤3&amp; 4直到它与GOAL匹配。 6)即使最后如果不匹配,也不可能。

Ex:浮动矢量大小为5.然后所有可能的操作都是

步骤2:0000 - &gt; (第1 - 第2 - 第3 - 第4 - 第5)

步骤3:0001 - &gt; (第1 - 第2 - 第3 - 第4和第5)(增量二进制数)

步骤4 :((1st-2nd-3rd-4th + 5th)!= GOAL) - &gt;增加并调用Step3。所以,0010

它将通过所有可能性进行计算。

答案 1 :(得分:1)

不确定这是否符合您的多项式时间要求,但遗传算法往往在这种优化方面做得很好。

另外,作为一个实现细节,由于您要添加大量浮点数,您可能需要查看Kahan summation以最小化浮点错误。

答案 2 :(得分:1)

我没有看到优雅的解决方案,但......以下是基于递归函数(模板函数,因此您可以将其与doublelong double一起使用而无需更改)

#include <cmath>
#include <vector>
#include <iostream>

template <typename F>
F getSol (std::vector<F> const vals, F const & goal,
          std::vector<bool> & sol, std::size_t const & used,
          F const & sum)
 {
   F  ret;

   if ( used == vals.size() )
    {
      ret = sum;
    }
   else
    {
      std::vector<bool> sol1 { sol };
      std::vector<bool> sol2 { sol };

      sol1.push_back(true);
      sol2.push_back(false);

      F ret1 { getSol(vals, goal, sol1, used+1U, sum+vals[used]) };
      F ret2 { getSol(vals, goal, sol2, used+1U, sum-vals[used]) };

      if ( std::fabs(ret1 - goal) < std::fabs(ret2 - goal) )
       {
         ret = ret1;
         sol = std::move(sol1);
       }
      else
       {
         ret = ret2;
         sol = std::move(sol2);
       }
    }

   return ret;
 }


int main()
 {
   std::vector<float> v { 0.32, 0.0004, 12.78, -9.2, 1.1 };
   std::vector<bool>  solution;

   float goal { 4.9f };

   float res { getSol(v, goal, solution, 0U, 0.0f) };

   std::cout << "the result is " << res << std::endl;
   std::cout << "the solutions is ";

   for ( auto const & b : solution )
      std::cout << b << ", ";

   std::cout << std::endl;

 }

答案 3 :(得分:1)

我们可以考虑贪婪算法,它在 O(n)时间内提供下降解决方案。

算法

让数组和目标为:

vector<float> v { 0.32, 0.0004, 12.78, -9.2, 1.1 };
float GOAL = 4.9; 

现在开始迭代第一个索引中的向量,并贪婪地选择符号,即

If  "+" :
     diff = |Goal- ValueTillNow| = |4.9-0.32| = 4.58
If  "-" :
    diff = |Goal- ValueTillNow| = |4.9-(-0.32)| = 5.22

既然我们希望 ValueTillNow 与目标接近,我们会贪婪地选择&#34; +&#34;第一次漂浮。

现在类似于数组中的rest index。更新 ValueTillNow 。计算两个选项的差异,即"+""-",然后选择距离目标更接近GOAL的选项。

时间复杂度:O(n)

答案 4 :(得分:1)

对我来说看起来像integer linear programming问题。 我会把它分成两个线性整数程序,第一个用于超越GOAL,第二个用于进入GOAL。因此,为您提供以下两个程序,其中b_i = 0代表-,而b_i = 1代表{ans}中的+

结束,从而最大限度地减少:

min  Sum(v_i - 2 * b_i * v_i)
s.t. Sum(v_i - 2 * b_i * v_i) > GOAL
     b_i >= 0
     b_i <= 1
     b_i is an int

max  Sum(v_i - 2 * b_i * v_i)
s.t. Sum(v_i - 2 * b_i * v_i) < GOAL
     b_i >= 0
     b_i <= 1
     b_i is an int

然后应用通常的算法来解决两个LP并看到更好的解决方案。 如果让算法运行到最后,那么问题就是NP难。但是有些算法可以在有限的步骤之后提供合理的解决方案。