2.9 KiB
Target
In all languages, the target is a function that takes 3 parameters:
x
the number we wish to approximateD
the maximum denominatormixed
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
.
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
var frac;
describe('source', function() { it('should load', function() { frac = require('./'); }); });
Miscellany
frac.js: frac.md
voc frac.md
.PHONY: test
test:
mocha -R spec
Node Ilk
{
"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" }
}