使用对数缩放计算线的子段

Calculating the subsegments of a line, using logarithmic scaling

我想计算长度为 totalLineLength 的线的子段长度,其中子段与给定事件的日志成正比。

视觉表示:|---|-----|--------|由子段组成的一行。

我要计算的子段之和等于totalLineLength,必须打印SuccessLog(x+1) 也必须使用(因为它总是对正参数和标度 1 给出正结果)。

使用线性缩放,我正在这样做,并且有效。

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");
}

使用 log(x+1)(calcSubSegment 已更新),它不起作用。

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");
}

我的代码有什么问题?我想 eventTotal 计算,但我不确定如何进行。

我应该如何使用对数缩放?我也想要一个数学解释。

如果我正确理解您的任务,最简单的解决方案是首先 map 所有 events 使用您的 log(x+1) 函数,然后仅使用您已经实现的线性拆分。像这样:

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"); 

这个想法是唯一的分裂

  1. 具有与某些系数成比例的部分
  2. 使所有部分的总和等于整体

同时是线性分裂。所以如果你想以任何方式缩放系数,你应该在传递给线性分裂逻辑之前缩放它们。否则,部分之和将不等于整体(在任何非线性缩放的情况下),就像您的代码中发生的那样。

P.S。用 == 比较浮点值不是一个好主意。您应该对 calculation/rounding 错误使用一定的容忍度。

处理对数的重要规则:log(a)+log(b) = log(a*b)。因此,为了计算总长度,我们需要找到各个数字的乘积而不是它们的总和。

  1. 您所做的是显示 log(a)+log(b) != log(a+b),因此请尝试将数字相乘而不是相加。

  2. 使用 中描述的误差范围,因为浮点运算的工作方式。

var totalLineLength = 20;
var eventTotal;
var calcSubSegment = function(x) {
    return totalLineLength * Math.log(x+1) / Math.log(eventTotal);
}

var events = [0.1, 1, 22];
eventTotal = events.reduce((a, b) => a * (b+1), 1);
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", subSegments);
}