当前位置: 首页 > 知识库问答 >
问题:

给定一组正整数和值X,找到一个其和>=X的子集S,使得sum(S)是这类已有子集的所有和中的最低值

屠锦
2023-03-14

给定一组正整数和值X,找到一个其和>=X的子集S,使得sum(S)是这类已有子集所有和中的最低值。

共有1个答案

能远
2023-03-14

回溯是这个问题的一种可能。

它允许递归地检查所有可能性,而不需要大量内存。

一旦找到最优解,它就会停止:sum=x,直到给定的容差(例如下面程序中的10^-10)

复杂度(即迭代次数)确实随随机元素而变化(当然),但也很大程度上取决于我们接受的容忍度。在容差为10^-10和大小为n=100的情况下,复杂度通常是可以接受的。如果容差较小,情况就不再是这样了。

通过n=100和五次运行,我得到了迭代次数:6102,3672,8479,2235,12926。然而,很明显,并不保证在所有情况下都有良好的性能。对于n=100,候选(子集)的数量是巨大的。

//  Find min sum greater than a given number X

#include    <iostream>
#include    <iomanip>
#include    <vector>
#include    <algorithm>
#include    <tuple>
#include    <cstdlib>
#include    <cmath>
#include    <ctime>

std::tuple<double, std::vector<double>> min_sum_greater(std::vector<double> &a, double X) {
    int n = a.size();
    std::vector<bool> parti (n, false);     // current partition studies
    std::vector<bool> parti_opt (n, false);  // optimal partition
    std::vector<double> sum_back (n, 0);   // sum of remaining elements

    //std::cout << "n = " << n << " \tX = " << X << "\n";
    std::sort(a.begin(), a.end(), std::greater<double>());

    sum_back[n-1] = a[n-1];
    for (int i = n-2; i >= 0; --i) {
        sum_back[i] = sum_back[i+1] + a[i];
    }
    double sum = 0.0;     // current sum

    int i = 0;          // index of the element being examined
    double best_sum = sum_back[0] + 1.0;
    bool up_down = true;
    double eps = 1.0e-10;       // error tolerance
    long long cpt = 0;      // to check the number of iterations

    while (true) {          // UP
        //std::cout << "Start of while loop: i = " << i << "\n";
        cpt++;
        if (up_down) {
            bool abandon = (sum + sum_back[i] < X - eps) || (sum > best_sum);
            if (abandon) {  //premature abandon
                parti[i] = false;
                up_down = false;
                i--;
                continue;
            }

            parti[i] = true;
            sum += a[i];
            //std::cout << "UP, i = " << i << " \tsum = " << sum << "\n";

            if (fabs(sum - X) < eps) {
                best_sum = sum;
                parti_opt = parti;
                break;
            }

            if (sum >= X) {
                if (sum < best_sum) {
                    best_sum = sum;
                    parti_opt = parti;
                    //std::cout << "i = " << i << " \tbest sum = " << best_sum << "\n";
                }
                parti[i] = false;
                sum -= a[i];
            }

            if (i == (n-1)) {           // leaf
                up_down = false;
                i--;
                continue;
            }
            i++;        

        } else {            // DOWN
            if (i < 0) break;
            if (parti[i]) {
                sum -= a[i];
                parti[i] = false;
                i++;
                up_down = true;
            } else {
                i--;
                up_down = false;
            }
        }
    }
    std::vector<double> answer;
    for (int i = 0; i < n; ++i) {
        if (parti_opt[i]) answer.push_back (a[i]);
    }
    std::cout << "number of iterations = " << cpt << " for n = " << n << "\n";
    return std::make_tuple (best_sum, answer);
}

int main () {
    //std::vector<double> a = {5, 6, 2, 10, 2, 3, 4, 13, 17, 38, 42};
    double X = 33.5;

    srand (time(NULL));
    int n = 100;
    double vmax = 100;
    X = vmax * n / 4;
    std::vector<double> a (n);
    for (int i = 0; i < n; ++i) {
        a[i] = vmax * double(rand())/RAND_MAX;
    }

    double sum;
    std::vector<double> y;
    std::tie (sum, y) = min_sum_greater (a, X);

    std::cout << std::setprecision(15) << "sum = " << sum << "\n";

    if (n < 20) {
        std::cout << "set: ";
        for (auto val: y) {
            std::cout << val << " ";
        }
        std::cout << "\n";
    }

}
 类似资料:
  • 描述一个算法,其中给定一组由n个整数和另一个整数x组成的S,确定S中是否存在两个元素,其和正好是x。请告诉我我的算法是否正确,或者我需要做什么修改?算法:

  • 我的问题是,我需要从这个布尔数组中提取和等于指定和的实际子集。我尝试过这样做,但是我的方法中的逻辑(涉及到使用布尔数组来减少我必须分析的子集的数量)变得相当复杂,并且我很难实现它。 有没有人知道当使用生成的布尔数组时,找到其和等于给定和的所有子集的好方法? 编辑%1 输入 输出

  • 我知道这是一个背包问题,其中权重和值相等,但我认为我在编码逻辑上犯了一个错误,因为即使对于数组中元素的数量(N)为50并且所需的最大总和(M)4500。 为了澄清这个问题,我们得到了一个由N个正整数和一个正整数M组成的数组。数组元素只能使用一次。我们必须找到这个数组的子集(不一定是连续的),使得总和最接近M,但不超过它。 这是我使用动态编程的尝试: 那么在我的代码中是否有任何可能的优化可以帮助我降

  • 我只能想到一个朴素的算法,它列出集合的所有子集,并检查子集的和是否和是否最小,但它是一个指数算法,列出所有子集需要O(2^n)。我能用动态规划在多项式时间内求解吗?

  • 可能的子集:- 我只能想到一个朴素的算法,它列出集合的所有子集,并检查子集和是否>=k,但它是一个指数算法,列出所有子集需要O(2^n)。我能用动态规划在多项式时间内求解吗?

  • 我有个算法问题。我试图从一个更大的值集合中找到所有唯一的值子集。 例如,假设我有集。我能用什么算法找到3的这些子集? 子集不应重复,且顺序不重要,因此集{1,2,3}与集{3,2,1}相同。鼓励使用Psudocode(或常规类型)。