线性排列算法

时间:2016-04-19 15:07:02

标签: javascript

我不确定标题是否正确。

我有几个标签在y范围内设置了它们的位置:

range = [0, 100px]

例如:5个标签位置:

positions = [5px, 6px, 8px, 72px, 76px]

现在我希望我的算法能够纠正这些位置,使它们彼此之间的距离不超过10px,并进行最小的修正。

我期待这样调用我的函数:

result = calculateNewPositions(range, positions, min(10px, 100px / positions.length))

并且这种情况的结果应该是:

[0px, 10px, 20px, 69px, 79px]

这个alghoritm的名称是什么或如何实现?

4 个答案:

答案 0 :(得分:2)

这是一种适用于大多数情况的算法,并且尝试根据原始值进行必要的最小调整。

  1. 遍历每对元素。
  2. 如果空间不够大,请将它们彼此分开1,确保不要违反范围。
  3. 重复,直到所有元素之间有足够的空间。
  4. 这是一个示例实现:

    function calculateNewPositions(positions, minSpacing, rangeMin, rangeMax) {
      var temp = positions.slice(0);
      var madeChange;
      do {
        madeChange = false;
        for (var i = 0; i < temp.length - 1; i++)
          if (temp[i + 1] - temp[i] < minSpacing) {
            if (temp[i] > rangeMin) { temp[i]--; madeChange = true; }
            if (temp[i + 1] < rangeMax) { temp[i + 1]++; madeChange = true; }
          }
      } while (madeChange);
      return temp;
    }
    

    演示:https://jsfiddle.net/aaxmuw2t/

    示例结果:[0,10,20,69,79]

    请注意,此算法非常简单,并且对于具有大量紧密数字的真正复杂数组而言,可能无法始终产生最佳结果。例如,如果您输入[33, 34, 35, 36],则会获得[19, 29, 40, 50],这会占用额外的空间。

答案 1 :(得分:1)

calculateNewPositions = function(positions, minDelta) {
   var newPositions = [0]
   positions.slice(1).forEach(function(pos, index) {
     var delta = positions[index + 1] - positions[index]
     newPositions.push(newPositions[index] + Math.max(delta, minDelta))
   })
   return newPositions
}

https://tonicdev.com/lipp/pos-diff

答案 2 :(得分:1)

我终于做了这样的事情:

var fixPositions = function(range, pos, delta, strict) {
  var i;
  var leftSpaces = [];
  var halfDelta = strict ? delta / 2 : 0;
  delta = Math.min(delta, (range[1] - range[0] / (pos.length + (strict ? 0 : 1))));

  // calculate all spaces that are greater than delta
  leftSpaces.push(Math.max(pos[0] - range[0] - halfDelta, 0));
  for (i = 1; i < pos.length; i++) {
    leftSpaces.push(Math.max(pos[i] - pos[i-1] - delta, 0));
  }
  leftSpaces.push(Math.max(range[1] - pos[pos.length-1] - halfDelta, 0));

  // save indexes of big spaces
  var nonZeroSpacesIdx = [];
  leftSpaces.map(function(space, i) {
    if (space > 0) {
      nonZeroSpacesIdx.push(i);
    }
  });

  // sort indexes by spaces sizes (start from smaller)
  nonZeroSpacesIdx.sort(function(a, b) {
    return leftSpaces[a] - leftSpaces[b];
  });

  // loop until spaces sum are greater than range
  var spacesSum = Infinity;
  while (nonZeroSpacesIdx.length > 0 && spacesSum > 0) {
    spacesSum = 0;
    for (i = 0; i < nonZeroSpacesIdx.length; i++) {
      spacesSum += leftSpaces[nonZeroSpacesIdx[i]];
    }
    var missingDiff = (spacesSum + (pos.length - 1) * delta + halfDelta * 2) - (range[1] - range[0]);

    if (missingDiff <= 0) {
      break;
    }
    // find min diff which can be substracted from all spaces
    var minDiff = Math.min(missingDiff / nonZeroSpacesIdx.length, leftSpaces[nonZeroSpacesIdx[0]]);
    for (i = 0; i < nonZeroSpacesIdx.length; i++) {
      leftSpaces[nonZeroSpacesIdx[i]] -= minDiff;
    }
    // remove index of first space if its equal zero
    if (leftSpaces[nonZeroSpacesIdx[0]] <= 0) {
      nonZeroSpacesIdx.shift();
    }
  }

  // reconstruct new positions
  var newPos = [];
  newPos.push(range[0] + leftSpaces[0] + halfDelta);
  for (i = 1; i < leftSpaces.length - 1; i++) {
    newPos[i] = newPos[i-1] + leftSpaces[i] + delta;
  }

  return newPos;
};

// result should be from range: [5, 95]
console.log(fixPositions([0, 100], [5, 6, 8, 72, 76], 10, true));

// result should be from range: [0, 100]
console.log(fixPositions([0, 100], [5, 6, 8, 72, 76], 10, false));

https://jsfiddle.net/fcwu1oyu/14/

它没有为我的输入提供完全相同的值,但它完成了我的饼图的工作:

enter image description here enter image description here

答案 3 :(得分:0)

正在进行的工作解决方案

代码推动两个太近的情侣appart,每边一个。这是对称的,并且有时会导致很大的推动值,这可以得到纠正。

&#13;
&#13;
function disperse(values, threshold, range) {
    var delta = Array.apply(null, { length: values.length }).map(function () { return 0; }),
        converged = false;

    while (!converged) {
        converged = true;
        delta = delta.map(function (d, i, dd) {
            if (i < dd.length - 1 && dd.length > 1 && values[i + 1] + dd[i + 1] - values[i] - d < threshold) {
                converged = false;
                dd[i + 1] += 1;
                return d - 1;
            }
            return d;
        });
    }
    converged = false;

    // try to minimise difference
    while (!converged) {
        converged = true;
        delta = delta.map(function (d, i) {
            var space;
            if (i < delta.length - 2) {
                space = values[i + 1] + delta[i + 1] - values[i] - d;
                if (d < 0 && space > threshold) {
                    converged = false;
                    return d + space - threshold;
                }
            }
            return d;
        });
    }

    // respect lower range
    delta.reduce(function (r, d, i, dd) {
        if (values[i] + d < r) {
            dd[i] = r - values[i];
            return r + threshold;
        }
        return values[i] + threshold + d;
    }, range[0]);

    // respect upper range
    delta.reduceRight(function (r, d, i, dd) {
        if (values[i] + d > r) {
            dd[i] = r - values[i];
            return r - threshold;
        }
        return values[i] + d;
    }, range[1]);

    return values.map(function (v, i) {
        return v + delta[i];
    });
}

document.write('<pre>' + JSON.stringify(disperse([5, 6, 8, 72, 76], 10, [0, 100]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(disperse([5, 6, 7, 8, 72, 76], 10, [0, 100]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(disperse([24, 28, 92, 94, 95], 10, [0, 100]), 0, 4) + '</pre>');
&#13;
&#13;
&#13;