我正在使用此库进行组合: https://github.com/eoincampbell/combinatorics/
我需要找到第n个排列并计算相当大的集合(最多约30个元素)的元素,但是我什至在开始之前就停在了轨道上,请查看以下代码:
int[] testSet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
var permutation = new Permutations<int>(testSet);
var test = permutation.Count;
一切正常,直到20个元素大集合,一旦我加21,排列就停止工作,例如。 这是permutation.Count返回的结果:
-4249290049419214848
这远不是正确的数字。
我假设所有原因都归结为我使用了多少数字-溢出了库使用的int / longs。这就是为什么我要征求意见-是否有图书馆?方法?还是一种相当快速的实现方法来在bigintegers上使用组合函数?
谢谢!
答案 0 :(得分:2)
排列数量由 nPr 或 n相对于r
定义 n!
P(n,r) = --------
(n - r)!
位置:
在您的示例中,您想要获取给定列表的所有排列。在这种情况下,n = r
。
public static BigInteger CalcCount(BigInteger n, BigInteger r)
{
BigInteger result = n.Factorial() / (n - r).Factorial();
return result;
}
public static class BigIntExtensions
{
public static BigInteger Factorial(this BigInteger integer)
{
if(integer < 1) return new BigInteger(1);
BigInteger result = integer;
for (BigInteger i = 1; i < integer; i++)
{
result = result * i;
}
return result;
}
}
这取决于您如何创建/枚举排列。通常,要生成任何排列,您不需要知道所有先前的排列。换句话说,创建置换可能是一个纯粹的功能,允许您直接创建nTh
置换,而无需创建所有可能的置换。
但是,这取决于所使用的算法。但是仅在需要时才创建置换可能会快很多(与预先创建所有可能的置换->性能和非常大的内存相反)。
这里是关于如何创建置换而无需计算先前的置换的精彩讨论:https://stackoverflow.com/a/24257996/1681616。
答案 1 :(得分:1)
这个评论太长了,但想跟进@Iqon的上述解决方案。以下是检索 n th lexicographical排列的算法:
public static int[] nthPerm(BigInteger myIndex, int n, int r, BigInteger total)
{
int j = 0, n1 = n;
BigInteger temp, index1 = myIndex;
temp = total ;
List<int> indexList = new List<int>();
for (int k = 0; k < n; k++) {
indexList.Add(k);
}
int[] res = new int[r];
for (int k = 0; k < r; k++, n1--) {
temp /= n1;
j = (int) (index1 / temp);
res[k] = indexList[j];
index1 -= (temp * j);
indexList.RemoveAt(j);
}
return res;
}
这是一个测试用例,是使用@Iqon提供的代码调用nthPerm
的结果。
public static void Main()
{
int[] testSet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
BigInteger numPerms, n, r;
n = testSet.Length;
r = testSet.Length;
numPerms = CalcCount(n, r);
Console.WriteLine(numPerms);
BigInteger testIndex = new BigInteger(1234567890987654321);
int[] myNthIndex = nthPerm(testIndex, (int) n, (int) r, numPerms);
int[] myNthPerm = new int[(int) r];
for (int i = 0; i < (int) r; i++) {
myNthPerm[i] = testSet[myNthIndex[i]];
}
Console.WriteLine(string.Join(",", myNthPerm));
}
// Returns 1,12,4,18,20,19,7,5,16,11,6,8,21,15,13,2,14,9,10,17,3
这里是带有工作代码的ideone的链接。
答案 2 :(得分:1)
您可以使用JNumberTools
List<String> list = new ArrayList<>();
//add elements to list;
JNumberTools.permutationsOf(list)
.uniqueNth(1000_000_000) //next 1 billionth permutation
.forEach(System.out::println);
此 API 将直接按字典顺序生成下一个排列。因此,您甚至可以生成 100 个项目的下一个十亿个排列。
用于生成给定大小的下一个排列使用:
JNumberTools 的 maven 依赖是:
<dependency>
<groupId>io.github.deepeshpatel</groupId>
<artifactId>jnumbertools</artifactId>
<version>1.0.0</version>
</dependency>