Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 分为两组的硬币零钱_Algorithm_Dynamic Programming_Combinatorics - Fatal编程技术网

Algorithm 分为两组的硬币零钱

Algorithm 分为两组的硬币零钱,algorithm,dynamic-programming,combinatorics,Algorithm,Dynamic Programming,Combinatorics,我试图找出如何解决一个问题,这似乎是一个常见算法问题的棘手变化,但需要额外的逻辑来处理特定的需求 给定一个硬币列表和一个数量,我需要计算使用无限数量的可用硬币提取给定数量的可能方法的总数(这是一个使用动态规划很容易解决的经典变革问题),同时也满足一些附加要求: 提取的硬币可分为两套大小相等的硬币(但不一定是等额的) 集合中元素的顺序不重要,但集合的顺序重要 示例 金额为6欧元和硬币[1,2]:解决方案为4 [(1,1), (2,2)] [(1,1,1), (1,1,1)] [(2,2), (

我试图找出如何解决一个问题,这似乎是一个常见算法问题的棘手变化,但需要额外的逻辑来处理特定的需求

给定一个硬币列表和一个数量,我需要计算使用无限数量的可用硬币提取给定数量的可能方法的总数(这是一个使用动态规划很容易解决的经典变革问题),同时也满足一些附加要求

  • 提取的硬币可分为两套大小相等的硬币(但不一定是等额的)
  • 集合中元素的顺序不重要,但集合的顺序重要
示例

金额为6欧元和硬币[1,2]:解决方案为4

[(1,1), (2,2)]
[(1,1,1), (1,1,1)]
[(2,2), (1,1)]
[(1,2), (1,2)]
金额为8欧元和硬币[1,2,6]:解决方案为7

[(1,1,2), (1,1,2)]
[(1,2,2), (1,1,1)]
[(1,1,1,1), (1,1,1,1)]
[(2), (6)]
[(1,1,1), (1,2,2)]
[(2,2), (2,2)]
[(6), (2)]
现在我尝试了不同的方法,但我发现的唯一方法是收集所有可能的解决方案(使用动态规划),然后过滤不可拆分的解决方案(使用奇数个硬币)和副本。我很确定有一种组合方法可以计算复制的总数量,但我不知道如何计算。

(以下方法首先枚举分区。我的另一个答案以自下而上的方式生成赋值。)如果您想根据硬币计数来计算硬币交换的分割,并排除对每一方的多余硬币分配(例如,将1+2+2+1分成两个相等基数的部分只是
(1,1)|(2,2)
(2,2)|(1,1)
(1,2)|(1,2)
,每个部分的元素顺序都不重要),我们可以依赖于忽略顺序的分区枚举

然而,我们需要知道每个分区中的多组元素(或相似元素的集合),以便计算将它们一分为二的可能性。例如,要计算拆分
1+2+2+1
的方式,我们首先要计算每个硬币的数量:

Python代码:

def partitions_的偶数为多集(n,硬币):
结果=[]
def C(m、n、s、p):
如果n<0或m个分区,其中部分的偶数为多集(6,[1,2,6])
=> [[6, 0, 0], [2, 2, 0]]
^^^^这个代表两个1和两个2
现在,由于我们正在计算选择其中一半的方法,我们需要在多项式乘法中找到
x^2
的系数

(x^2 + x + 1) * (x^2 + x + 1) = ... 3x^2 ...
表示从多集计数中选择两个的三种方式
[2,2]

2,0 => 1,1
0,2 => 2,2
1,1 => 1,2
在Python中,我们可以使用
numpy.polymul
乘以多项式系数。然后我们在结果中查找适当的系数

例如:

导入numpy
def计数按多集分割分区计数(多集):
系数=(多集[0]+1)*[1]
对于x范围内的i(1,len(多集)):
系数=numpy.polymul(系数,(多集[i]+1)*[1])
返回系数[和(多集)/2]
输出:

=> count_split_partitions_by_multiset_count([2,2,0])
=> 3

(发布了一个类似的答案。)

下面是一个表的实现和一些详细说明。这将在大约2秒钟内生成
f(500、[1,2,6,12,24,48,60])
的答案

C(n,k,S)=sum(C(n-S_i,k-1,S[i:])
的简单声明意味着使用
k
硬币添加所有获得当前总和的方法。然后,如果我们将
n
分成所有可以分成两部分的方式,我们只需添加所有这些部分可以由相同数量的
k
硬币制成的方式

将我们从中选择的硬币子集固定到递减列表的美妙之处在于,任何任意的硬币组合都只会被计算一次——在计算中,组合中最左边的硬币是递减子集中的第一枚硬币(假设我们以同样的方式对它们进行排序)。例如,取自
[1,2,6,12,24,48,60]
的任意子集
[6,24,48]
,将仅计算在子集
[6,12,24,48,60]
的总和中,因为下一个子集
[12,24,48,60]
将不包括
6
和前一个子集
[2,6,12,24,48,60]
至少有一枚
2
硬币

Python代码(请参见;确认):

导入时间
def f(n,硬币):
t0=时间。时间()
最小硬币=最小(硬币)
m=[[0]*len(硬币)表示X范围内的k(n/min_硬币+1)]表示X范围内的n(n+1)]
#初始化基本情况
对于x范围内的i(len(硬币)):
m[0][0][i]=1
对于x范围内的i(len(硬币)):
对于X范围内的_i(i+1):
对于X范围内的_n(硬币[_i],n+1):
对于X范围内的k(1,\ n/min\硬币+1):
m[_n][k][i]+=m[_n-硬币[_i]][k-1][[u i]
结果=0
对于X范围(1,n+1)内的a:
b=n-a
对于X范围内的k(1,n/min_硬币+1):
结果=结果+m[a][k][len(硬币)-1]*m[b][k][len(硬币)-1]
总时间=时间。时间()-t0
返回(结果、总时间)
打印f(500[1,2,6,12,24,48,60])

非常感谢!使用这种技术,我能够将算法的速度提高一个数量级!无论如何,对于更大的输入来说仍然很慢:有了7枚硬币[1,2,6,12,24,48,60],我可以在一小时内计算出500枚。我很确定有一种方法可以计算选择一半硬币的方法,或者至少可以缓存部分结果集并再次降低复杂性。@AndreaMostosi我认为我们可能能够做到。一般算法可能是:
对于A,b,其中A+b=n;用m个部分计算a的分区+用m个部分计算b的分区,对于m=1到min(a,b)
我会看看以后是否能想出一些办法。关于计算部分数量有限的分区的文献很容易找到。@AndreaMostosi补充了一个实现上述想法的答案。谢谢您的努力!:)这一次我不清楚你是如何批准的