Related
heyy guys i m trying to solve Factorial decomposition (codewars task )
well some numbers work with me untill i reach number 23 the page keep looping over and over someone help me please
function decomp(n) {
let c =[]
let sum =1
for(let i=n;i>=1;i--){
sum*=i
}
let k= 2
while(k<=sum){
if(sum%k!==0){
k++}
while(sum%k==0){
c.push(k)
sum = sum/k
}
}
return c.join('*')
}
the function works good until i reach the number 23 the keep loading over and over , the tasks is about the function is decomp(n) and should return the decomposition of n! into its prime factors in increasing order of the primes, as a string.
factorial can be a very big number (4000! has 12674 digits, n can go from 300 to 4000).
In Fortran - as in any other language - the returned string is not permitted to contain any redundant trailing whitespace: you can use dynamically allocated character strings.
example
n = 12; decomp(12) -> "2^10 * 3^5 * 5^2 * 7 * 11"
since 12! is divisible by 2 ten times, by 3 five times, by 5 two times and by 7 and 11 only once.
n = 22; decomp(22) -> "2^19 * 3^9 * 5^4 * 7^3 * 11^2 * 13 * 17 * 19"
n = 25; decomp(25) -> 2^22 * 3^10 * 5^6 * 7^3 * 11^2 * 13 * 17 * 19 * 23
23! cannot be exactly expressed in double-precision floating-point format, which JavaScript uses for its numbers.
However, you don't need to compute n!. You just need to factorize each number and concat their factorization.
Actually, you don't even need to factorize each number. Note that given n and p, there are (n/p) numbers no greater than n that are multiples of p, (n/(p*p)) that are multiples of p*p, etc.
function *primes(n) {
// Sieve of Eratosthenes
const isPrime = Array(n + 1).fill(true);
isPrime[0] = isPrime[1] = false;
for (let i = 2; i <= n; i++) {
if (isPrime[i]) {
yield i;
for (let j = i * i; j <= n; j += i)
isPrime[j] = false;
}
}
}
function decomp(n) {
let s = n + '! =';
for (const p of primes(n)) {
let m = n, c = 0;
// There are (n/p) numbers no greater than n that are multiples of p
// There are (n/(p*p)) numbers no greater than n that are multiples of p*p
// ...
while (m = ((m / p) | 0)) {
c += m;
}
s += (p == 2 ? ' ' : ' * ') + p + (c == 1 ? '' : '^' + c);
}
return s;
}
console.log(decomp(12))
console.log(decomp(22))
console.log(decomp(23))
console.log(decomp(24))
console.log(decomp(25))
Here I'm trying to form an arrangement that contains pairs of numbers that each pair of m's are separated by m elements. for example:
for [0,2], the pair arrangement is [2,0, 0,2] such that m=2, hence the number 2 is separated by 2 elements.
for [0,1] = there is no valid arrangement
I still can't figure out the pattern or algorithm for the arrangement as I need to find the arrangement up to [0,1,2,3,4,5,6,7,8]. however the valid arrangement for this list is [3,7,8,2,3,1,2,1,6,7,5,8,4,0,0,6,5,4] by doing it manually.
In the codes below, I could only rearrange the numbers in the list by getting the largest number in the list first. I want to know how to separate the pair according to the number of the pair (e.g if the pair is 2, hence separation number is 2)
how can i do the separation and pattern for the list of numbers?
package main
import "fmt"
func MagicPairs(list []int) {
//length := len(list) * 2
magicPair := []int{}
magicPair = append(list, list...)
for i := 0; i <len(magicPair); i++{
if len(magicPair) == 0 {
//do nothing
}else{
m := max(list)
for p, x := range magicPair{
if magicPair[len(magicPair)-1] == m {
magicPair = append([]int{m}, (magicPair)[:len(magicPair)-1]...)
fmt.Println(magicPair)
return
}
if x == m{
magicPair = append([]int{m}, append((magicPair)[:p], (magicPair)[p+1:]...)...)
}
previousPair := magicPair[x]
if x == previousPair{
}
}
}
}
fmt.Println("1", magicPair)
}
func max(list[] int) int{
max := list[0]
for _, value := range list{
if value > max {
max = value
}
}
return max
}
func main(){
list := [] int {0,1,2,3,4,5,6,7,8}
MagicPairs(list)
}
You seem to try to find the optimal solution by doubling up the source list and then shuffling the numbers around by repeatedly slicing and concatenating the array.
I think this problem lends itself to a recursive approach. Create thze target array with 2 * len(list) empty slots. (Whether a slot is empty or not must be marked with a special value, for example -1.) Then recursively try to fit the elements of the original array into the target array.
Let's look at your example {0, 1, 3}. Create the target array:
. . . . . .
Try all possible poitions for the 0. The first is
0 0 . . . .
Now try to fit the 1. There are two possibilities
0 0 . . . .
0 0 1 . 1 .
but that won't accomodate the next element, 3. Go back one step:
0 0 . . . .
0 0 . 1 . 1
The 3 won't fit in here, either. We've exhausted our search for that position of zeros, so let's take the next viable position of zeros:
. 0 0 . . .
There's only one way to place the one:
. 0 0 . . .
. 0 0 1 . 1
Now let's try to fit the 3 and, bingo!, it fits:
. 0 0 . . .
. 0 0 1 . 1
3 0 0 1 3 1
Now you can stop the search or try to find other solutions. In this case, there's only one other solution, namely the reflection of this one, but there are 300 ways to place the numbers from 1 to 8, foe example.
That approach is pretty much brute force, but in practice, there aren't many valid ways to fill the array, so that wrong paths are detected early. Perhaps placing the big numbers first gives better performance. You can play with that and measure it.
Here's a program that does that. (It probably looks more like C than Go. never mind.)
package main
import "fmt"
func fit_r(res[] int, a[] int, i int) int {
n := len(a);
if i == n {
fmt.Printf("%v\n", res);
return 1;
} else {
count := 0;
m := a[i];
for j := 0; j < 2*n - 1 - m; j++ {
if res[j] == -1 && res[j + 1 + m] == -1 {
// place values
res[j] = m;
res[j + 1 + m] = m;
// test further values
count += fit_r(res, a, i + 1);
// clean up and remove values again
res[j] = -1;
res[j + 1 + m] = -1;
}
}
return count;
}
}
func fit(a[] int) int {
res := make([] int, 2 * len(a));
for i := range res {
res[i] = -1;
}
return fit_r(res, a, 0);
}
func main() {
list := [] int {0, 1, 2, 3};
n := fit(list);
fmt.Println(n, "solutions");
}
Exponentiation by squaring is an algorithm that quickly computes an, where a and n are signed integers. (It does so in O(log n) multiplications).
Is there a similar algorithm, that instead computes (a / b)n, where a, b, and n are all unsigned integers? The problem with the obvious approach (i.e., computing an / bn) is that it will return wrong results due to integer overflow on the intermediate values.
I don't have floating points in the host language, only ints.
I'm okay with an approximate answer.
If you want excellent accuracy for the value of (a/b)^n, where a, b, and n are unsigned integers and you do not have floating point arithmetic available--use extended-precision integer calculations to find a^n and b^n, then divide the two.
Some languages, such as Python, have extended-precision integer arithmetic built in. If your language does not have it, look for a package that implements it. If you cannot do that, just make your own package. It is not that hard--such a package was an assignment in my second-semester computer science class back in the day. The multiplications and powers are fairly straightforward; the most difficult part is the division, even if you just want the quotient and remainder. But "most difficult" does not mean "very difficult" and you could probably do it. The second must difficult routine is printing the extended integer to decimal format.
The basic idea is to store each integer in an array or list of regular unsigned integers, where is integer is a "digit" in arithmetic with a large base. You want to be able to handle the product of any two digits, so if your machine has 32-bit integers and you have no way of handling 64-bit integers, store "digits" of 16 bits each. The larger the "digit" the faster the calculations. If your calculations are few and your printing to decimal is frequent, use a power of 10 such as 10000 for each "digit".
Ask if you need more detail.
Here's a pow implementation in fixed point based on Feynman's log algorithm. It's quick and somewhat dirty; C libraries tend to use a polynomial approximation, but that approach is more complicated, and I'm not sure how well it would translate to fixed point.
// powFraction approximates (a/b)**n.
func powFraction(a uint64, b uint64, n uint64) uint64 {
if a == 0 || b == 0 || a < b {
panic("powFraction")
}
return expFixed((logFixed(a) - logFixed(b)) * n)
}
// logFixed approximates 2**58 * log2(x). [Feynman]
func logFixed(x uint64) uint64 {
if x == 0 {
panic("logFixed")
}
// Normalize x into [2**63, 2**64).
n := numberOfLeadingZeros(x)
x <<= n
p := uint64(1 << 63)
y := uint64(0)
for k := uint(1); k <= 63; k++ {
// Warning: if q > x-p, then p + q may overflow.
if q := p >> k; q <= x-p {
p += q
y += table[k-1]
}
}
return uint64(63-n)<<58 + y>>6
}
// expFixed approximately inverts logFixed.
func expFixed(y uint64) uint64 {
n := 63 - uint(y>>58)
y <<= 6
p := uint64(1 << 63)
for k := uint(1); k <= 63; k++ {
if z := table[k-1]; z <= y {
p += p >> k
y -= z
}
}
return p >> n
}
// numberOfLeadingZeros returns the number of leading zeros in the word x.
// [Hacker's Delight]
func numberOfLeadingZeros(x uint64) uint {
n := uint(64)
if y := x >> 32; y != 0 {
x = y
n = 32
}
if y := x >> 16; y != 0 {
x = y
n -= 16
}
if y := x >> 8; y != 0 {
x = y
n -= 8
}
if y := x >> 4; y != 0 {
x = y
n -= 4
}
if y := x >> 2; y != 0 {
x = y
n -= 2
}
if x>>1 != 0 {
return n - 2
}
return n - uint(x)
}
// table[k-1] approximates 2**64 * log2(1 + 2**-k). [MPFR]
var table = [...]uint64{
10790653543520307104, // 1
5938525176524057593, // 2
3134563013331062591, // 3
1613404648504497789, // 4
818926958183105433, // 5
412613322424486499, // 6
207106307442936368, // 7
103754619509458805, // 8
51927872466823974, // 9
25976601570169168, // 10
12991470209511302, // 11
6496527847636937, // 12
3248462157916594, // 13
1624280643531991, // 14
812152713665686, // 15
406079454902306, // 16
203040501980337, // 17
101520444623942, // 18
50760270720599, // 19
25380147462480, // 20
12690076756788, // 21
6345039134781, // 22
3172519756487, // 23
1586259925518, // 24
793129974578, // 25
396564990243, // 26
198282495860, // 27
99141248115, // 28
49570624104, // 29
24785312063, // 30
12392656035, // 31
6196328018, // 32
3098164009, // 33
1549082005, // 34
774541002, // 35
387270501, // 36
193635251, // 37
96817625, // 38
48408813, // 39
24204406, // 40
12102203, // 41
6051102, // 42
3025551, // 43
1512775, // 44
756388, // 45
378194, // 46
189097, // 47
94548, // 48
47274, // 49
23637, // 50
11819, // 51
5909, // 52
2955, // 53
1477, // 54
739, // 55
369, // 56
185, // 57
92, // 58
46, // 59
23, // 60
12, // 61
6, // 62
3, // 63
}
Just in case someone is looking for a constant-space solution, I've kind of solved the issue with binomial expansions, which are a decent approximation. I'm using the following code:
// Computes `k * (1+1/q) ^ N`, with precision `p`. The higher
// the precision, the higher the gas cost. It should be
// something around the log of `n`. When `p == n`, the
// precision is absolute (sans possible integer overflows).
// Much smaller values are sufficient to get a great approximation.
function fracExp(uint k, uint q, uint n, uint p) returns (uint) {
uint s = 0;
uint N = 1;
uint B = 1;
for (uint i = 0; i < p; ++i){
s += k * N / B / (q**i);
N = N * (n-i);
B = B * (i+1);
}
return s;
}
Which simply computes the p first terms of the binomial expansion of (1 + r)^N, where r is a small positive real number. I posted a more thoughtful explanation at Ethereum Stack Exchange.
Given an infinite positive integer array or say a stream of positive integers, find out the first five numbers whose sum is twenty.
By reading the problem statement, it first seems to be 0-1 Knapsack problem, but I am confused that can 0-1 Knapsack algo be used on a stream of integers. Let suppose I write a recursive program for the above problem.
int knapsack(int sum, int count, int idx)
{
if (sum == 0 && count == 0)
return 1;
if ((sum == 0 && count != 0) || (sum != 0 && count == 0))
return 0;
if (arr[idx] > 20) //element cann't be included.
return knapsack(sum, count idx + 1);
return max(knapsack(sum, count, idx +1), knapsack(sum - arr[idx], count -1, idx + 1));
}
Now when the above function will call on an infinite array, the first call in max function i.e. knapsack(sum, count, idx +1) will never return as it will keep on ignoring the current element. Even if we change the order of the call in max function, there is still possibility that the first call will never return. Is there any way to apply knapsack algo in such scenarios?
This works if you are working with only positive integers.
Basically keep a list of ways you can reach any of the first 20 numbers and whenever you process a new number process this list accordingly.
def update(dictlist, num):
dk = dictlist.keys()
for i in dk:
if i+num <=20:
for j in dictlist[i]:
listlen = len(dictlist[i][j]) + 1
if listlen >5:
continue
if i+num not in dictlist or listlen not in dictlist[i+num]:
dictlist[i+num][listlen] = dictlist[i][j]+[num]
if num not in dictlist:
dictlist[num]= {}
dictlist[num][1] = [num]
return dictlist
dictlist = {}
for x in infinite_integer_stream:
dictlist = update(dictlist,x)
if 20 in dictlist and 5 in dictlist[20]:
print dictlist[20][5]
break
This code might have some minor bugs and I do not have time now to debug it. But basically dictlist[i][j] stores a j length list that sums to i.
Delphi code:
var
PossibleSums: array[1..4, 0..20] of Integer;
Value, i, j: Integer;
s: string;
begin
s := '';
for j := 1 to 4 do
for i := 0 to 20 do
PossibleSums[j, i] := -1;
while True do begin
Value := 1 + Random(20); // stream emulation
Memo1.Lines.Add(IntToStr(Value));
if PossibleSums[4, 20 - Value] <> -1 then begin
//we just have found 5th number to make the full sum
s := IntToStr(Value);
i := 20 - Value;
for j := 4 downto 1 do begin
//unwind storage chain
s := IntToStr(PossibleSums[j, i]) + ' ' + s;
i := i - PossibleSums[j, i];
end;
Memo1.Lines.Add(s);
Break;
end;
for j := 3 downto 1 do
for i := 0 to 20 - Value do
if (PossibleSums[j, i] <> -1) and (PossibleSums[j + 1, i + Value] = -1) then
PossibleSums[j + 1, i + Value] := Value;
if PossibleSums[1, Value] = -1 then
PossibleSums[1, Value] := Value;
end;
end;
output:
4
8
9
2
10
2
17
2
4 2 10 2 2
I know that there is an algorithm that permits, given a combination of number (no repetitions, no order), calculates the index of the lexicographic order.
It would be very useful for my application to speedup things...
For example:
combination(10, 5)
1 - 1 2 3 4 5
2 - 1 2 3 4 6
3 - 1 2 3 4 7
....
251 - 5 7 8 9 10
252 - 6 7 8 9 10
I need that the algorithm returns the index of the given combination.
es: index( 2, 5, 7, 8, 10 ) --> index
EDIT: actually I'm using a java application that generates all combinations C(53, 5) and inserts them into a TreeMap.
My idea is to create an array that contains all combinations (and related data) that I can index with this algorithm.
Everything is to speedup combination searching.
However I tried some (not all) of your solutions and the algorithms that you proposed are slower that a get() from TreeMap.
If it helps: my needs are for a combination of 5 from 53 starting from 0 to 52.
Thank you again to all :-)
Here is a snippet that will do the work.
#include <iostream>
int main()
{
const int n = 10;
const int k = 5;
int combination[k] = {2, 5, 7, 8, 10};
int index = 0;
int j = 0;
for (int i = 0; i != k; ++i)
{
for (++j; j != combination[i]; ++j)
{
index += c(n - j, k - i - 1);
}
}
std::cout << index + 1 << std::endl;
return 0;
}
It assumes you have a function
int c(int n, int k);
that will return the number of combinations of choosing k elements out of n elements.
The loop calculates the number of combinations preceding the given combination.
By adding one at the end we get the actual index.
For the given combination there are
c(9, 4) = 126 combinations containing 1 and hence preceding it in lexicographic order.
Of the combinations containing 2 as the smallest number there are
c(7, 3) = 35 combinations having 3 as the second smallest number
c(6, 3) = 20 combinations having 4 as the second smallest number
All of these are preceding the given combination.
Of the combinations containing 2 and 5 as the two smallest numbers there are
c(4, 2) = 6 combinations having 6 as the third smallest number.
All of these are preceding the given combination.
Etc.
If you put a print statement in the inner loop you will get the numbers
126, 35, 20, 6, 1.
Hope that explains the code.
Convert your number selections to a factorial base number. This number will be the index you want. Technically this calculates the lexicographical index of all permutations, but if you only give it combinations, the indexes will still be well ordered, just with some large gaps for all the permutations that come in between each combination.
Edit: pseudocode removed, it was incorrect, but the method above should work. Too tired to come up with correct pseudocode at the moment.
Edit 2: Here's an example. Say we were choosing a combination of 5 elements from a set of 10 elements, like in your example above. If the combination was 2 3 4 6 8, you would get the related factorial base number like so:
Take the unselected elements and count how many you have to pass by to get to the one you are selecting.
1 2 3 4 5 6 7 8 9 10
2 -> 1
1 3 4 5 6 7 8 9 10
3 -> 1
1 4 5 6 7 8 9 10
4 -> 1
1 5 6 7 8 9 10
6 -> 2
1 5 7 8 9 10
8 -> 3
So the index in factorial base is 1112300000
In decimal base, it's
1*9! + 1*8! + 1*7! + 2*6! + 3*5! = 410040
This is Algorithm 2.7 kSubsetLexRank on page 44 of Combinatorial Algorithms by Kreher and Stinson.
r = 0
t[0] = 0
for i from 1 to k
if t[i - 1] + 1 <= t[i] - 1
for j from t[i - 1] to t[i] - 1
r = r + choose(n - j, k - i)
return r
The array t holds your values, for example [5 7 8 9 10]. The function choose(n, k) calculates the number "n choose k". The result value r will be the index, 251 for the example. Other inputs are n and k, for the example they would be 10 and 5.
zero-base,
# v: array of length k consisting of numbers between 0 and n-1 (ascending)
def index_of_combination(n,k,v):
idx = 0
for p in range(k-1):
if p == 0: arrg = range(1,v[p]+1)
else: arrg = range(v[p-1]+2, v[p]+1)
for a in arrg:
idx += combi[n-a, k-1-p]
idx += v[k-1] - v[k-2] - 1
return idx
Null Set has the right approach. The index corresponds to the factorial-base number of the sequence. You build a factorial-base number just like any other base number, except that the base decreases for each digit.
Now, the value of each digit in the factorial-base number is the number of elements less than it that have not yet been used. So, for combination(10, 5):
(1 2 3 4 5) == 0*9!/5! + 0*8!/5! + 0*7!/5! + 0*6!/5! + 0*5!/5!
== 0*3024 + 0*336 + 0*42 + 0*6 + 0*1
== 0
(10 9 8 7 6) == 9*3024 + 8*336 + 7*42 + 6*6 + 5*1
== 30239
It should be pretty easy to calculate the index incrementally.
If you have a set of positive integers 0<=x_1 < x_2< ... < x_k , then you could use something called the squashed order:
I = sum(j=1..k) Choose(x_j,j)
The beauty of the squashed order is that it works independent of the largest value in the parent set.
The squashed order is not the order you are looking for, but it is related.
To use the squashed order to get the lexicographic order in the set of k-subsets of {1,...,n) is by taking
1 <= x1 < ... < x_k <=n
compute
0 <= n-x_k < n-x_(k-1) ... < n-x_1
Then compute the squashed order index of (n-x_k,...,n-k_1)
Then subtract the squashed order index from Choose(n,k) to get your result, which is the lexicographic index.
If you have relatively small values of n and k, you can cache all the values Choose(a,b) with a
See Anderson, Combinatorics on Finite Sets, pp 112-119
I needed also the same for a project of mine and the fastest solution I found was (Python):
import math
def nCr(n,r):
f = math.factorial
return f(n) / f(r) / f(n-r)
def index(comb,n,k):
r=nCr(n,k)
for i in range(k):
if n-comb[i]<k-i:continue
r=r-nCr(n-comb[i],k-i)
return r
My input "comb" contained elements in increasing order You can test the code with for example:
import itertools
k=3
t=[1,2,3,4,5]
for x in itertools.combinations(t, k):
print x,index(x,len(t),k)
It is not hard to prove that if comb=(a1,a2,a3...,ak) (in increasing order) then:
index=[nCk-(n-a1+1)Ck] + [(n-a1)C(k-1)-(n-a2+1)C(k-1)] + ... =
nCk -(n-a1)Ck -(n-a2)C(k-1) - .... -(n-ak)C1
There's another way to do all this. You could generate all possible combinations and write them into a binary file where each comb is represented by it's index starting from zero. Then, when you need to find an index, and the combination is given, you apply a binary search on the file. Here's the function. It's written in VB.NET 2010 for my lotto program, it works with Israel lottery system so there's a bonus (7th) number; just ignore it.
Public Function Comb2Index( _
ByVal gAr() As Byte) As UInt32
Dim mxPntr As UInt32 = WHL.AMT.WHL_SYS_00 '(16.273.488)
Dim mdPntr As UInt32 = mxPntr \ 2
Dim eqCntr As Byte
Dim rdAr() As Byte
modBinary.OpenFile(WHL.WHL_SYS_00, _
FileMode.Open, FileAccess.Read)
Do
modBinary.ReadBlock(mdPntr, rdAr)
RP: If eqCntr = 7 Then GoTo EX
If gAr(eqCntr) = rdAr(eqCntr) Then
eqCntr += 1
GoTo RP
ElseIf gAr(eqCntr) < rdAr(eqCntr) Then
If eqCntr > 0 Then eqCntr = 0
mxPntr = mdPntr
mdPntr \= 2
ElseIf gAr(eqCntr) > rdAr(eqCntr) Then
If eqCntr > 0 Then eqCntr = 0
mdPntr += (mxPntr - mdPntr) \ 2
End If
Loop Until eqCntr = 7
EX: modBinary.CloseFile()
Return mdPntr
End Function
P.S. It takes 5 to 10 mins to generate 16 million combs on a Core 2 Duo. To find the index using binary search on file takes 397 milliseconds on a SATA drive.
Assuming the maximum setSize is not too large, you can simply generate a lookup table, where the inputs are encoded this way:
int index(a,b,c,...)
{
int key = 0;
key |= 1<<a;
key |= 1<<b;
key |= 1<<c;
//repeat for all arguments
return Lookup[key];
}
To generate the lookup table, look at this "banker's order" algorithm. Generate all the combinations, and also store the base index for each nItems. (For the example on p6, this would be [0,1,5,11,15]). Note that by you storing the answers in the opposite order from the example (LSBs set first) you will only need one table, sized for the largest possible set.
Populate the lookup table by walking through the combinations doing Lookup[combination[i]]=i-baseIdx[nItems]
EDIT: Never mind. This is completely wrong.
Let your combination be (a1, a2, ..., ak-1, ak) where a1 < a2 < ... < ak. Let choose(a,b) = a!/(b!*(a-b)!) if a >= b and 0 otherwise. Then, the index you are looking for is
choose(ak-1, k) + choose(ak-1-1, k-1) + choose(ak-2-1, k-2) + ... + choose (a2-1, 2) + choose (a1-1, 1) + 1
The first term counts the number of k-element combinations such that the largest element is less than ak. The second term counts the number of (k-1)-element combinations such that the largest element is less than ak-1. And, so on.
Notice that the size of the universe of elements to be chosen from (10 in your example) does not play a role in the computation of the index. Can you see why?
Sample solution:
class Program
{
static void Main(string[] args)
{
// The input
var n = 5;
var t = new[] { 2, 4, 5 };
// Helping transformations
ComputeDistances(t);
CorrectDistances(t);
// The algorithm
var r = CalculateRank(t, n);
Console.WriteLine("n = 5");
Console.WriteLine("t = {2, 4, 5}");
Console.WriteLine("r = {0}", r);
Console.ReadKey();
}
static void ComputeDistances(int[] t)
{
var k = t.Length;
while (--k >= 0)
t[k] -= (k + 1);
}
static void CorrectDistances(int[] t)
{
var k = t.Length;
while (--k > 0)
t[k] -= t[k - 1];
}
static int CalculateRank(int[] t, int n)
{
int k = t.Length - 1, r = 0;
for (var i = 0; i < t.Length; i++)
{
if (t[i] == 0)
{
n--;
k--;
continue;
}
for (var j = 0; j < t[i]; j++)
{
n--;
r += CalculateBinomialCoefficient(n, k);
}
n--;
k--;
}
return r;
}
static int CalculateBinomialCoefficient(int n, int k)
{
int i, l = 1, m, x, y;
if (n - k < k)
{
x = k;
y = n - k;
}
else
{
x = n - k;
y = k;
}
for (i = x + 1; i <= n; i++)
l *= i;
m = CalculateFactorial(y);
return l/m;
}
static int CalculateFactorial(int n)
{
int i, w = 1;
for (i = 1; i <= n; i++)
w *= i;
return w;
}
}
The idea behind the scenes is to associate a k-subset with an operation of drawing k-elements from the n-size set. It is a combination, so the overall count of possible items will be (n k). It is a clue that we could seek the solution in Pascal Triangle. After a while of comparing manually written examples with the appropriate numbers from the Pascal Triangle, we will find the pattern and hence the algorithm.
I used user515430's answer and converted to python3. Also this supports non-continuous values so you could pass in [1,3,5,7,9] as your pool instead of range(1,11)
from itertools import combinations
from scipy.special import comb
from pandas import Index
debugcombinations = False
class IndexedCombination:
def __init__(self, _setsize, _poolvalues):
self.setsize = _setsize
self.poolvals = Index(_poolvalues)
self.poolsize = len(self.poolvals)
self.totalcombinations = 1
fast_k = min(self.setsize, self.poolsize - self.setsize)
for i in range(1, fast_k + 1):
self.totalcombinations = self.totalcombinations * (self.poolsize - fast_k + i) // i
#fill the nCr cache
self.choose_cache = {}
n = self.poolsize
k = self.setsize
for i in range(k + 1):
for j in range(n + 1):
if n - j >= k - i:
self.choose_cache[n - j,k - i] = comb(n - j,k - i, exact=True)
if debugcombinations:
print('testnth = ' + str(self.testnth()))
def get_nth_combination(self,index):
n = self.poolsize
r = self.setsize
c = self.totalcombinations
#if index < 0 or index >= c:
# raise IndexError
result = []
while r:
c, n, r = c*r//n, n-1, r-1
while index >= c:
index -= c
c, n = c*(n-r)//n, n-1
result.append(self.poolvals[-1 - n])
return tuple(result)
def get_n_from_combination(self,someset):
n = self.poolsize
k = self.setsize
index = 0
j = 0
for i in range(k):
setidx = self.poolvals.get_loc(someset[i])
for j in range(j + 1, setidx + 1):
index += self.choose_cache[n - j, k - i - 1]
j += 1
return index
#just used to test whether nth_combination from the internet actually works
def testnth(self):
n = 0
_setsize = self.setsize
mainset = self.poolvals
for someset in combinations(mainset, _setsize):
nthset = self.get_nth_combination(n)
n2 = self.get_n_from_combination(nthset)
if debugcombinations:
print(str(n) + ': ' + str(someset) + ' vs ' + str(n2) + ': ' + str(nthset))
if n != n2:
return False
for x in range(_setsize):
if someset[x] != nthset[x]:
return False
n += 1
return True
setcombination = IndexedCombination(5, list(range(1,10+1)))
print( str(setcombination.get_n_from_combination([2,5,7,8,10])))
returns 188