首先,我要说这不是家庭作业(我是一名A-Level学生,这与我们解决的问题没什么关系(这是方式更难)),但更多我正试图改进我的编程逻辑。
我想到了一个有一个随机整数数组的场景,比如说10个整数。用户将输入他想要计数的数字,算法将尝试计算出该总和所需的数字。例如,如果我想从这个整数数组中得到总和44:
myIntegers = array(1, 5, 9, 3, 7, 12, 36, 22, 19, 63);
输出结果为:
36 + 3 + 5 = 44
或者那些东西。我希望我能说清楚。作为一个额外的好处,我想让算法选择尽可能少的数字来得到所需的总和,或者如果不能用所提供的数字进行求和则给出错误。
我考虑过使用递归并遍历数组,反复添加数字,直到达到或超过总和。但是我无法理解的是,如果算法超过总和并且需要选择从数组中选择的数字,该怎么办。
我不是在寻找完整的代码,也不是一个完整的算法,我只是希望你对我应该如何处理这个问题提出意见,或许可以分享一些提示或其他内容。我今晚可能会开始研究这个问题。 :P
正如我所说,不是家庭作业。只是我想要做一些更先进的事情。
感谢您提供的任何帮助。 :)
答案 0 :(得分:31)
您正在查看Knapsack Problem
背包问题或背包问题是组合优化中的一个问题:给定一组具有权重和值的项目,确定要包含在集合中的每个项目的数量,以使总重量小于a给定限制,总值尽可能大。它的名字来源于受到固定尺寸背包约束的人所面临的问题,并且必须填写最有用的物品。
编辑:您的特例是Subset Sum Problem
答案 1 :(得分:10)
请subset sum做什么? ]
答案 2 :(得分:4)
这是你在大学水平算法课程中看到的经典Knapsack问题(或者至少我看过它)。最好在纸上解决这个问题,代码中的解决方案应该相对容易解决。
编辑:要考虑的一件事是dynamic programming。
答案 3 :(得分:2)
您的问题与subset sum problem有关。 在最坏的情况下,你必须尝试所有可能的组合。
答案 4 :(得分:2)
这里没有捷径我害怕。除了其他人所说的,关于这是什么具体问题等,这里有一些实用的建议为你提供一个起点:
我会对数组进行排序并给出输入总和m
,会发现数组中的第一个数字小于m
,将其称为n
(这是您的第一个可能数字总和),从最高可能的补充(m-n
)开始,一路向下。
如果找不到精确匹配,请选择最高可用匹配,将其称为 o
,(现在是您的第二个号码)并从(m-n-o
开始寻找第三个号码并且再次努力。
如果找不到精确匹配,请从下一个数字n(索引为1的原始n的索引)开始并执行相同操作。您可以继续这样做,直到找到两个数字的精确匹配。如果找不到两个数字的总和匹配,请再次启动该过程,但将其展开以包含第三个数字。等等。
这可以递归地完成。至少这种方法可以确保当您找到匹配时,它将是集合中编号最小的数字形成总输入和。
但是,最糟糕的情况是,你最终会经历整个过程。
编辑:正如Venr正确指出的那样,我的第一种方法是错误的。编辑的方法来反映这一点。
答案 5 :(得分:2)
这个问题有一个非常有效的随机算法。我知道你已经接受了答案,但无论如何我很乐意分享,我只希望人们仍然可以查看这个问题:)。
Let Used = list of numbers that you sum.
Let Unused = list of numbers that you DON'T sum.
Let tmpsum = 0.
Let S = desired sum you want to reach.
for ( each number x you read )
toss a coin:
if it's heads and tmpsum < S
add x to Used
else
add x to Unused
while ( tmpsum != S )
if tmpsum < S
MOVE one random number from Unused to Used
else
MOVE one random number from Used to Unused
print the Used list, containing the numbers you need to add to get S
这将比动态编程解决方案快得多,特别是对于随机输入。唯一的问题是你无法可靠地检测到何时没有解决方案(你可以让算法运行几秒钟,如果它没有完成,假设没有解决方案)并且你不能确定你会得到解决方案选择的元素数量最少。同样,您可以添加一些逻辑以使算法继续运行并尝试找到具有较少元素的解决方案,直到满足某些停止条件,但这会使其变慢。但是,如果您只对一个有效的解决方案感兴趣并且您有很多数字并且所需的总和可能非常大,那么这可能比DP算法更好。
这种方法的另一个优点是它也可以用于负数和有理数而不需要修改,这对DP解决方案来说是不正确的,因为DP解决方案涉及使用部分和作为数组索引,索引只能是自然的数字。当然,您可以使用哈希表,但这会使DP解决方案更慢。
答案 6 :(得分:1)
我不确切地知道这个任务被调用了什么,但它似乎有点http://en.wikipedia.org/wiki/Knapsack_problem。
答案 7 :(得分:1)
为了完整性,您需要跟踪每个总和中的加数数量,当然还要通过跟踪您使用的第一个数字,跳过该数字并使用其他数字重复该过程来生成其他序列。这将解决(7 + 2 + 1)超过(6 + 4)问题。
答案 8 :(得分:1)
重复别人的回答:这是一个子集总和问题。 它可以通过动态编程技术有效地解决。
以下内容尚未提及:问题是Pseudo-P(或弱意义上的NP-Complete)。
S中存在一种算法(基于动态规划)多项式(其中S是和)和n(元素数量)证明了这种说法。
问候。
答案 9 :(得分:0)
好的,我写了一个C ++程序来解决上述问题。算法很简单: - )
首先按降序排列你所拥有的数组(我已经以降序形式对数组进行了硬编码,但你可以应用任何排序算法)。
接下来我拿了三个堆栈n,pos和sum。第一个存储可以找到可能总和组合的数字,第二个存储从哪里开始搜索的数组的索引,第三个存储添加的元素将为您输入的数字。
该函数查找数组中最大数字,该数字小于或等于输入的数字。如果它相等,它只是将数字推到总和栈上。如果没有,则将遇到的数组元素推送到sum栈(暂时),找到要搜索的数字和遇到的数字之间的差异,然后执行递归。
让我举个例子: - 在{63,36,22,19,12,9,7,5,3,1}
中找到44前36个将被推入(最大数量小于44) 将推入44-36 = 8(下一个要搜索的数字) 7将被推入总和 8-7 = 1将被推入n 1将被推入总和
因此44 = 36 + 7 + 1: - )
#include <iostream>
#include<conio.h>
using namespace std;
int found=0;
void func(int n[],int pos[],int sum[],int arr[],int &topN,int &topP,int &topS)
{
int i=pos[topP],temp;
while(i<=9)
{
if(arr[i]<=n[topN])
{
pos[topP]=i;
topS++;
sum[topS]=arr[i];
temp=n[topN]-arr[i];
if(temp==0)
{
found=1;
break;
}
topN++;
n[topN]=temp;
temp=pos[topP]+1;
topP++;
pos[topP]=temp;
break;
}
i++;
}
if(i==10)
{
topP=topP-1;
topN=topN-1;
pos[topP]+=1;
topS=topS-1;
if(topP!=-1)
func(n,pos,sum,arr,topN,topP,topS);
}
else if(found!=1)
func(n,pos,sum,arr,topN,topP,topS);
}
main()
{
int x,n[100],pos[100],sum[100],arr[10]={63,36,22,19,12,9,7,5,3,1},topN=-1,topP=-1,topS=-1;
cout<<"Enter a number: ";
cin>>x;
topN=topN+1;
n[topN]=x;
topP=topP+1;
pos[topP]=0;
func(n,pos,sum,arr,topN,topP,topS);
if(found==0)
cout<<"Not found any combination";
else{
cout<<"\n"<<sum[0];
for(int i=1;i<=topS;i++)
cout<<" + "<<sum[i];
}
getch();
}
您可以复制代码并将其粘贴到IDE中,工作正常: - )