0
# Mathematical Utilities
1
2
Root finding, special functions, and numerical utilities for advanced mathematical operations.
3
4
## Core Imports
5
6
```typescript
7
import {
8
bisect,
9
factorial,
10
gamma,
11
gammaln,
12
sign,
13
numericSort,
14
quickselect,
15
epsilon,
16
combinations,
17
combinationsReplacement,
18
permutationsHeap,
19
equalIntervalBreaks
20
} from "simple-statistics";
21
```
22
23
## Root Finding
24
25
### bisect { .api }
26
27
```typescript { .api }
28
function bisect(
29
func: (x: any) => number,
30
start: number,
31
end: number,
32
maxIterations: number,
33
errorTolerance: number
34
): number;
35
```
36
37
Finds roots of a function using the bisection method - a robust numerical algorithm for finding where f(x) = 0.
38
39
**Parameters:**
40
- `func: (x: any) => number` - Function to find the root of
41
- `start: number` - Left boundary of search interval
42
- `end: number` - Right boundary of search interval
43
- `maxIterations: number` - Maximum number of iterations
44
- `errorTolerance: number` - Acceptable error tolerance
45
46
**Returns:** `number` - Root value where f(x) ≈ 0
47
48
**Requirements:** Function must be continuous and f(start) and f(end) must have opposite signs.
49
50
```typescript
51
import { bisect } from "simple-statistics";
52
53
// Find where x² - 4 = 0 (should be x = 2)
54
const quadratic = (x: number) => x * x - 4;
55
const root = bisect(quadratic, 0, 5, 100, 0.0001);
56
console.log(`Root of x² - 4 = 0: ${root}`); // ≈ 2.0000
57
58
// Find break-even point for business model
59
const profit = (price: number) => (price - 10) * (100 - price) - 200;
60
const breakEven = bisect(profit, 5, 50, 100, 0.01);
61
console.log(`Break-even price: $${breakEven.toFixed(2)}`);
62
```
63
64
## Special Functions
65
66
### factorial { .api }
67
68
```typescript { .api }
69
function factorial(n: number): number;
70
```
71
72
Calculates the factorial of a non-negative integer (n!).
73
74
**Parameters:**
75
- `n: number` - Non-negative integer
76
77
**Returns:** `number` - n! = n × (n-1) × ... × 2 × 1
78
79
```typescript
80
import { factorial } from "simple-statistics";
81
82
const fact5 = factorial(5); // 120
83
const fact0 = factorial(0); // 1 (by definition)
84
85
console.log(`5! = ${fact5}`);
86
87
// Combinatorics: number of ways to arrange 8 people
88
const arrangements = factorial(8); // 40,320
89
console.log(`Ways to arrange 8 people: ${arrangements.toLocaleString()}`);
90
```
91
92
### gamma { .api }
93
94
```typescript { .api }
95
function gamma(n: number): number;
96
```
97
98
Calculates the gamma function, which extends factorials to real numbers.
99
100
**Parameters:**
101
- `n: number` - Input value
102
103
**Returns:** `number` - Γ(n) value
104
105
**Properties:**
106
- Γ(n) = (n-1)! for positive integers
107
- Γ(1/2) = √π
108
- Γ(n+1) = n × Γ(n)
109
110
```typescript
111
import { gamma } from "simple-statistics";
112
113
const gamma4 = gamma(4); // 6 (same as 3!)
114
const gammaHalf = gamma(0.5); // √π ≈ 1.772
115
116
console.log(`Γ(4) = ${gamma4}`);
117
console.log(`Γ(0.5) = ${gammaHalf.toFixed(3)}`);
118
119
// Used in probability distributions
120
const betaFunction = (a: number, b: number) => (gamma(a) * gamma(b)) / gamma(a + b);
121
console.log(`Beta(2,3) = ${betaFunction(2, 3).toFixed(3)}`);
122
```
123
124
### gammaln { .api }
125
126
```typescript { .api }
127
function gammaln(n: number): number;
128
```
129
130
Calculates the natural logarithm of the gamma function. More numerically stable for large values.
131
132
**Parameters:**
133
- `n: number` - Input value
134
135
**Returns:** `number` - ln(Γ(n))
136
137
```typescript
138
import { gammaln } from "simple-statistics";
139
140
// For large values, use log form to avoid overflow
141
const largeGammaLn = gammaln(100); // ln(Γ(100))
142
console.log(`ln(Γ(100)) = ${largeGammaLn.toFixed(2)}`);
143
144
// Convert back if needed (for smaller results)
145
const largeGamma = Math.exp(largeGammaLn);
146
console.log(`Γ(100) ≈ ${largeGamma.toExponential(3)}`);
147
```
148
149
## Utility Functions
150
151
### sign { .api }
152
153
```typescript { .api }
154
function sign(x: number): number;
155
```
156
157
Returns the sign of a number.
158
159
**Parameters:**
160
- `x: number` - Input number
161
162
**Returns:** `number`
163
- 1 if x > 0
164
- -1 if x < 0
165
- 0 if x = 0
166
167
```typescript
168
import { sign } from "simple-statistics";
169
170
console.log(`sign(5) = ${sign(5)}`); // 1
171
console.log(`sign(-3) = ${sign(-3)}`); // -1
172
console.log(`sign(0) = ${sign(0)}`); // 0
173
174
// Direction of price movement
175
const priceChange = -2.5;
176
const direction = sign(priceChange) === 1 ? "up" : sign(priceChange) === -1 ? "down" : "flat";
177
console.log(`Price moved ${direction}`);
178
```
179
180
### numericSort { .api }
181
182
```typescript { .api }
183
function numericSort(array: number[]): number[];
184
```
185
186
Sorts an array of numbers in ascending order using proper numeric comparison.
187
188
**Parameters:**
189
- `array: number[]` - Array of numbers to sort
190
191
**Returns:** `number[]` - New sorted array (original array unchanged)
192
193
```typescript
194
import { numericSort } from "simple-statistics";
195
196
const unsorted = [10, 2, 30, 4, 5];
197
const sorted = numericSort(unsorted);
198
console.log(`Sorted: ${sorted}`); // [2, 4, 5, 10, 30]
199
200
// Proper numeric sorting vs. lexicographic
201
const badSort = [10, 2, 30, 4, 5].sort(); // ["10", "2", "30", "4", "5"] - wrong!
202
const goodSort = numericSort([10, 2, 30, 4, 5]); // [2, 4, 5, 10, 30] - correct!
203
```
204
205
### quickselect { .api }
206
207
```typescript { .api }
208
function quickselect<T>(array: T[], k: number, compare?: (a: T, b: T) => number): T;
209
```
210
211
Finds the k-th smallest element in an array using the quickselect algorithm (faster than full sorting).
212
213
**Parameters:**
214
- `array: T[]` - Array to search in
215
- `k: number` - Index of element to find (0-based)
216
- `compare?: (a: T, b: T) => number` - Optional comparison function
217
218
**Returns:** `T` - The k-th smallest element
219
220
```typescript
221
import { quickselect } from "simple-statistics";
222
223
const data = [7, 3, 1, 9, 2, 8, 5];
224
225
// Find median (middle element) without full sorting
226
const median = quickselect(data, Math.floor(data.length / 2));
227
console.log(`Median: ${median}`); // 5
228
229
// Find 3rd smallest element
230
const thirdSmallest = quickselect(data, 2); // 0-based index
231
console.log(`3rd smallest: ${thirdSmallest}`); // 3
232
233
// Custom comparison for objects
234
const people = [{age: 25}, {age: 30}, {age: 20}, {age: 35}];
235
const secondYoungest = quickselect(people, 1, (a, b) => a.age - b.age);
236
console.log(`Second youngest age: ${secondYoungest.age}`); // 25
237
```
238
239
### epsilon { .api }
240
241
```typescript { .api }
242
const epsilon: number;
243
```
244
245
Machine epsilon - the smallest representable positive number such that 1 + epsilon ≠ 1 in floating-point arithmetic.
246
247
```typescript
248
import { epsilon } from "simple-statistics";
249
250
console.log(`Machine epsilon: ${epsilon}`);
251
252
// Use for floating-point comparisons
253
function almostEqual(a: number, b: number): boolean {
254
return Math.abs(a - b) < epsilon * Math.max(Math.abs(a), Math.abs(b));
255
}
256
257
console.log(`0.1 + 0.2 === 0.3: ${0.1 + 0.2 === 0.3}`); // false
258
console.log(`0.1 + 0.2 ≈ 0.3: ${almostEqual(0.1 + 0.2, 0.3)}`); // true
259
```
260
261
## Combinatorics
262
263
### combinations { .api }
264
265
```typescript { .api }
266
function combinations<T>(array: T[], k: number): T[][];
267
```
268
269
Generates all possible combinations of k elements from an array.
270
271
**Parameters:**
272
- `array: T[]` - Source array
273
- `k: number` - Number of elements in each combination
274
275
**Returns:** `T[][]` - Array of all possible combinations
276
277
```typescript
278
import { combinations } from "simple-statistics";
279
280
const fruits = ['apple', 'banana', 'cherry', 'date'];
281
const pairs = combinations(fruits, 2);
282
283
console.log("All fruit pairs:");
284
pairs.forEach(pair => console.log(pair.join(' + ')));
285
// apple + banana, apple + cherry, apple + date, banana + cherry, etc.
286
287
// Team selection
288
const players = ['Alice', 'Bob', 'Charlie', 'David', 'Eve'];
289
const teams = combinations(players, 3);
290
console.log(`Possible 3-person teams: ${teams.length}`);
291
```
292
293
### combinationsReplacement { .api }
294
295
```typescript { .api }
296
function combinationsReplacement<T>(array: T[], k: number): T[][];
297
```
298
299
Generates combinations with replacement - elements can be repeated.
300
301
**Parameters:**
302
- `array: T[]` - Source array
303
- `k: number` - Number of elements in each combination
304
305
**Returns:** `T[][]` - Array of combinations with replacement
306
307
```typescript
308
import { combinationsReplacement } from "simple-statistics";
309
310
const dice = [1, 2, 3, 4, 5, 6];
311
const twoRolls = combinationsReplacement(dice, 2);
312
313
console.log("Possible dice combinations (with replacement):");
314
console.log(`Total combinations: ${twoRolls.length}`); // 21 combinations
315
```
316
317
### permutationsHeap { .api }
318
319
```typescript { .api }
320
function permutationsHeap<T>(array: T[]): T[][];
321
```
322
323
Generates all permutations of an array using Heap's algorithm.
324
325
**Parameters:**
326
- `array: T[]` - Source array
327
328
**Returns:** `T[][]` - Array of all permutations
329
330
```typescript
331
import { permutationsHeap } from "simple-statistics";
332
333
const letters = ['A', 'B', 'C'];
334
const perms = permutationsHeap(letters);
335
336
console.log("All permutations of ABC:");
337
perms.forEach(perm => console.log(perm.join('')));
338
// ABC, ACB, BAC, BCA, CAB, CBA
339
340
console.log(`Total permutations: ${perms.length}`); // 6 = 3!
341
```
342
343
## Data Binning
344
345
### equalIntervalBreaks { .api }
346
347
```typescript { .api }
348
function equalIntervalBreaks(values: number[], nClasses: number): number[];
349
```
350
351
Creates equal-width intervals for data binning and histogram creation.
352
353
**Parameters:**
354
- `values: number[]` - Data values
355
- `nClasses: number` - Number of intervals/classes
356
357
**Returns:** `number[]` - Array of break points defining intervals
358
359
```typescript
360
import { equalIntervalBreaks, min, max } from "simple-statistics";
361
362
// Age distribution binning
363
const ages = [22, 25, 28, 31, 35, 42, 48, 52, 58, 61, 67, 73];
364
const ageBreaks = equalIntervalBreaks(ages, 4);
365
366
console.log("Age group breaks:", ageBreaks);
367
// Example: [22, 34.75, 47.5, 60.25, 73]
368
369
// Create age groups
370
const ageGroups = [];
371
for (let i = 0; i < ageBreaks.length - 1; i++) {
372
const group = ages.filter(age => age >= ageBreaks[i] && age < ageBreaks[i + 1]);
373
ageGroups.push({
374
range: `${ageBreaks[i]}-${ageBreaks[i + 1]}`,
375
count: group.length,
376
ages: group
377
});
378
}
379
380
console.log("Age distribution:");
381
ageGroups.forEach(group => {
382
console.log(`${group.range}: ${group.count} people`);
383
});
384
```
385
386
## Usage Examples
387
388
### Numerical Analysis
389
390
```typescript
391
import { bisect, gamma, factorial } from "simple-statistics";
392
393
// Solve engineering problems numerically
394
class EngineeringCalculator {
395
// Find optimal pipe diameter for fluid flow
396
static findOptimalDiameter(flowRate: number, maxPressureDrop: number): number {
397
// Darcy-Weisbach equation simplified
398
const pressureDrop = (diameter: number) => {
399
const velocity = flowRate / (Math.PI * diameter * diameter / 4);
400
return 0.02 * (1 / diameter) * velocity * velocity * 1000 - maxPressureDrop;
401
};
402
403
return bisect(pressureDrop, 0.1, 2.0, 100, 0.001);
404
}
405
406
// Calculate reliability using gamma function
407
static weibullReliability(time: number, shape: number, scale: number): number {
408
return Math.exp(-Math.pow(time / scale, shape));
409
}
410
411
// Quality control calculations
412
static binomialCoefficient(n: number, k: number): number {
413
return factorial(n) / (factorial(k) * factorial(n - k));
414
}
415
}
416
417
// Usage examples
418
const optimalDiameter = EngineeringCalculator.findOptimalDiameter(0.5, 100);
419
console.log(`Optimal pipe diameter: ${optimalDiameter.toFixed(3)} m`);
420
421
const reliability = EngineeringCalculator.weibullReliability(1000, 2, 1500);
422
console.log(`Reliability after 1000 hours: ${(reliability * 100).toFixed(1)}%`);
423
```
424
425
### Combinatorial Optimization
426
427
```typescript
428
import { combinations, permutationsHeap, factorial } from "simple-statistics";
429
430
// Traveling salesman problem (small scale)
431
class TSPSolver {
432
private distances: number[][];
433
434
constructor(cities: string[], distanceMatrix: number[][]) {
435
this.distances = distanceMatrix;
436
}
437
438
// Brute force solution for small instances
439
solveExact(cities: string[]): { route: string[], distance: number } {
440
const routes = permutationsHeap(cities.slice(1)); // Fix first city
441
let bestRoute = cities;
442
let bestDistance = Infinity;
443
444
for (const route of routes) {
445
const fullRoute = [cities[0], ...route];
446
const distance = this.calculateRouteDistance(fullRoute);
447
448
if (distance < bestDistance) {
449
bestDistance = distance;
450
bestRoute = fullRoute;
451
}
452
}
453
454
return { route: bestRoute, distance: bestDistance };
455
}
456
457
private calculateRouteDistance(route: string[]): number {
458
let total = 0;
459
for (let i = 0; i < route.length - 1; i++) {
460
// Simplified - would need city name to index mapping
461
total += Math.random() * 100; // Placeholder
462
}
463
return total;
464
}
465
}
466
467
// Portfolio optimization - select best k stocks from n options
468
function selectOptimalPortfolio(stocks: any[], k: number): any[][] {
469
const portfolioCombinations = combinations(stocks, k);
470
471
// Evaluate each combination (simplified)
472
return portfolioCombinations.map(portfolio => ({
473
stocks: portfolio,
474
expectedReturn: portfolio.reduce((sum, stock) => sum + stock.expectedReturn, 0) / k,
475
risk: Math.sqrt(portfolio.reduce((sum, stock) => sum + stock.variance, 0) / k)
476
}));
477
}
478
```
479
480
### Scientific Computing
481
482
```typescript
483
import { gamma, gammaln, bisect } from "simple-statistics";
484
485
// Statistical distributions using special functions
486
class Distributions {
487
// Beta distribution probability density function
488
static betaPDF(x: number, alpha: number, beta: number): number {
489
if (x < 0 || x > 1) return 0;
490
491
const logBeta = gammaln(alpha) + gammaln(beta) - gammaln(alpha + beta);
492
return Math.exp((alpha - 1) * Math.log(x) + (beta - 1) * Math.log(1 - x) - logBeta);
493
}
494
495
// Find quantiles using bisection
496
static betaQuantile(p: number, alpha: number, beta: number): number {
497
const cdf = (x: number) => {
498
// Simplified incomplete beta function - would need proper implementation
499
return this.betaCDF(x, alpha, beta) - p;
500
};
501
502
return bisect(cdf, 0.001, 0.999, 100, 0.0001);
503
}
504
505
private static betaCDF(x: number, alpha: number, beta: number): number {
506
// Placeholder - would need proper incomplete beta implementation
507
return Math.pow(x, alpha) * Math.pow(1 - x, beta);
508
}
509
}
510
511
// Physics simulations
512
class PhysicsUtils {
513
// Stirling's approximation for large factorials
514
static stirlingApproximation(n: number): number {
515
return Math.sqrt(2 * Math.PI * n) * Math.pow(n / Math.E, n);
516
}
517
518
// Compare with actual gamma function
519
static compareStirling(n: number): void {
520
const actual = gamma(n + 1); // Γ(n+1) = n!
521
const approx = this.stirlingApproximation(n);
522
const error = Math.abs(actual - approx) / actual * 100;
523
524
console.log(`n=${n}: Actual=${actual.toExponential(3)}, Stirling=${approx.toExponential(3)}, Error=${error.toFixed(2)}%`);
525
}
526
}
527
528
// Test accuracy
529
[5, 10, 20, 50].forEach(n => PhysicsUtils.compareStirling(n));
530
```