I want to calculate the length of the subsegments of a line having a length of totalLineLength
, where the subsegments are proportional to the log of the events given.
A visual representation: |---|-----|---------| a line composed of subsegments.
I want the sum of the calculated subsegments to be equal to totalLineLength
, and Success
must be printed. Log(x+1)
must be used too (because it gives always positive results for positive arguments and scales 1 too).
Using linear scaling, I'm doing this, and it works.
var totalLineLength = 20;
var eventTotal;
var calcSubSegment = function(x) {
return totalLineLength * x / eventTotal;
}
var events = [0.1, 1, 22];
eventTotal = events.reduce((a, b) => a + b, 0);
var subSegments = events.map(calcSubSegment);
var segmentLength = subSegments.reduce((a, b) => a + b);
if (segmentLength != totalLineLength) {
console.log("Error:", segmentLength);
} else {
console.log("Success");
}
Using log(x+1) (calcSubSegment is updated), it doesn't work.
var totalLineLength = 20;
var eventTotal;
var calcSubSegment = function(x) {
return totalLineLength * Math.log(x+1) / Math.log(eventTotal+1);
}
var events = [0.1, 1, 22];
eventTotal = events.reduce((a, b) => a + b, 0);
var subSegments = events.map(calcSubSegment);
var segmentLength = subSegments.reduce((a, b) => a + b, 0);
if (segmentLength != totalLineLength) {
console.log("Error:", segmentLength);
} else {
console.log("Success");
}
What's wrong with my code? I suppose the eventTotal calculation, but I'm not sure how to proceed.
How should I use logarithmic scaling? I'd also like a mathematical explanation.
If I understand your task correctly, the simplest solution is first map
all events
using your log(x+1)
functions and then just use linear split you've already implemented. Something like this:
function linearSplit(events, totalLineLength) {
var eventTotal;
var calcSubSegment = function(x) {
return totalLineLength * x / eventTotal;
}
eventTotal = events.reduce((a, b) => a + b, 0);
var subSegments = events.map(calcSubSegment);
return subSegments;
}
function logSplit(events, totalLineLength) {
var logEvents = events.map(function(x) { return Math.log(x+1); } );
return linearSplit(logEvents, totalLineLength);
}
function testSplit(events, totalLineLength, fnSplit, splitName) {
var subSegments = fnSplit(events, totalLineLength);
var segmentLength = subSegments.reduce((a, b) => a + b, 0);
console.log(splitName + ":\n" + events + " =>\n" + subSegments);
if (Math.abs(segmentLength - totalLineLength) > 0.001) {
console.log(splitName + " Error:", segmentLength);
} else {
console.log(splitName + " Success");
}
}
var totalLineLength = 20;
var events = [0.1, 1, 22];
testSplit(events, totalLineLength, linearSplit, "linearSplit");
testSplit(events, totalLineLength, logSplit, "logSplit");
The idea is that the only splitting that
at the same time is the linear splitting. So if you want to scale coefficients in any way, you should scale them before passing to the linear splitting logic. Otherwise the sum of parts will not equal the whole (in case of any non-linear scaling) as it happens in your code.
P.S. It is not very good idea to compare floating point values by ==
. You should use some tolerance for calculation/rounding errors.