C# 级数计算

C# 级数计算,c#,.net,algorithm,math,C#,.net,Algorithm,Math,我有一些随机整数,比如 99 20 30 1 100 400 5 10 我必须从这些整数的任意组合中找到最接近(等于或大于但不小于)给定数字的和,如 183 做这件事的最快和准确的方法是什么?这是子集和问题的一种变体,也是类似于子集和的NP难问题 但如果涉及的数字很小,则存在伪多项式时间算法。退房: 好的,更多细节 以下问题: 给定一个整数数组,以及整数a,b,是否存在 其和位于 区间[a,b]是NP难的 这是因为我们可以通过选择a=b=0来求解子集和 现在这个问题很容易简化为你们的问题,

我有一些随机整数,比如

99 20 30 1 100 400 5 10
我必须从这些整数的任意组合中找到最接近(等于或大于但不小于)给定数字的和,如

183

做这件事的最快和准确的方法是什么?

这是子集和问题的一种变体,也是类似于子集和的NP难问题

但如果涉及的数字很小,则存在伪多项式时间算法。退房:

好的,更多细节

以下问题:

给定一个整数数组,以及整数a,b,是否存在 其和位于 区间[a,b]是NP难的

这是因为我们可以通过选择a=b=0来求解子集和

现在这个问题很容易简化为你们的问题,所以你们的问题也是NP难的

现在您可以使用上面wiki链接中提到的多项式时间近似算法

给定一个由N个整数组成的数组,一个目标S和一个近似阈值c

有一个多项式时间近似算法(涉及1/c),它告诉我们区间[(1-c)S,S]中是否有子集和


您可以反复使用它(通过某种形式的二进制搜索)来找到所需的最佳近似值。注意,您也可以在from[S,(1+c)S]的间隔上使用此选项,而背包只会为您提供一个解决方案这似乎是一个,其中整数的值为每个项目的“权重”,每个项目的“利润”为1,您正在寻找最少的物品数量,以精确地计算背包的最大允许重量。

一个简单的暴力方法是读入文本,将其解析为数字,然后遍历所有组合,直到找到所需的总和

一个更快的解决方案是对数字进行排序,然后。。。 把最大的数字加到你的总数上,是不是太大了?如果是这样的话,脱下它,试试下一个最小的。 如果总和太小,请将下一个最大的数字相加,然后重复。 继续添加数字,不要让总和超过目标值。当你击中目标时完成


请注意,回溯时,可能需要回溯多个级别。听起来是递归的好例子…

如果你的数字很小,你可以使用一种简单的技术。不要让这个名字吓到你。这种技术是可以理解的。基本上你把大问题分解成子问题

这里我们将问题定义为
can[number]
。如果可以从文件中的整数构造
number
,则
can[number]
true
,否则为
false
。很明显,
0
可以通过根本不使用任何数字来构造,因此
can[0]
true
。现在您尝试使用输入文件中的每个数字。我们试着看看总和
j
是否可以实现。如果一个
已经实现的和+当前数我们尝试==j
,那么
j
显然是可以实现的。如果要跟踪哪些数字构成了特定的总和,请使用另一个
prev
数组,该数组存储最后使用的数字来进行总和。有关此想法的实现,请参见下面的代码:

int UPPER_BOUND = number1 + number2 + ... + numbern //The largest number you can construct
bool can[UPPER_BOUND + 1]; //can[number] is true if number can be constructed
can[0] = true; //0 is achievable always by not using any number

int prev[UPPER_BOUND + 1]; //prev[number] is the last number used to achieve sum "number"
for (int i = 0; i < N; i++) //Try to use every number(numbers[i]) from the input file
{
   for (int j = UPPER_BOUND; j >= 1; j--) //Try to see if j is an achievable sum
   {
      if (can[j]) continue; //It is already an achieved sum, so go to the next j

      if (j - numbers[i] >= 0 && can[j - numbers[i]]) //If an (already achievable sum) + (numbers[i]) == j, then j is obviously achievable
      {
          can[j] = true;
          prev[j] = numbers[i]; //To achieve j we used numbers[i]
      } 
   }
}

int CLOSEST_SUM = -1;
for (int i = SUM; i <= UPPER_BOUND; i++)
   if (can[i])
   {
       //the closest number to SUM(larger than SUM) is i
       CLOSEST_SUM = i;
       break; 
   }

int currentSum = CLOSEST_SUM;    
do
{
    int usedNumber = prev[currentSum];
    Console.WriteLine(usedNumber);

    currentSum -= usedNumber;
} while (currentSum > 0);
int上界=number1+number2+…+numbern//可以构造的最大数
布尔can[上限+1]//如果可以构造数字,则can[number]为真
can[0]=true//0始终可以通过不使用任何数字来实现
int-prev[上界+1]//prev[number]是用于实现“number”之和的最后一个数字
for(int i=0;i=1;j--)//试着看看j是否是一个可实现的和
{
如果(can[j])continue;//这已经是一个已实现的和,那么转到下一个j
如果(j-numbers[i]>=0&&can[j-numbers[i]])//如果(已经可实现的和)+(numbers[i])==j,那么j显然是可实现的
{
can[j]=真;
prev[j]=数字[i];//为了得到j,我们使用数字[i]
} 
}
}
int最接近的_和=-1;
对于(int i=总和;i 0);

如果数字较大,可以将其转换为整数程序。使用
Mathematica
s解算器,它可能看起来像这样

nums = {99, 20, 30 , 1, 100, 400, 5, 10};
vars = a /@ Range@Length@nums;
Minimize[(vars.nums - 183)^2, vars, Integers]

您可以对值列表进行排序,找到第一个大于目标值的值,然后开始关注小于目标值的值。找到最接近目标值的和,然后将其与第一个大于目标值的值进行比较。如果最接近的和与目标之间的差值小于大于目标与目标的第一个值之间的差值,则您拥有最接近的和


有点古怪,但我认为逻辑是紧密联系在一起的。

对其他人来说,听起来很像家庭作业吗?@JOE:这意味着答案会有所不同。少给鱼,多教钓鱼。@Joe-Dude。寒冷这是家庭作业这一事实只是改变了OP得到的答案。在回答家庭作业问题之前,我们通常希望先看到一些尝试。即使这样,答案通常也能帮助OP找到正确的答案,但让他们自己找出最终的解决方案,而不是简单地给他们答案。@JOE:你的评论对吗?你的评论似乎很随意。为了记录在案,他不能把它标记为家庭作业,因为这个问题已经有五个标记了。但是,他已经说过这不是家庭作业。事实上,这个变体不是NP完全的。子集和是因为其解决方案易于检查(和=目标?通过:失败)。事实并非如此,因为解决方案无法在多项式时间内检查(因为你必须执行相同的问题解决,以查看是否能找到更好的答案。唯一不会出现这种情况的是当解决方案是目标时)@Moron:问题,如前所述,正在查找可用整数中小于目标值的最大和。在一般情况下,这意味着我们必须假设