Java 生成列表的最佳方法<;双倍>;给定开始、结束和步骤的值序列?

Java 生成列表的最佳方法<;双倍>;给定开始、结束和步骤的值序列?,java,double,sequence,Java,Double,Sequence,事实上,我很惊讶我在这里找不到答案,尽管我可能只是使用了错误的搜索词或其他东西。我能找到的最接近的是,但他们询问如何生成具有特定步长的特定范围的doubles,答案就是这样。我需要的东西,将生成任意开始,结束和步长的数字 我想在某个图书馆的某个地方已经有了这样的方法,但如果是这样的话,我就不能很容易地找到它(再说一遍,也许我只是使用了错误的搜索词或其他东西)。这是我在过去几分钟里自己做的: import java.lang.Math; 导入java.util.List; 导入java.util.

事实上,我很惊讶我在这里找不到答案,尽管我可能只是使用了错误的搜索词或其他东西。我能找到的最接近的是,但他们询问如何生成具有特定步长的特定范围的
double
s,答案就是这样。我需要的东西,将生成任意开始,结束和步长的数字

我想在某个图书馆的某个地方已经有了这样的方法,但如果是这样的话,我就不能很容易地找到它(再说一遍,也许我只是使用了错误的搜索词或其他东西)。这是我在过去几分钟里自己做的:

import java.lang.Math;
导入java.util.List;
导入java.util.ArrayList;
公共类双序列发生器{
/**
*生成一个双值列表,以“开始”开头,以“结束”
*“开始”的最后一步,包括提供的“结束”值。
**/
公共静态列表生成序列(双开始、双结束、双步骤){
双数值=(结束-开始)/步骤+1.0;
列表序列=新的ArrayList(numValues.intValue());
顺序。添加(开始);
for(int i=1;i
这些方法运行一个简单的循环,将
步骤
乘以序列索引并添加到
开始
偏移量。这减轻了连续递增(例如在每次迭代中将
步骤
添加到一个变量中)可能出现的复合浮点错误

我添加了
generateSequenceRounded
方法,用于分数步长可能导致明显浮点错误的情况。它确实需要更多的算法,因此在像我们这样对性能非常敏感的情况下,如果不需要舍入,可以选择使用更简单的方法。我怀疑在大多数通用用例中,舍入开销可以忽略不计

请注意,我故意排除了处理“异常”参数的逻辑,例如
无穷大
NaN
开始
结束
,或者一个负的
步长
大小,以简化操作并希望专注于手头的问题

下面是一些示例用法和相应的输出:

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));
现有的库是否已经提供了这种功能

如果没有,我的方法是否存在任何问题


有人有更好的方法吗?

我个人认为,我会将DoubleSequenceGenerator的分类缩短一点,以便于其他用途,并且只使用一种序列生成器方法,该方法包含使用所需精度或完全不使用精度的选项:

在下面的生成器方法中,如果未向可选的setPrecision参数提供任何值(或任何小于0的值),则不执行十进制精度舍入。如果为精度值提供了0,则将数字四舍五入至最接近的整数数字(即:89.674四舍五入至90.0)。如果提供的特定精度值大于0,则值将转换为该十进制精度

这里使用BigDecimal表示……嗯……精度:

import java.util.List;
import java.util.ArrayList;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class DoubleSequenceGenerator {

     public static List<Double> generateSequence(double start, double end, 
                                          double step, int... setPrecision) {
        int precision = -1;
        if (setPrecision.length > 0) {
            precision = setPrecision[0];
        }
        List<Double> sequence = new ArrayList<>();
        for (double val = start; val < end; val+= step) {
            if (precision > -1) {
                sequence.add(BigDecimal.valueOf(val).setScale(precision, RoundingMode.HALF_UP).doubleValue());
            }
            else {
                sequence.add(BigDecimal.valueOf(val).doubleValue());
            }
        }
        if (sequence.get(sequence.size() - 1) < end) { 
            sequence.add(end); 
        }
        return sequence;
    }    

    // Other class goodies here ....
}
控制台显示:

[0.0, 0.2, 0.4, 0.6000000000000001, 0.8, 1.0, 1.2, 1.4, 1.5999999999999999, 1.7999999999999998, 1.9999999999999998, 2.0]
[0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 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.2, 71.4, 81.60000000000001, 91.80000000000001, 102.0]
[0.0, 10.0, 20.0, 31.0, 41.0, 51.0, 61.0, 71.0, 82.0, 92.0, 102.0]
[0.0, 10.2, 20.4, 30.6, 40.8, 51.0, 61.2, 71.4, 81.6, 91.8, 102.0]

使用Java11流API可以很容易地生成序列

简单的方法是使用
双流

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
精度错误,可以使用
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());
}
在Java9中添加了具有此签名(3个参数)的方法。因此,对于Java8,代码如下所示

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

public static List<Double> generateSequenceRounded(double start, double end, double step) {
    long mult = (long) Math.pow(10, BigDecimal.valueOf(step).scale());
    return DoubleStream.iterate(start, d -> (double) Math.round(mult * (d + step)) / mult)
                .limit((long) (1 + (end - start) / step)).boxed().collect(Collectors.toList());
}
返回此BigDecimal的小数位数。如果为零或正,则刻度为小数点右侧的位数。如果为负数,则数字的未标度值乘以10,即标度的负数幂。例如,-3表示未缩放的值乘以1000

大体上

和输出:

[0.0, 10.2, 20.4, 30.6, 40.8, 51.0, 61.2, 71.4, 81.6, 91.8, 102.0]
[0.0, 10.24367, 20.48734, 30.73101, 40.97468, 51.21835, 61.46202, 71.70569, 81.94936, 92.19303]
  • 现有的库是否已经提供了这种功能

    对不起,我不知道,但从其他答案来看,它们相对简单——不,没有。不需要。嗯,几乎

  • 如果没有,我的方法是否存在任何问题

    是和否。您至少有一个bug,还有一些性能提升的空间,但方法本身是正确的

  • 您的错误:舍入错误(只需将
    while(mult*fraction<1.0)
    更改为
    while(mult*fraction<10.0)
    ,这应该可以解决它)
  • 所有其他的都没有到达
    。。。嗯,也许他们只是没有足够的观察力来阅读代码中的注释
  • <
    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]
    }
    
    DoubleStream.iterate(start, d -> d + step)
        .limit((int) (1 + (end - start) / step))
    
    public static List<Double> generateSequenceRounded(double start, double end, double step) {
        long mult = (long) Math.pow(10, BigDecimal.valueOf(step).scale());
        return DoubleStream.iterate(start, d -> (double) Math.round(mult * (d + step)) / mult)
                    .limit((long) (1 + (end - start) / step)).boxed().collect(Collectors.toList());
    }
    
    int java.math.BigDecimal.scale()
    
    System.out.println(generateSequenceRounded(0.0, 102.0, 10.2));
    System.out.println(generateSequenceRounded(0.0, 102.0, 10.24367));
    
    [0.0, 10.2, 20.4, 30.6, 40.8, 51.0, 61.2, 71.4, 81.6, 91.8, 102.0]
    [0.0, 10.24367, 20.48734, 30.73101, 40.97468, 51.21835, 61.46202, 71.70569, 81.94936, 92.19303]
    
        public static List<Double> genNoRoundDirectToDouble(double start, double end, double step) {
        int len = (int)Math.ceil((end-start)/step) + 1;
        var sequence = new ArrayList<Double>(len);
        sequence.add(start);
        for (int i=1 ; i < len ; ++i) sequence.add(start + step*i);
        return sequence;
        }
    
    public static List<Double> gen_DoubleStream_presice(double start, double end, double step) {
        return IntStream.range(0, (int)Math.ceil((end-start)/step) + 1)
            .mapToDouble(i -> start + i * step)
            .boxed()
            .collect(Collectors.toList());
    }
    
    double[] sequence = new double[len];
    for (int i=1; i < len; ++i) sequence[i] = start + step*i;
    List<Double> list = new ArrayList<Double>(len);
    list.add(start);
    for (int i=1; i < len; ++i) list.add(Math.round(sequence[i])/mult);
    return list;