时间:2010-05-04 12:29:07

标签: c++ algorithm c++11

问候,

将std :: vector分成两个相等部分的方法的任何输入?我需要找到| part1 - part2 |。

之间可能存在的最小差异

这就是我现在正在做的事情,但是你可以告诉它在某些情况下会产生非最佳分割。

auto mid = std::find_if(prim, ultim, [&](double temp) -> bool
{
    if(tempsum >= sum)
        return true;

    tempsum += temp;
    sum -= temp;
    return false;
});

矢量被排序,从最高到最低,值确实可以出现两次。 我不期望part1和part2具有相同数量的元素,但sum(part1)应该尽可能接近sum(part2)

例如,如果我们有{2.4,0.12,1.26,0.51,0.70},最佳分割将是{2.4,0.12}和{1.26,0.51,0.70}。

如果有帮助,我正在尝试实现Shannon Fano编码的分裂算法。

也许这会帮助你们更好地理解我的问题http://en.wikipedia.org/wiki/Shannon%E2%80%93Fano_coding#Example

感谢任何意见,谢谢!

3 个答案:

答案 0 :(得分:3)

这是一个已知为NP-Complete的Partition problem,因此通常不存在多项式时间算法。然而,当集合中的元素的大小有界时,问题变得更容易。上面链接到维基百科有一个很好的部分近似算法(当你需要一个“足够好”的解决方案)。

答案 1 :(得分:2)

鉴于:

The vector is sorted, highest to lowest, values can indeed appear twice.
I'm not expecting part1 and part2 to have the same numbers of elements, but
sum(part1) should be as close as possible to sum(part2)

这不是最优的,但它会提供一个合理的近似值,例如你给出的值(除非我搞砸了......我实际上并没有编译或测试它)。如果原始矢量中有负数,它也可以工作:

std::pair<std::vector<double>, std::vector<double> >
    split(const std::vector<double>& data)
{
    std::pair<std::vector<double>, std::vector<double> > rv;
    double s1=0.0, s2=0.0;
    std::vector<double>::const_iterator i;

    for (i=data.begin(); i != data.end(); ++i)
    {
        double dif1 = abs(*i + s1 - s2);
        double dif2 = abs(*i + s2 - s1);

        if (dif1 < dif2)
        {
            rv.first.push_back(*i);
            s1 += *i;
        }
        else
        {
            rv.second.push_back(*i);
            s2 += *i;
        }
    }
    return rv;
}

编辑:如果向量中的负数总和超过列表中正数的总和,则此方法的结果质量会降低。要解决此问题,您可以尝试按降序绝对值而非严格降序排序原始列表。

答案 2 :(得分:0)

如果您想使用std算法和lambdas,可以执行以下操作

void splitProbabilityVector(std::vector<double>& data, std::vector<double>& rightHandSplit)
{
    double s1=0.0, s2=0.0;
    auto bound = std::stable_partition(data.begin(), data.end(), [&](double e) -> bool
    {
        if (abs(e + s1 - s2) < abs(e + s2 - s1))
        { s1 += e; return true;}
        else
        { s2 += e; return false; }
    });

    rightHandSplit.assign(bound, data.end());
    data.resize(bound-data.begin());
}

应该是非常高效的。出于好奇,为什么你在你链接的维基页面上使用这个算法时说明:

  

出于这个原因,Shannon-Fano是   几乎没用过;霍夫曼编码是   几乎与计算简单和   产生始终的前缀代码   实现最低预期代码字   长度。

相关问题