Генерация списка равномерно распределенных круглых чисел в диапазоне от минимального до максимального

#javascript #math #range #sequence

#javascript #математика #диапазон #последовательность

Вопрос:

Я пытаюсь взять столбец, заполненный числами, и использовать значения min и max для создания списка диапазонов фильтров, по которым пользователь может фильтровать таблицу. Использование наивного подхода приводит к тому, что трудно обрабатывать диапазоны чисел, и я бы предпочел иметь круглые числа, которые могли бы работать независимо от значений min / max.

Я видел этот вопрос в нескольких местах и думаю, что лучший ответ, который я нашел до сих пор, — это ответ таблицы чисел Стюарта Эйнсворта, но я хотел бы иметь очень круглые шаги.

Например, если мне нужно сгенерировать 4 диапазона от 0 до 100000, числа будут:

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

Однако, если мои min и max являются нечетными числами, мне может быть неудобно использовать диапазоны.

Пример: если мне нужно сгенерировать 5 диапазонов от -1234 до 4321, диапазоны будут:

 -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
 

Или что-то подобное. Диапазоны должны быть близки по размеру друг к другу, но гораздо важнее, чтобы числа было легко просматривать и использовать.

Комментарии:

1. «Арифметическая прогрессия» решит эту проблему. Очевидно, пользовательская функция, которая принимает значение и возвращает нужное вам значение. Может потребоваться две функции. Один для нижней границы значения и один для верхней. Я вскоре опубликую решение, если смогу заставить его работать

2. @GetSet, не так ли? Разве арифметическая прогрессия не была бы фиксированным диапазоном, в то время как OP проявил интерес к размытию чисел / диапазона, чтобы легче усваивать значения минимумов и максимумов.

3. @SpencerD тем не менее, некоторые вспомогательные функции помогут. Я начал работать над решением, и оно «параметризует» «связанный прирост», поэтому гибкость все еще возможна

4. Судя по «предпочел бы такие диапазоны», 1-й диапазон min — это само минимальное значение. А максимальное значение последнего диапазона — это само максимальное значение. 2-й диапазон min и max составляет порядка 1000. Последующие диапазоны составляют порядка 1100. Выполнимо с помощью «арифметической прогрессии» @SpencerD поэтапно

Ответ №1:

Вот подход, который вычисляет минимальный размер шага, необходимый для покрытия диапазона, затем усекает его до ближайшей десятой степени от 10, которая включает размер шага, а затем возвращает точки шага, начиная с 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 resu<
}

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

Ответ №2:

Подход Ника использует формулу «арифметической прогрессии». Это действительно единственный способ решить эту проблему.

Следующее решение учитывает то, что прокомментировал @SpencerD с точки зрения того, как одна арифметическая прогрессия не даст желаемых результатов. Это не прямая последовательность, за исключением «средних» частей с шагом 1100.

Таким образом, я публикую этот ответ, чтобы продемонстрировать, что все еще возможно с арифметической прогрессией, если разбивать ее поэтапно. Оставшееся объяснение содержится в комментариях к коду.

 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>