生成从最小到最大范围内均匀分布的整数列表
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>
我正在尝试获取一个用数字填充的列,并使用最小值和最大值生成一个过滤范围列表,供用户过滤 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>