生成从最小到最大范围内均匀分布的整数列表

Generating a list of evenly distributed round numbers within a range from min to max

我正在尝试获取一个用数字填充的列,并使用最小值和最大值生成一个过滤范围列表,供用户过滤 table。使用天真的方法最终会导致难以处理数字范围,我真的宁愿拥有可以工作的整数,而不管 min/max 值如何。

我在好几个地方看到过这个问题,我认为到目前为止我找到的最佳答案是 Stuart Ainsworth's number table answer,但我希望步骤非常圆。

例如,如果我需要生成 0-100000 的 4 个范围,数字将为:

0     - 25000
25000 - 50000
50000 - 75000
75000 - 100000

但是,如果我的最小值和最大值是奇怪的数字,我可能会难以使用范围。

示例:如果我需要生成从 -1234 到 4321 的 5 个范围,则范围为:

-1234 - -123
-123  -  988
988   - 2099
2099  - 3210
3210  - 4321

我更喜欢这样的范围:

-1234 - 0          -1234 -  0          -1234 - -100
0     - 1000        0    -  1000       -100  -  1000
1000  - 2100        1000 -  2000        1000 -  2100
2100  - 3200        2000 -  3000        2100 -  3200
3200  - 4321        3000 -  4321        3200 -  4321

或类似的东西。范围的大小需要彼此 接近 ,但更重要的是数字易于浏览和使用。

这是一种计算覆盖范围所需的最小步长的方法,然后将其截断为最接近的 10 的幂的十分之一,其中包括步长,然后 returns 从最小开始到结束的步长点最大值,中间边界步长距离和外部边界略高以包含 min/max 值。

const range = (min, max, steps) => {
  // minimum step size
  stepsize = (max - min) / steps;
  // increase the step size to a nice boundary
  // for example, 1/10th of the 10^n range that includes it
  pow = Math.trunc(Math.log10(stepsize)) - 1;
  stepsize = Math.trunc(stepsize / 10 ** pow) * 10 ** pow;
  // round min to the same boundary
  result = [min];
  min = Math.trunc(min / 10 ** pow) * 10 ** pow;
  for (let i = 0; i < steps - 1; i++) {
    min += stepsize;
    result.push(min);
  }
  result.push(max);
  return result;
}

console.log(range(-1234, 4321, 5));

尼克的方法使用“算术级数”公式。这确实是解决这个问题的唯一方法。

以下解决方案考虑了@SpencerD 就单个算术级数如何无法获得所需结果发表的评论。这不是一个直线序列,除了以 1,100 递增的“中间”部分。

因此,我发布这个答案是为了证明如果分阶段分解,所有的算术级数仍然是可能的。其余解释在代码注释中。

function lowerBound(actualValue, boundIncrement) {
    
    // lets determine the min by an arithmetic progression formula
    
    return Math.floor( actualValue / boundIncrement ) * boundIncrement
    
    
}

function upperBound(actualValue, boundIncrement) {
    
    // lets determine the max by an arithmetic progression formula, hence the + 1 for the upper range
    
    return (Math.floor( actualValue / boundIncrement ) + 1) * boundIncrement
    
}

function getRanges(min, max) {
    let nextMin = min

    // now lets use the helper funcs to determine the ranges based on "would prefer ranges like" from question

    let ranges = [];


    // if 1st range is negative, lets make it that value up to 0
    if (min < 0) {
        ranges.push([min, 0]);
        nextMin = 0;
    }


    // 2nd range requires special condition that it be on increment of 1,000

    if ( max > nextMin) {

        
        // so lets do this without conditions
        
        ranges.push([nextMin, upperBound(nextMin, 1000)]);
        nextMin = upperBound(nextMin, 1000);

    }

    // lets determine if there are subsequent ranges, on increment of 1,100
        
    if (nextMin < max) {
        
        while ( nextMin < max) {
            ranges.push([nextMin, upperBound(nextMin, 1100)])
            nextMin = upperBound(nextMin, 1100)
            
        }
        
    }


    // lastly, lets make (overwrite) the max range's max with the max value instead
    ranges[ ranges.length - 1][1] = max

    return ranges;

}

function test() {

    console.log( getRanges( parseInt(document.getElementById("min").value), parseInt(document.getElementById("max").value) ) );
}
Enter min: <input type="text" id="min" value="-1234">
    <br>
    Enter max: <input type="text" id="max" value="4321">
    <br>
    <button onclick="test()">Console.log()</button>