frac/frac.md

126 lines
2.9 KiB
Markdown
Raw Normal View History

2013-12-14 07:11:37 +00:00
# Target
In all languages, the target is a function that takes 3 parameters:
- `x` the number we wish to approximate
- `D` the maximum denominator
- `mixed` if true, return a mixed fraction (default); if false, improper
The JS implementation walks through the algorithm.
# JS Implementation
In this version, the return value is `[quotient, numerator, denominator]`,
where `quotient == 0` for improper fractions. The interpretation is
`x ~ quotient + numerator / denominator` where `0 <= numerator < denominator`
and `quotient <= x` for negative `x`.
```js>frac.js
var frac = function(x, D, mixed) {
```
The goal is to maintain a feasible fraction (with bounded denominator) below
the target and another fraction above the target. The lower bound is
`floor(x) / 1` and the upper bound is `(floor(x) + 1) / 1`. We keep track of
the numerators and denominators separately:
```
var n1 = Math.floor(x), d1 = 1;
var n2 = n1+1, d2 = 1;
```
If `x` is not integral, we bisect using mediants until a denominator exceeds
our target:
```
if(x !== n1) while(d1 <= D && d2 <= D) {
```
The mediant is the sum of the numerators divided by the sum of demoninators:
```
var m = (n1 + n2) / (d1 + d2);
```
If we happened to stumble upon the exact value, then we choose the closer one
(the mediant if the denominator is within bounds, or the bound with the larger
denominator)
```
if(x === m) {
if(d1 + d2 <= D) d1+=d2, n1+=n2, d2=D+1;
else if(d1 > d2) d2=D+1;
else d1=D+1;
break;
}
```
Otherwise shrink the range:
```
else if(x < m) n2 = n1+n2, d2 = d1+d2;
else n1 = n1+n2, d1 = d1+d2;
}
```
At this point, `d1 > D` or `d2 > D` (but not both -- keep track of how `d1` and
`d2` change). So we merely return the desired values:
```
if(d1 > D) d1 = d2, n1 = n2;
if(!mixed) return [0, n1, d1];
var q = Math.floor(n1/d1);
return [q, n1 - q*d1, d1];
};
```
Finally we put some export jazz:
```
if(typeof module !== undefined) module.exports = frac;
```
# Tests
```js>test.js
var frac;
describe('source', function() { it('should load', function() { frac = require('./'); }); });
```
# Miscellany
```make>Makefile
frac.js: frac.md
voc frac.md
.PHONY: test
test:
mocha -R spec
```
## Node Ilk
```json>package.json
{
"name": "frac",
"version": "0.1.0",
"author": "SheetJS",
"description": "Rational approximation with bounded denominator",
"keywords": [ "math", "fraction", "rational", "approximation" ],
"main": "./frac.js",
"dependencies": {},
"devDependencies": {"mocha":""},
"repository": {
"type":"git",
"url": "git://github.com/SheetJS/frac.git"
},
"scripts": {
"test": "make test"
},
"bugs": { "url": "https://github.com/SheetJS/frac/issues" },
"engines": { "node": ">=0.8" }
}
```