Skip to content

Commit

Permalink
factorial and combination
Browse files Browse the repository at this point in the history
  • Loading branch information
mzusin committed Apr 23, 2024
1 parent 671bf23 commit 6c7192f
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 19 deletions.
1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,5 @@ declare module 'mz-math' {
export const permutationsWithRepetition: (n: number, r: number) => number;
export const permutationsWithoutRepetition: (n: number, r: number) => number;
export const combinationsWithoutRepetition: (n: number, r: number) => number;
export const combinationsWithRepetition: (n: number, r: number) => number;
}
4 changes: 2 additions & 2 deletions dist/mz-math.esm.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/mz-math.esm.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/mz-math.min.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/mz-math.min.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/mz-math.node.cjs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions dist/mz-math.node.cjs.map

Large diffs are not rendered by default.

26 changes: 23 additions & 3 deletions src/main/combinatorics/combinatorics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export const combinationsWithoutRepetition = (n: number, r: number) : number =>
};

/**
* Combinations with repetitions:
* Combinations with repetitions (multi-choose):
* - "n" is the number of things to choose from
* - we choose "r" of them
* - order doesn't matter
Expand All @@ -145,8 +145,8 @@ export const combinationsWithoutRepetition = (n: number, r: number) : number =>
* We can have three scoops. How many variations will there be?
*
* Tabulation (Bottom-Up Dynamic Programming).
* Time Complexity:
* Space Complexity:
* Time Complexity: 𝑂((r + n) × r)
* Space Complexity: 𝑂((r + n) × r)
*/
export const combinationsWithRepetition = (n: number, r: number) : number => {
if (n < 0 || r < 0) {
Expand All @@ -156,6 +156,26 @@ export const combinationsWithRepetition = (n: number, r: number) : number => {
throw new Error('Both n and r should be integers.');
}

if(n === 0) return 1;

// Calculate adjusted values for indices in the DP table
const totalItems = r + n - 1;
const chooseItems = r;

// Initialize DP table
const dp: number[][] = Array.from({ length: totalItems + 1 }, () => Array(chooseItems + 1).fill(0));

// Base case initialization: C(i, 0) = 1 for all i, because there's one way to choose nothing
for (let i = 0; i <= totalItems; i++) {
dp[i][0] = 1;
}

for (let i = 1; i <= totalItems; i++) {
for (let j = 1; j <= Math.min(i, chooseItems); j++) {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
}
}

return dp[totalItems][chooseItems];
};

39 changes: 38 additions & 1 deletion test/combinatorics.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { permutationsWithRepetition, permutationsWithoutRepetition, combinationsWithoutRepetition } from '../src/main/combinatorics/combinatorics';
import {
permutationsWithRepetition,
permutationsWithoutRepetition,
combinationsWithoutRepetition,
combinationsWithRepetition,
} from '../src/main/combinatorics/combinatorics';

describe('Combinatorics', () => {

Expand Down Expand Up @@ -69,4 +74,36 @@ describe('Combinatorics', () => {
expect(() => combinationsWithoutRepetition(5, 2.7)).toThrow('Both n and r should be integers.');
});
});

describe('combinationsWithRepetition()', () => {
it('calculates simple combinations with repetition correctly', () => {
expect(combinationsWithRepetition(5, 3)).toBe(35);
expect(combinationsWithRepetition(3, 2)).toBe(6); // C(3+2-1, 2) = C(4, 2) = 6
expect(combinationsWithRepetition(4, 3)).toBe(20); // C(4+3-1, 3) = C(6, 3) = 20
});

it('returns 1 when r is 0, regardless of n', () => {
expect(combinationsWithRepetition(5, 0)).toBe(1); // C(5-1, 0) = C(4, 0) = 1
expect(combinationsWithRepetition(10, 0)).toBe(1); // C(10-1, 0) = C(9, 0) = 1
});

it('calculates combinations correctly when n is 1', () => {
expect(combinationsWithRepetition(1, 1)).toBe(1);
expect(combinationsWithRepetition(1, 5)).toBe(1); // C(1+5-1, 5) = C(5, 5) = 1
});

it('throws an error if n or r are negative', () => {
expect(() => combinationsWithRepetition(-1, 3)).toThrow('Both n and r should be non-negative integers.');
expect(() => combinationsWithRepetition(5, -1)).toThrow('Both n and r should be non-negative integers.');
});

it('handles edge cases for large n and small r', () => {
expect(combinationsWithRepetition(100, 1)).toBe(100); // C(100+1-1, 1) = C(100, 1) = 100
});

it('handles cases where n is 0', () => {
expect(combinationsWithRepetition(0, 0)).toBe(1); // C(0+0-1, 0) = C(-1, 0) = 1 (special handled)
expect(combinationsWithRepetition(0, 1)).toBe(1); // C(0+1-1, 1) = C(0, 1) = 1 (special handled)
});
});
});

0 comments on commit 6c7192f

Please sign in to comment.