有多少种方法来表示给定数字集中的一个数字
我想知道我们可以用多少种方式将数字 x 表示为给定数字集合 {a1.a2,a3,...} 中的数字之和。每个数字可以多次被取走。
例如,如果x=4且a1=1,a2=2,则表示x=4的方式为:
1+1+1+1
1+1+2
1+2+1
2+1+1
2+2
因此方式的数量=5。
我想知道是否存在一个公式或其他一些快速方法可以做到这一点。我无法用暴力破解它。我想为它编写代码。
注意:x 可以大到 10^18。 a1,a2,a3,… 项的数量最多可以为 15,并且 a1,a2,a3,… 中的每一项也最多只能为 15。
I want to know in how many ways can we represent a number x as a sum of numbers from a given set of numbers {a1.a2,a3,...}. Each number can be taken more than once.
For example, if x=4 and a1=1,a2=2, then the ways of representing x=4 are:
1+1+1+1
1+1+2
1+2+1
2+1+1
2+2
Thus the number of ways =5.
I want to know if there exists a formula or some other fast method to do so. I can't brute force through it. I want to write code for it.
Note: x can be as large as 10^18. The number of terms a1,a2,a3,… can be up to 15, and each of a1,a2,a3,… can also be only up to 15.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
计算组合的数量可以在
O(log x)
内完成,无需考虑对任意大小的整数执行矩阵乘法所需的时间。组合的数量可以表示为递归。令
S(n)
为通过将一组数字相加得到n
的方法数。重复率是其中
a_i
是i
在集合中出现的次数。另外,对于n<0,S(n)=0。这种递归可以用大小为 15*15 的矩阵A
来表示(或更小是集合中最大的数更小)。然后,如果您有一个列向量V
,则矩阵乘法
A*V
的结果将是A
矩阵定义如下:其中
a_i
的定义如上。通过手动执行乘法,可以立即看到该矩阵与S(n_14) ... S(n)
向量相乘有效的证明;向量中的最后一个元素将等于n+1
递归式的右侧。通俗地说,矩阵中的元素将列向量中的元素向上移动一行,矩阵的最后一行计算最新项。为了计算递推的任意项
S(n)
,需要计算A^n * V
,其中V
等于In为了将运行时间降低到
O(log x)
,可以使用求幂平方来计算A^n
。事实上,完全忽略列向量就足够了,
A^n
的右下元素包含所需的值S(n)
。如果上面的解释很难理解,我提供了一个 C 程序,它可以按照我上面描述的方式计算组合的数量。请注意,它很快就会溢出 64 位整数。使用GMP,您将能够更进一步地获得高精度浮点类型,尽管您不会得到一个确切的答案。
不幸的是,我找不到一种快速方法来获得
x=10^18
等数字的准确答案,因为答案可能比10^x
大得多。Calculating the number of combinations can be done in
O(log x)
, disregarding the time it takes to perform matrix multiplication on arbitrarily sized integers.The number of combinations can be formulated as a recurrence. Let
S(n)
be the number of ways to make the numbern
by adding numbers from a set. The recurrence iswhere
a_i
is the number of timesi
occurs in the set. Also, S(n)=0 for n<0. This kind of recurrence can be formulated in terms of a matrixA
of size 15*15 (or less is the largest number in the set is smaller). Then, if you have a column vectorV
containingthen the result of the matrix multiplication
A*V
will beThe
A
matrix is defined as follows:where
a_i
is as defined above. The proof that the multiplication of this matrix with a vector ofS(n_14) ... S(n)
works can be immediately seen by performing the multiplication manually; the last element in the vector will be equal to the right hand side of the recurrence withn+1
. Informally, the ones in the matrix shifts the elements in the column vector one row up, and the last row of the matrix calculates the newest term.In order to calculate an arbitrary term
S(n)
of the recurrence is to calculateA^n * V
, whereV
is equal toIn order to get the runtime down to
O(log x)
, one can use exponentiation by squaring to calculateA^n
.In fact, it is sufficient to ignore the column vector altogether, the lower right element of
A^n
contains the desired valueS(n)
.In case the above explanation was hard to follow, I have provided a C program that calculates the number of combinations in the way I described above. Beware that it will overflow a 64-bits integer very quickly. You'll be able to get much further with a high-precision floating point type using GMP, though you won't get an exact answer.
Unfortunately, I can't see a fast way to get an exact answer for numbers such at
x=10^18
, since the answer can be much larger than10^x
.由于总和的顺序很重要,因此它成立:
这对于动态规划解决方案来说已经足够了。如果值 S(i, set) 从 0 到 n 创建,则复杂度为 O( n*k ) 。
编辑:只是一个想法。将一个求和视为一个序列
(s_1, s_2, ..., s_m)
。序列第一部分的总和在某个时刻将大于n/2
,令其对于索引j
:最多有
k
不同的和S
,并且对于每个S,最多有k
个可能的最后元素s_j
。所有可能性(S,s_j)
将序列总和分为 3 部分。它保持
n/2 >= L, R > n/2 - 最大值{a_i}
。这样,上面的公式就有了更复杂的形式:我不确定,但我认为每一步都需要“创建”范围
S(x,set)
值,其中范围将按因子max{a_i}
线性增长。编辑 2:@Andrew 示例。第一种方法很容易实现,并且适用于“小”
x
。这是 python 代码:此实现存在大
x
内存问题(在 python 中>10^5)。由于只需要最后的max(a_i)
值,因此可以使用循环缓冲区来实现它。这些值增长得非常快,例如 S(100000, [1,2,8] ) 约为 10^21503。
Since order in sum is important it holds:
That is enough for dynamic programming solution. If values S(i, set) are created from 0 to n, than complexity is
O( n*k )
.Edit: Just an idea. Look at one summation as a sequence
(s_1, s_2, ..., s_m)
. Sum of first part of sequence will be larger thann/2
at one point, let it be for indexj
:There are at most
k
different sumsS
, and for each S there are at mostk
possible last elementss_j
. All of possibilities(S,s_j)
split sequence sum in 3 parts.It hold
n/2 >= L, R > n/2 - max{a_i}
. With that, upper formula have more complicated form:I'm not sure, but I think that with each step it will be needed to 'create' range of
S(x,set)
values where range will grow linearly by factormax{a_i}
.Edit 2: @Andrew samples. It is easy to implement first method and it works for 'small'
x
. Here is python code:This implementation has problem with memory for large
x
(>10^5 in python). Since only lastmax(a_i)
values are needed it is possible to implement it with circular buffer.These values grow very fast, e.g. S(100000, [1,2,8] ) is ~ 10^21503.
如果您想从给定的一组数字中找到表示数字 N 的所有可能方法,那么您应该遵循已经提出的动态规划解决方案。
但如果您只想知道方法的数量,那么您正在处理受限分区函数问题。
您还应该查看关于没有限制的分区函数的维基百科文章,其中没有限制申请。
附言。如果也允许负数,那么可能有(可数)无限种方式来表示您的总和。
PS2。这更像是一道数学问题,而不是编程问题
If you want to find all possible ways of representing a number N from a given set of numbers then you should follow a dynamic programming solution as already proposed.
But if you just want to know the number of ways, then you are dealing with the restricted partition function problem.
You should also check the wikipedia article on partition function without restrictions where no restrictions apply.
PS. If negative numbers are also allowed then there probably are (countably )infinite ways to represent your sum.
PS2. This is more a math question than a programming one