建立有效地求和为数字的排列
Posted
技术标签:
【中文标题】建立有效地求和为数字的排列【英文标题】:Building permutation that sums to a number efficiently 【发布时间】:2020-09-03 17:35:59 【问题描述】:我希望生成更多的排列,总和为给定数字N
,但这次更有效。因为通过一般方法,创建 100 多个排列需要很长时间。
但是,我处于另一个僵局,我发现构建向上排列非常困难,该排列利用已经解决的排列 n-1
生成每个排列总和为 n
。
我将不胜感激任何帮助!我仍然是一个新手,如果这似乎是一个简单的问题,我很抱歉。但这让我心烦意乱!
Input(n): 4
Output: [[4],[3,1],[1,3],[2,2],[1,1,2],[1,2,1],[2,1,1],[1,1,1,1]]
import java.util.*;
import javax.naming.PartialResultException;
public class Perm
private List<List<Integer>> computePerm(int n)
// I totally lost it here
if (n == 0)
return computePerm(0);
else
List<Integer> arr2 = new ArrayList<>();
for (List<Integer> entry : arr1)
for (int i = 0; i < entry.size(); i++)
arr2.add(entry.get(i)); // This obviously doesn't work
return computePerm(n);
public static void main(String[] args)
Perm perm1 = new Perm();
System.out.println(computePerm(4));
【问题讨论】:
这能回答你的问题吗? Is there an efficient algorithm for integer partitioning with restricted number of parts? 还有:Integer Partition in Java、Integer Partitioning in Java、Integer Partition Code Optimization。这些可能比骗子要好,因为您有一个一般情况,大概是允许所有数字。 我查看了您链接我的帖子(非常有帮助),但是在大多数情况下,分区仅按一个顺序创建,而我也希望生成其他顺序,其中的数字相同,但定位不同。这是否意味着我必须独立排列每个分区案例? 我希望 n 达到 100 左右,但目前这似乎几乎是不可能的。 也许这有帮助? ***.com/questions/60347184/… 【参考方案1】:您可以使用mapToObj
和reduce
方法生成指定数字的和的可能组合的二维数组,即整数组合。首先准备求和的二维数组,得到一个二维数组流,然后将这些数组的对依次相乘,得到笛卡尔积。
Try it online!
int n = 5;
int[][] composition = IntStream.range(0, n)
// prepare 2D arrays of summands
.mapToObj(i -> IntStream.rangeClosed(1, n - i)
.mapToObj(j -> new int[]j)
// Stream<int[][]>
.toArray(int[][]::new))
// intermediate output, 2D arrays of summands
.peek(arr -> System.out.println(Arrays.deepToString(arr)))
// sequential summation of array pairs, i.e. getting the
// cartesian product of arrays, up to the given number
.reduce((arr1, arr2) -> Arrays.stream(arr1)
// combinations of inner arrays
.flatMap(row1 ->
// sum of the elements of the first row
int sum = Arrays.stream(row1).sum();
// if the specified number is reached
if (sum == n) return Arrays.stream(new int[][]row1);
// otherwise continue appending summands
return Arrays.stream(arr2)
// drop those combinations that are greater
.filter(row2 -> Arrays.stream(row2).sum() + sum <= n)
.map(row2 -> Stream.of(row1, row2)
.flatMapToInt(Arrays::stream).toArray());
) // array of combinations
.toArray(int[][]::new))
// otherwise an empty 2D array
.orElse(new int[0][]);
// final output, the integer composition of the specified number
Arrays.stream(composition).map(Arrays::toString).forEach(System.out::println);
中间输出,2D 和数数组:
[[1], [2], [3], [4], [5]]
[[1], [2], [3], [4]]
[[1], [2], [3]]
[[1], [2]]
[[1]]
最终输出,指定数的整数组成:
[1, 1, 1, 1, 1]
[1, 1, 1, 2]
[1, 1, 2, 1]
[1, 1, 3]
[1, 2, 1, 1]
[1, 2, 2]
[1, 3, 1]
[1, 4]
[2, 1, 1, 1]
[2, 1, 2]
[2, 2, 1]
[2, 3]
[3, 1, 1]
[3, 2]
[4, 1]
[5]
另见:Integer partition iterative code optimization
【讨论】:
【参考方案2】:如果加法的顺序很重要,以至于[1,3]
和[3,1]
不相同,那么它就是整数组合。您可以使用递归方法有效地构建此序列,但即便如此,它对于大数字来说还是太慢了。排列数是2<sup>(n-1)</sup>
,所以我停在27
。
表n=[1-27]
的输出,组合数,时间毫秒:
n: 1, composition: 1, time: 1
n: 2, composition: 2, time: 0
n: 3, composition: 4, time: 0
n: 4, composition: 8, time: 0
n: 5, composition: 16, time: 0
n: 6, composition: 32, time: 1
n: 7, composition: 64, time: 1
n: 8, composition: 128, time: 2
n: 9, composition: 256, time: 4
n: 10, composition: 512, time: 7
n: 11, composition: 1024, time: 14
n: 12, composition: 2048, time: 24
n: 13, composition: 4096, time: 24
n: 14, composition: 8192, time: 1
n: 15, composition: 16384, time: 3
n: 16, composition: 32768, time: 6
n: 17, composition: 65536, time: 11
n: 18, composition: 131072, time: 22
n: 19, composition: 262144, time: 46
n: 20, composition: 524288, time: 87
n: 21, composition: 1048576, time: 174
n: 22, composition: 2097152, time: 368
n: 23, composition: 4194304, time: 768
n: 24, composition: 8388608, time: 1635
n: 25, composition: 16777216, time: 3040
n: 26, composition: 33554432, time: 9015
n: 27, composition: 67108864, time: 45804
Java 7 代码:
public static void main(String[] args)
List<List<Integer>> list = composition(4, true);
for (List<Integer> comb : list)
System.out.println(comb);
for (int i = 1; i <= 27; i++)
long time = System.currentTimeMillis();
List<List<Integer>> list1 = composition(i, false);
time = System.currentTimeMillis() - time;
System.out.println("n: " + String.format("%2d", i)
+ ", composition: " + String.format("%8d", list1.size())
+ ", time: " + String.format("%5d", time));
public static List<List<Integer>> composition(int n, boolean verbose)
List<List<Integer>> list = new ArrayList<>();
composition(n, verbose ? "" : null, list, new ArrayList<Integer>());
return list;
public static void composition(
int i, String indent, List<List<Integer>> master, List<Integer> holder)
if (indent != null && i == 0)
System.out.println(indent + "i=" + i + " comb=" + holder);
if (i == 0) master.add(holder);
for (int j = i; j >= 1; j--)
ArrayList<Integer> temp = new ArrayList<>(holder);
temp.add(j);
if (indent != null)
System.out.println(indent + "i=" + i + ",j=" + j + " temp=" + temp);
composition(i - j, indent != null ? indent + " " : null, master, temp);
递归树n=4
的输出:
i=4,j=4 temp=[4]
i=0 comb=[4]
i=4,j=3 temp=[3]
i=1,j=1 temp=[3, 1]
i=0 comb=[3, 1]
i=4,j=2 temp=[2]
i=2,j=2 temp=[2, 2]
i=0 comb=[2, 2]
i=2,j=1 temp=[2, 1]
i=1,j=1 temp=[2, 1, 1]
i=0 comb=[2, 1, 1]
i=4,j=1 temp=[1]
i=3,j=3 temp=[1, 3]
i=0 comb=[1, 3]
i=3,j=2 temp=[1, 2]
i=1,j=1 temp=[1, 2, 1]
i=0 comb=[1, 2, 1]
i=3,j=1 temp=[1, 1]
i=2,j=2 temp=[1, 1, 2]
i=0 comb=[1, 1, 2]
i=2,j=1 temp=[1, 1, 1]
i=1,j=1 temp=[1, 1, 1, 1]
i=0 comb=[1, 1, 1, 1]
列表n=4
的输出:
[4]
[3, 1]
[2, 2]
[2, 1, 1]
[1, 3]
[1, 2, 1]
[1, 1, 2]
[1, 1, 1, 1]
【讨论】:
【参考方案3】:这可以递归完成。堆栈包含您之前构建并添加到其中的内容。
虽然我首先担心正确性,然后是优化,但我看不出有什么方法可以解决您需要枚举每个 integer partitions 的事实,因此复杂性将呈指数级增长,除非我错过了一些东西。
import java.util.ArrayDeque;
import java.util.ArrayList;
class Main
public static ArrayList<ArrayList<Integer>> partition(int n)
var result = new ArrayList<ArrayList<Integer>>();
partition(n, new ArrayDeque<>(), result);
return result;
private static void partition(int n, ArrayDeque<Integer> path,
ArrayList<ArrayList<Integer>> result)
if (n == 0) result.add(new ArrayList<Integer>(path));
for (int i = 1; i <= n; i++)
path.offer(i);
partition(n - i, path, result);
path.pop();
public static void main(String[] args)
for (var result : partition(4))
for (int i : result)
System.out.print(i + " ");
System.out.println();
输出:
1 1 1 1
1 1 2
2 2 1
1 3
2 1 1
1 2
3 1
4
【讨论】:
以上是关于建立有效地求和为数字的排列的主要内容,如果未能解决你的问题,请参考以下文章