确定是否有任何从设定总和到目标值的双精度组合

时间:2018-06-14 00:43:13

标签: algorithm set

我在工作中有一个问题,让我有点难过。我需要验证给定剂量的药物可以从药丸剂量大小的任何组合构建。

例如

dose = 400.0
sizes= [15.0, 30.0, 45.0]

400不能通过这些值的任何总和来创建(至少我认为是真的)。但是,如果变量更改为:

dose = 400.0 
sizes = [10.0, 15.0, 30.0]

我希望结果是真的,因为10x40 = 400.或者如果这是senario:

dose = 405.0
sizes = [2.5, 15.0, 30.0, 100.0]

我也期望得到真正的结果,因为4x100 + 2X2.5 = 405.

您将如何编写此算法?它似乎与Subset Sum算法有关,但在我的情况下,我想允许任何设置项的多次出现成为解决方案的一部分。

2 个答案:

答案 0 :(得分:2)

执行此操作的一种标准方法是:

  1. 将所有双打表示为有理,因此[2.5, 15.0, 30.0, 100.0]变为[5/2, 15, 30, 100]
  2. 乘以分母的最小公倍数来获得整数尺寸:[5, 30, 60, 200]且剂量为810
  3. 检查您所需的剂量是大小的GCF的倍数:是的,8105的倍数(如果没有,那么显然无法制作)
  4. 除以GCF的大小:[1, 6, 12, 40],剂量为162
  5. 将标准动态编程解决方案应用于“coin change problem”。网上有很多资源,例如this question

答案 1 :(得分:2)

以下Java实现解决了双重值的问题。

注意 - 处理基于double / float的算术时的Java is known for its inaccuracy。但是,对于较低的精度,此解决方案应该足够了。当然,这种算法可以用不受精度问题影响的编码语言实现,例如C ++。 使用容差阈值检查更新了算法。这意味着现在也可以处理更精确的精度。感谢aschepler指出了一个有问题的精确用例。

使用coin change problem实现了两种算法(基于经典an online java compiler):

  • 只返回一个布尔值,指示是否存在提供给定总和的子集
  • 第一个算法的扩展,其中列出了从子集
  • 中使用的值

代码如下:

import java.util.*;

    public class MyClass {
        public static void main(String args[]) {

        double set[] = {2.5, 15.0, 30.0, 100.0};
        double sum = 405.0;
        int n = set.length;
        if (count(set, n, sum))
            System.out.println("Found a subset with given sum");
        else
            System.out.println("No subset with given sum");

        List<Double> listing = new ArrayList<Double>();
        if (countList(set, n, sum,listing))
            System.out.println("Found a subset with given sum: " + listing);
        else
            System.out.println("No subset with  given sum");
    }

    public static boolean count( double S[], int m, double n)
    {
        // If n is near 0 then there is 1 solution 
        // (do not include any coin)
        if (n >= -0.00001 && n <= 0.00001)
            return true;

        // If n is less than 0 then no 
        // solution exists
        if (n < 0)
            return false;

        // If there are no coins and n 
        // is greater than 0, then no
        // solution exist
        if (m <=0 && n > 0)
            return false;

        // count is true if one of the solutions (i) including S[m-1] (ii) excluding S[m-1] is true
        return count( S, m - 1, n ) ||
               count( S, m, n-S[m-1] );
    }
    public static boolean countList( double S[], int m, double n, List<Double> listing)
    {
        // If n is near 0 then there is 1 solution 
        // (do not include any coin)
        if (n >= -0.00001 && n <= 0.00001)
            return true;

        // If n is less than 0 then no 
        // solution exists
        if (n < 0)
            return false;

        // If there are no coins and n 
        // is greater than 0, then no
        // solution exist
        if (m <=0 && n > 0)
            return false;

        // count is true if one of the solutions (i) including S[m-1] (ii) excluding S[m-1] is true
        List<Double> with = new ArrayList<>();
        with.add(S[m-1]);
        List<Double> without = new ArrayList<>();
        boolean withResult = countList( S, m, n-S[m-1], with );
        boolean withoutResult = countList( S, m - 1, n, without );
        if(withResult) {
            listing.addAll(with);
        }
        else if (withoutResult) {
            listing.addAll(without);
        }
        return withResult || withoutResult;
    }
}

输出:

Found a subset with given sum
Found a subset with given sum: [100.0, 100.0, 100.0, 100.0, 2.5, 2.5]

这是一个更具挑战性的输入:

double set[] = {2.5, 15.0, 30.0, 100.0, 0.2, 0.3};
double sum = 165.9;
Found a subset with given sum
Found a subset with given sum: [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 100.0, 2.5, 2.5, 2.5, 2.5, 2.5]

还有:

double set[] = {0.2, 0.3, 2.5, 15.0, 30.0, 100.0};
double sum = 148.6;
Found a subset with given sum
Found a subset with given sum: [100.0, 30.0, 15.0, 2.5, 0.3, 0.3, 0.3, 0.2]

遵循精确修复:

double set[] = {0.05, 0.012, 0.008};
double sum = 0.1;
Found a subset with given sum
Found a subset with given sum: [0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.008, 0.012]

参考文献: