2017-04-02 39 views
1

我最近遇到以下 问题:查找“平均”相等的上下距离的值一组给定

给定一组具有高度yᵢ点,找到行的高度,这以上的点的平均距离等于平均距离到线以下几点:

enter image description here

更抽象的定义:给定一组的实值的数据点Y = {Y 1,...,YN},发现将Y分成两组Y + = {y∈Y:y>ȳ }和Y - = {yεY:y<ȳ},使得⁺与Y +元素之间的平均距离等于ȳ与Y - 元素之间的平均距离。

天真的解决方案:用Y的平均值初始化,,计算平均上下距离并根据平均距离的上限还是下限更大来反复向上或向下移动。

问题:这个问题很基本,所以可能有更好的解决方案(?)即使是一个非迭代的代数算法?

+1

什么,其y坐标*等于*该ȳ点?那些不包含在你的集合Y +和Y - 中,所以它似乎把Y分解成三个集合,其中一个似乎被忽略了。如果它们包含在两组中,它们将改变平均距离。 –

+0

@RoryDaulton好点,任何解决方案都很好。因此,只要算法快速/简单,就包括一个,两个或不设置。 –

+1

如果您知道哪些点位于线条的上方和下方,因为存在间隙,那么这非常简单。但如果你不这样做,那么如果一个点在线上会发生什么? – maraca

回答

2

如在评论中提及,如果知道哪些点上方和线以下,则可以解决这样的:

一个=行

上面的b点的数目=行

SA = Y全部总和线

SB之上下面的点数=线以下所有的Y总和

现在我们可以建立以下关系式:

(sa - a * y)/a = (b * y - sb)/b    | * a * b 
sa * b - a * b * y = a * b * y - a * sb   | + a * b * y + a * sb 
sa * b + a * sb = 2 * a * b * y     |/(2 * a * b) 
==> y = (a * sb + b * sa)/(2 * a * b) 
     = sa/(2 * a) + sb/(2 * b) 
     = (sa/a + sb/b)/2 

如果我们interprete结果那么我们可以说这是上面和下面的线的点的平均值之间的平均水平。

+0

将您的答案与良好的初始估计和迭代更新相结合,与原始解决方案相比,算法稍有改进。 –

1

基于maraca's answer迭代求解:

初始化ȳ与所述给定值的平均值。

  1. 拆分给定值到上述下面ȳ那些
  2. 计算新的最优ȳ为此拆分。

重复直到ȳ收敛

这比问题中概述的算法略快。

// Find mean with equal average distance to upper and lower values: 
 
function findEqualAverageDistanceMean(values) { 
 
    let mean = values.reduce((a, b) => a + b)/values.length, 
 
     last = NaN; 
 

 
    // Iteratively equalize average distances: 
 
    while (last != mean) { 
 
    let lower_total = 0, 
 
     lower_n = 0, 
 
     upper_total = 0, 
 
     upper_n = 0; 
 

 
    for (let value of values) { 
 
     if (value > mean) { 
 
     upper_total += value; 
 
     ++upper_n; 
 
     } else if (value < mean) { 
 
     lower_total += value; 
 
     ++lower_n; 
 
     } 
 
    } 
 
    last = mean; 
 
    mean = (upper_total/upper_n + lower_total/lower_n)/2; 
 
    } 
 
    return mean; 
 
} 
 

 
// Example: 
 
let canvas = document.getElementById("canvas"), 
 
    ctx = canvas.getContext("2d"), 
 
    points = Array.from({length: 100},() => Math.random() ** 4), 
 
    mean = points.reduce((a, b) => a + b)/points.length, 
 
    equalAverageDistanceMean = findEqualAverageDistanceMean(points); 
 

 
function draw(points, mean, equalAverageDistanceMean) { 
 
    for (let [i, point] of points.entries()) { 
 
    ctx.fillStyle = (point < equalAverageDistanceMean) ? 'red' : 'green'; 
 
    ctx.fillRect(i * canvas.width/points.length, canvas.height * point, 3, 3); 
 
    } 
 
    ctx.fillStyle = 'black'; 
 
    ctx.fillRect(0, canvas.height * mean, canvas.width, .5); 
 
    ctx.fillRect(0, canvas.height * equalAverageDistanceMean, canvas.width, 3); 
 
} 
 

 
draw(points, mean, equalAverageDistanceMean);
<canvas id="canvas" width="400" height="200">