Лучший способ для создания списка <Double> Последовательность значений дается начало, конец и шаг?

NanoWizard:

Я на самом деле очень удивлен , что я не смог найти ответ на этот вопрос здесь, хотя , возможно , я просто использовать неправильно условие поиска или что - то. Ближайший я смог найти это , но они просят о создании определенного диапазона doubleс с конкретным размером шага, и ответы рассматривать его как таковой. Мне нужно что - то , что будет генерировать числа с произвольным началом, концом и размером шага.

Я полагаю , что там должен быть какой - то способ , как это в библиотеке где - то уже, но если это так , я не смог найти его легко (опять же , может быть , я просто используя неправильные условия поиска или что - то). Так вот что я приготовил сам по себе в течение последних нескольких минут , чтобы сделать это:

import java.lang.Math;
import java.util.List;
import java.util.ArrayList;

public class DoubleSequenceGenerator {


     /**
     * Generates a List of Double values beginning with `start` and ending with
     * the last step from `start` which includes the provided `end` value.
     **/
    public static List<Double> generateSequence(double start, double end, double step) {
        Double numValues = (end-start)/step + 1.0;
        List<Double> sequence = new ArrayList<Double>(numValues.intValue());

        sequence.add(start);
        for (int i=1; i < numValues; i++) {
          sequence.add(start + step*i);
        }

        return sequence;
    }

    /**
     * Generates a List of Double values beginning with `start` and ending with
     * the last step from `start` which includes the provided `end` value.
     * 
     * Each number in the sequence is rounded to the precision of the `step`
     * value. For instance, if step=0.025, values will round to the nearest
     * thousandth value (0.001).
     **/
    public static List<Double> generateSequenceRounded(double start, double end, double step) {

        if (step != Math.floor(step)) {
            Double numValues = (end-start)/step + 1.0;
            List<Double> sequence = new ArrayList<Double>(numValues.intValue());

            double fraction = step - Math.floor(step);
            double mult = 10;
            while (mult*fraction < 1.0) {
                mult *= 10;
            }

            sequence.add(start);
            for (int i=1; i < numValues; i++) {
              sequence.add(Math.round(mult*(start + step*i))/mult);
            }

            return sequence;
        }

        return generateSequence(start, end, step);
    }

}

Эти методы выполнение простого цикла умножения stepна индексе последовательности и добавление к startсмещению. Это уменьшает компаундирования ошибки с плавающей точкой , которое будет происходить с непрерывным приращением (например, добавив stepк переменной на каждой итерации).

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

Обратите внимание , что я намеренно исключил логику для обработки «ненормальные» аргументов , таких как Infinity, NaN, start> end, или отрицательного stepразмера для простоты и желание сосредоточиться на вопрос под руку.

Вот некоторые примеры использования и соответствующий вывод:

System.out.println(DoubleSequenceGenerator.generateSequence(0.0, 2.0, 0.2))
System.out.println(DoubleSequenceGenerator.generateSequenceRounded(0.0, 2.0, 0.2));
System.out.println(DoubleSequenceGenerator.generateSequence(0.0, 102.0, 10.2));
System.out.println(DoubleSequenceGenerator.generateSequenceRounded(0.0, 102.0, 10.2));
[0.0, 0.2, 0.4, 0.6000000000000001, 0.8, 1.0, 1.2000000000000002, 1.4000000000000001, 1.6, 1.8, 2.0]
[0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0]
[0.0, 10.2, 20.4, 30.599999999999998, 40.8, 51.0, 61.199999999999996, 71.39999999999999, 81.6, 91.8, 102.0]
[0.0, 10.2, 20.4, 30.6, 40.8, 51.0, 61.2, 71.4, 81.6, 91.8, 102.0]

Есть ли существующая библиотека, которая предоставляет такую ​​функциональность уже?

Если нет, то есть ли какие-либо проблемы с моим подходом?

Кто-нибудь есть лучший подход к этому?

Евгений Khyst:

Последовательности могут быть легко получены с использованием Java 11 Стрит API.

Простой подход заключается в использовании DoubleStream:

public static List<Double> generateSequenceDoubleStream(double start, double end, double step) {
  return DoubleStream.iterate(start, d -> d <= end, d -> d + step)
      .boxed()
      .collect(toList());
}

На диапазонах с большим числом итераций, doubleточность ошибка может накапливаться в результате большей ошибки ближе к концу диапазона. Ошибка может быть сведено к минимуму за счет перехода к IntStreamи используя целые числа и одну двойную множитель:

public static List<Double> generateSequenceIntStream(int start, int end, int step, double multiplier) {
  return IntStream.iterate(start, i -> i <= end, i -> i + step)
      .mapToDouble(i -> i * multiplier)
      .boxed()
      .collect(toList());
}

Для того, чтобы избавиться от doubleточности ошибки на всех, BigDecimalмогут быть использованы:

public static List<Double> generateSequenceBigDecimal(BigDecimal start, BigDecimal end, BigDecimal step) {
  return Stream.iterate(start, d -> d.compareTo(end) <= 0, d -> d.add(step))
      .mapToDouble(BigDecimal::doubleValue)
      .boxed()
      .collect(toList());
}

Примеры:

public static void main(String[] args) {
  System.out.println(generateSequenceDoubleStream(0.0, 2.0, 0.2));
  //[0.0, 0.2, 0.4, 0.6000000000000001, 0.8, 1.0, 1.2, 1.4, 1.5999999999999999, 1.7999999999999998, 1.9999999999999998]

  System.out.println(generateSequenceIntStream(0, 20, 2, 0.1));
  //[0.0, 0.2, 0.4, 0.6000000000000001, 0.8, 1.0, 1.2000000000000002, 1.4000000000000001, 1.6, 1.8, 2.0]

  System.out.println(generateSequenceBigDecimal(new BigDecimal("0"), new BigDecimal("2"), new BigDecimal("0.2")));
  //[0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0]
}

Метод итерация с этой подписью (3 параметра) был добавлен в Java 9. Итак, для Java 8 кода выглядит

DoubleStream.iterate(start, d -> d + step)
    .limit((int) (1 + (end - start) / step))

рекомендация

отhttp://10.200.1.11:23101/article/api/json?id=435139&siteId=1
рекомендация