Desenvolvi uma classe em C# para facilitar a conversão e desconversão de CSNs, utilizando o BigInteger do .NET, que suporta quaisquer valores sem overflow.
O uso é muito simples, para saber o CSN de uma combinação qualquer, basta chamar:
var c = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
Console.WriteLine(Rankings.Rank(c).ToString());
E para saber qual é a combinação, fornecendo o CSN...
var r = Rankings.UnRank(15, new BigInteger(3268760)); // é preciso fornecer o valor de "k", o "tamanho" da combinação.
foreach (var item in r) {
Console.Write(item + " ");
}
- Os CSNs estão em ordem Colex.
- Tanto os CSNs quanto as combinações, devem estar indexadas em 1, dessa forma:
CSN 1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
ao invés de:
CSN 0 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }
embora o código original seja indexado por zero: 0 <= CSN < C(n, k)
Espero que seja útil. Segue o código:
public class Rankings {
public static BigInteger Rank(int[] c) {
BigInteger r = BigInteger.Zero;
int k = 1;
for (int i = 0; i < c.Length; i++) {
r += Binomial(c[i] - 1, k++);
}
return r;
}
public static int[] UnRank(int k, BigInteger rank) {
int[] c = new int[k];
int n;
int x = k;
for (int i = k - 1; i >= 0; i--) {
n = i;
while (Binomial(n, x) <= (rank - 1)) {
n++;
}
c[i] = n;
rank -= Binomial(n - 1, x--);
}
return c;
}
private static BigInteger Binomial(int n, int k) {
BigInteger r = BigInteger.One;
for (int i = 1; i <= k; i++) {
r *= n - (k - i);
r /= i;
}
return r;
}
}