找到具有与给定集合的值相等的上下距离的“平均值”

时间:2017-04-02 21:23:02

标签: algorithm math distance

我最近遇到了以下情况 问题:

给定一组高度为yᵢ的点,找到与上面的点之间的平均距离等于线下方点的平均距离的线的高度:

enter image description here

更抽象的定义:给定一组实值数据点Y = {y1,...,yn},找到将Y分成两组的ȳY⁺= {y∈Y:y&gt; ȳ}和Y - = {y∈Y:y < ȳ}所以ȳ和Y elements元素之间的平均距离等于ȳ和Y - 元素之间的平均距离。

朴素解决方案:用Y的平均值初始化,,计算平均上下距离,并根据上下平均距离是否更大来反复上下移动。

问题:这个问题非常基础,所以可能有更好的解决方案(?)甚至是非迭代代数算法?

2 个答案:

答案 0 :(得分:2)

正如评论中提到的,如果您知道哪些点在线上方和下方,那么您可以这样解决:

a =线上方的点数

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

如果我们解释结果,那么我们可以说这是该线上下线点的平均值之间的平均值。

答案 1 :(得分:1)

基于maraca's answer的迭代解决方案:

使用给定值的平均值初始化。。

  1. 将指定值拆分为以上 以下
  2. 计算此分割的新优化。。
  3. 重复,直到ȳ收敛

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

    // 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">