我正在寻找一种算法,该算法生成整数的固定长度分区的所有排列。订单无关紧要。
例如,对于n = 4且长度L = 3:
[(0, 2, 2), (2, 0, 2), (2, 2, 0),
(2, 1, 1), (1, 2, 1), (1, 1, 2),
(0, 1, 3), (0, 3, 1), (3, 0, 1), (3, 1, 0), (1, 3, 0), (1, 0, 3),
(0, 0, 4), (4, 0, 0), (0, 4, 0)]
对于长度小于L的分区,我对整数分区+排列进行了喋喋不休;但这太慢了,因为我多次得到同一个分区(因为[0, 0, 1]
可能是[0, 0, 1]
的排列; - )
任何帮助表示赞赏,不,这不是作业 - 个人兴趣: - )
答案 0 :(得分:3)
鉴于您对此感兴趣,您可能会对一个明确的答案感兴趣!它可以在Knuth的计算机编程艺术(subvolume 4A)的“7.2.1.2 - 生成所有排列”中找到。
此外,可以找到3个具体算法here。
答案 1 :(得分:2)
好。首先,忘记排列,然后生成长度为L的分区(由@Svein Bringsli建议)。请注意,对于每个分区,您可以对元素进行排序,例如>。现在只需“计数”,维持您的订购。对于n = 4,k = 3:
(4, 0, 0)
(3, 1, 0)
(2, 2, 0)
(2, 1, 1)
那么,如何实现呢?它看起来像:从位置i减去1并将其添加到下一个位置时保持我们的顺序,从位置i减去1,将1添加到位置i + 1,然后移动到下一个位置。如果我们处于最后位置,请退一步。
这是一个小蟒蛇,它就是这样做的:
def partition_helper(l, i, result):
if i == len(l) - 1:
return
while l[i] - 1 >= l[i + 1] + 1:
l[i] -= 1
l[i + 1] += 1
result.append(list(l))
partition_helper(l, i + 1, result)
def partition(n, k):
l = [n] + [0] * (k - 1)
result = [list(l)]
partition_helper(l, 0, result)
return result
现在您有了一个列表列表(实际上是一个多集的列表),并且生成列表中每个多集的所有排列为您提供了解决方案。我不打算这样,有一个递归算法,基本上说,对于每个位置,选择多集中的每个唯一元素,并附加多集的排列,这是因为从多集中删除了该元素。
答案 2 :(得分:2)
如@pbarranis所述,当 n 等于 k 时,@ rlibby的代码不包括所有列表。下面是包含所有列表的Python代码。此代码是非递归的,在内存使用方面可能更有效。
def successor(n, l):
idx = [j for j in range(len(l)) if l[j] < l[0]-1]
if not idx:
return False
i = idx[0]
l[1:i+1] = [l[i]+1]*(len(l[1:i+1]))
l[0] = n - sum(l[1:])
return True
def partitions(n, k):
l = [0]*k
l[0] = n
results = []
results.append(list(l))
while successor(n, l):
results.append(list(l))
return results
列表以连字顺序创建(算法和更多描述here)。
答案 3 :(得分:0)
就像我上面提到的,我无法获得@ rlibby的代码来满足我的需求,我需要代码,其中n = l,所以只需要你的一部分。这是我在C#下面的代码。我知道这不是上述问题的完美答案,但我相信你只需要修改第一种方法,使其适用于不同的l值;基本上添加相同的代码@rlibby,使长度为l的数组而不是长度为n。
public static List<int[]> GetPartitionPermutations(int n)
{
int[] l = new int[n];
var results = new List<int[]>();
GeneratePermutations(l, n, n, 0, results);
return results;
}
private static void GeneratePermutations(int[] l, int n, int nMax, int i, List<int[]> results)
{
if (n == 0)
{
for (; i < l.Length; ++i)
{
l[i] = 0;
}
results.Add(l.ToArray());
return;
}
for (int cnt = Math.Min(nMax, n); cnt > 0; --cnt)
{
l[i] = cnt;
GeneratePermutations(l, (n - cnt), cnt, i + 1, results);
}
}
答案 4 :(得分:0)
很多搜索导致了这个问题。这是一个包含排列的答案:
#!/usr/bin/python
from itertools import combinations_with_replacement as cr
def all_partitions(n, k):
"""
Return all possible combinations that add up to n
i.e. divide n objects in k DISTINCT boxes in all possible ways
"""
all_part = []
for div in cr(range(n+1), k-1):
counts = [div[0]]
for i in range(1, k-1):
counts.append(div[i] - div[i-1])
counts.append(n-div[-1])
all_part.append(counts)
return all_part
例如,OP提出的all_part(4,3)给出了:
[[0, 0, 4],
[0, 1, 3],
[0, 2, 2],
[0, 3, 1],
[0, 4, 0],
[1, 0, 3],
[1, 1, 2],
[1, 2, 1],
[1, 3, 0],
[2, 0, 2],
[2, 1, 1],
[2, 2, 0],
[3, 0, 1],
[3, 1, 0],
[4, 0, 0]]
答案 5 :(得分:0)
我发现使用递归函数对于较大的长度和整数并不好,因为它会占用太多RAM,并且使用生成器/可恢复函数(产生&#39;值)太慢了需要一个大型库来实现跨平台。
所以这里是C ++中的非递归解决方案,它以排序顺序生成分区(这也是排列的理想选择)。我发现这比看似聪明和简洁的递归解决方案快了10倍,我尝试了4或更大的分区长度,但对于1-3的长度,性能不一定更好(我不喜欢&#39;关心短长度,因为他们用这两种方法都很快。
// Inputs
unsigned short myInt = 10;
unsigned short len = 3;
// Partition variables.
vector<unsigned short> partition(len);
unsigned short last = len - 1;
unsigned short penult = last - 1;
short cur = penult; // Can dip into negative value when len is 1 or 2. Can be changed to unsigned if len is always >=3.
unsigned short sum = 0;
// Prefill partition with 0.
fill(partition.begin(), partition.end(), 0);
do {
// Calculate remainder.
partition[last] = max(0, myInt - sum); // Would only need "myInt - sum" if partition vector contains signed ints.
/*
*
* DO SOMETHING WITH "partition" HERE.
*
*/
if (partition[cur + 1] <= partition[cur] + 1) {
do {
cur--;
} while (
cur > 0 &&
accumulate(partition.cbegin(), partition.cbegin() + cur, 0) + (len - cur) * (partition[cur] + 1) > myInt
);
// Escape if seeked behind too far.
// I think this if-statement is only useful when len is 1 or 2, can probably be removed if len is always >=3.
if (cur < 0) {
break;
}
// Increment the new cur position.
sum++;
partition[cur]++;
// The value in each position must be at least as large as the
// value in the previous position.
for (unsigned short i = cur + 1; i < last; ++i) {
sum = sum - partition[i] + partition[i - 1];
partition[i] = partition[i - 1];
}
// Reset cur for next time.
cur = penult;
}
else {
sum++;
partition[penult]++;
}
} while (myInt - sum >= partition[penult]);
我写的做了什么&#34;分区&#34; HERE。是您实际使用该值的地方。 (在最后一次迭代中,代码将继续执行循环的其余部分,但我发现这比不断检查退出条件更好 - 它针对更大的操作进行了优化)
0,0,10 0,1,9 0,2,8 0,3,7 0,4,6 0,5,5 1,1,8 1,2,7 1,3,6 1,4,5 2,2,6 2,3,5 2,4,4 3,3,4
哦,我已经使用了&#34; unsigned short&#34;因为我知道我的长度和整数不会超过某些限制,如果它不适合你就改变它:)检查评论;一个变量(cur)必须被签名以处理1或2的长度,并且有一个相应的if语句,并且我在评论中还注意到,如果你的分区向量已经签名有一些可以简化的行。
要获得所有组合,在C ++中我会使用这种简单的排列策略,幸好不会产生任何重复:
do {
// Your code goes here.
} while (next_permutation(partition.begin(), partition.end()));
将其嵌入 DO SOMETHING WITH&#34;分区&#34;在这里现场,你很高兴。
查找组合的替代方法(基于此处的Java代码https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)如下所示。我发现这比next_permutation()更好。
// Process lexicographic permutations of partition (compositions).
composition = partition;
do {
// Your code goes here.
// Find longest non-increasing suffix
i = last;
while (i > 0 && composition[i - 1] >= composition[i]) {
--i;
}
// Now i is the head index of the suffix
// Are we at the last permutation already?
if (i <= 0) {
break;
}
// Let array[i - 1] be the pivot
// Find rightmost element that exceeds the pivot
j = last;
while (composition[j] <= composition[i - 1])
--j;
// Now the value array[j] will become the new pivot
// Assertion: j >= i
// Swap the pivot with j
temp = composition[i - 1];
composition[i - 1] = composition[j];
composition[j] = temp;
// Reverse the suffix
j = last;
while (i < j) {
temp = composition[i];
composition[i] = composition[j];
composition[j] = temp;
++i;
--j;
}
} while (true);
您会注意到那里有一些未声明的变量,只需在代码中的所有do循环之前在代码中声明它们:i
,j
,pos
和{{1 (无符号短裤)和temp
(与composition
相同的类型和长度)。您可以重复使用partition
的声明,以便在分区代码段中的for循环中使用它。还要注意使用i
之类的变量,它假定这段代码嵌套在前面给出的分区代码中。
再次&#34;你的代码在这里&#34;是你为了自己的目的而消费构图的地方。
以下是我的标题。
last
尽管使用这些方法大大提高了速度,但对于任何相当大的整数和分区长度,这仍然会让你对你的CPU感到生气:)