生成数字的分区
我需要一种算法来生成正数的所有可能分区,我想出了一个(作为答案发布),但这是指数时间。
该算法应返回将数字表示为小于或等于其自身的正数之和的所有可能方式。 例如,对于数字 5,结果将是:
- 5
- 4+1
- 3+2
- 3+1+1
- 2+2+1
- 2+1+1+1
- 1+1+1+ 1+1
所以我的问题是:是否有更有效的算法?
编辑:问题的标题是“数字的求和分解”,因为我真的不知道这叫什么。 ShreevatsaR 指出它们被称为“分区”,所以我编辑了相应的问题标题。
I needed an algorithm to generate all possible partitions of a positive number, and I came up with one (posted as an answer), but it's exponential time.
The algorithm should return all the possible ways a number can be expressed as the sum of positive numbers less than or equal to itself. So for example for the number 5, the result would be:
- 5
- 4+1
- 3+2
- 3+1+1
- 2+2+1
- 2+1+1+1
- 1+1+1+1+1
So my question is: is there a more efficient algorithm for this?
EDIT: Question was titled "Sum decomposition of a number", since I didn't really know what this was called. ShreevatsaR pointed out that they were called "partitions," so I edited the question title accordingly.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
它称为分区。 [另请参阅维基百科:分区(数论)。]
分区数 p (n) 呈指数增长,因此您为生成所有分区所做的任何事情都必然需要花费指数时间。
也就是说,您可以做得比您的代码更好。 请参阅此或其更新版本,位于Python 算法和数据结构 作者:大卫·埃普斯坦。
It's called Partitions. [Also see Wikipedia: Partition (number theory).]
The number of partitions p(n) grows exponentially, so anything you do to generate all partitions will necessarily have to take exponential time.
That said, you can do better than what your code does. See this, or its updated version in Python Algorithms and Data Structures by David Eppstein.
这是我的 Python 解决方案(指数时间)
:
Here's my solution (exponential time) in Python:
当你问更有效的算法时,我不知道该比较哪个。 但这里有一种以直接方式(Erlang)编写的算法:
它是时间指数的(与 Berk Güder 的 Python 解决方案)可以在堆栈空间中线性化吗? 但是使用相同的技巧,记忆化,您可以通过节省一些内存和更少的指数来实现很大的改进。 (对于 N=50,速度快十倍)
无论如何,您应该针对您的语言和目的进行基准测试。
When you ask to more efficient algorithm, I don't know which to compare. But here is one algorithm written in straight forward way (Erlang):
It is exponential in time (same as Can Berk Güder's solution in Python) and linear in stack space. But using same trick, memoization, you can achieve big improvement by save some memory and less exponent. (It's ten times faster for N=50)
Anyway you should benchmark for your language and purposes.
这是一种更冗长的方法(这是我在知道术语“分区”之前所做的,它使我能够进行谷歌搜索):
Here's a much more long-winded way of doing it (this is what I did before I knew the term "partition", which enabled me to do a google search):
Java 实现。 可以从记忆中受益。
Java implementation. Could benefit from memoization.
这是我用 Haskell 编写的使用同态的解决方案。
它绝对不是最有效的,但我认为它非常优雅,而且肯定具有启发性。
Here's a solution in using paramorphisms that I wrote in Haskell.
It's definitely not the most efficient one around, but I think it's quite elegant and it's certainly instructive.
这是这个问题的java代码
here is the java code for this question
另一个 Java 解决方案。 它首先创建第一个分区,该分区只是给定的编号。 然后它进入 while 循环,查找最后创建的分区中大于 1 的最后一个数字。从该数字将 1 移至数组中的下一个数字。 如果下一个数字最终与找到的数字相同,则它会移动到下一个数字。 当最后创建的分区的第一个数字为 1 时,循环停止。这是有效的,因为所有分区中的数字始终按降序排序。
以数字 5 为例。首先它创建第一个分区,即数字 5。然后它在最后一个分区中找到大于 1 的最后一个数字。由于我们的最后一个分区是数组 [5, 0, 0, 0, 0],所以它找到了数字5 在索引 0 处。然后它从 5 中取出一个并将其移动到下一个位置。 这就是我们得到分区 [4, 1, 0, 0, 0] 的方式。 它再次进入循环。 现在它从 4 中取出一个并将其向上移动,这样我们就得到了 [3, 2, 0, 0, 0]。 然后同样的事情,我们得到 [3, 1, 1, 0, 0]。 在下一次迭代中,我们得到 [2, 2, 1, 0, 0]。 现在它从第二个 2 中获取 1,并尝试将其移动到索引 2,其中我们有 1。它将跳到下一个索引,因为我们也会得到 2,并且我们将有分区 [2, 1, 2, 0, 0],其中只是最后一个的重复。 相反,我们得到 [2, 1, 1, 1, 0]。 在最后一步中,我们到达 [1, 1, 1, 1, 1] 并且存在循环,因为新分区的第一个数字是 1。
Another Java solution. It starts by creating first partition which is only the given number. Then it goes in while loop which is finding the last number in last created partition which is bigger then 1. From that number it moves 1 to next number in array. If next number would end up being the same as the found number it moves to the next in line. Loop stops when first number of last created partition is 1. This works because at all times numbers in all partitions are sorted in descending order.
Example with number 5. First it creates first partition which is just number 5. Then it finds last number in last partition that is greater then 1. Since our last partition is array [5, 0, 0, 0, 0] it founds number 5 at index 0. Then it takes one from 5 and moves it to next position. That is how we get partition [4, 1, 0, 0, 0]. It goes into the loop again. Now it takes one from 4 and moves it up so we get [3, 2, 0, 0, 0]. Then the same thing and we get [3, 1, 1, 0, 0]. On next iteration we get [2, 2, 1, 0, 0]. Now it takes one from second 2 and tries to move it to index 2 where we have 1. It will skip to the next index because we would also get 2 and we would have partition [2, 1, 2, 0, 0] which is just duplicate of the last one. instead we get [2, 1, 1, 1, 0]. And in the last step we get to [1, 1, 1, 1, 1] and loop exists since first number of new partition is 1.
这是我的 Rust 实现(灵感来自 Python 算法和数据结构 ):
Here is my Rust implementation (inspired by Python Algorithms and Data Structures):