如何获得具有线性分布的随机日期?

How to get a random Date with linear distribution?

我想从值的(逆)线性分布中得到一个随机毫秒值(如果我没记错的话)。

本质上我想在两个时间点earlylate之间有一个随机时间点t(在我的例子中是Datet 朝向 early 的概率比朝向 late 的概率大得多。 late本身可能有0.0的概率。

我目前的java代码只使用均匀分布,所以我打算将其修改为(逆)线性分布:

public Date getRandomDate(Date early, Date late) {
  long diff = late.getTime() - early.getTime();
  final int randVal = rand.nextInt((int) diff);
  Calendar cal = Calendar.getInstance();
  cal.setTime(early);
  cal.add(Calendar.MILLISECOND, randVal);
  return cal.getTime();
}

搭便车 this answer 到一个类似的问题,你可以只调用两个 rand 调用中的最小值:

final int randVal = Math.min(rand.nextInt((int) diff), rand.nextInt((int) diff));

最后,这是另一种使用 cumulative distribution function (x^2) 求解 x 的更复杂的方法:

int randVal = (int) Math.floor(diff * (1.0 - Math.sqrt(rand.nextDouble())));
if(randVal >= diff) randVal = 0; // handle the edge case

为了满足您指定的要求,已从 1.0 中减去平方根以反转分布,即将较大的密度放在范围的底部。

也许您可以将类比应用于此答案中所示的日期。

Java:非均匀分布的随机整数

似乎是正确的,做得很好。

使用java.time

该问题使用了过时且麻烦的日期时间 classes,现在已成为遗留问题,已被 java.time classes 取代。这是相同类型的代码,连同帕克的解决方案,在 java.time.

中重写

Instant

首先,如果您必须使用 java.util.Date 个对象,请转换 to/from InstantInstant class represents a moment on the timeline in UTC with a resolution of nanoseconds(最多九 (9) 位小数)。要转换,请查看添加到旧 classes 的新方法。

Instant instant = myJavaUtilDate.toInstant();  // From legacy to modern class.
java.util.Date myJavaUtilDate = java.util.Date.from( instant ) ;  // From modern class to legacy.

让我们重写方法签名,但传递和返回 Instant 个对象。

public Instant getRandomDate( Instant early , Instant late) {

验证early参数确实早于later参数。或者,断言下面看到的 Duration 不是负数 ( ! duration.isNegative() ).

    if( early.isAfter( late) ) { … }  // Assert `early` is not after `late`.

半开

计算最早时刻和最晚时刻之间的差值。这是在通常用于定义时间跨度的半开放方法中完成的,其中开始是 包含 ,结尾是 不包含

Duration

Duration class 以总秒数加上以纳秒为单位的小数秒表示这样的跨度。

    Duration duration = Duration.between( early , late ) ;

要进行随机数学运算,我们需要一个整数。要处理纳秒分辨率,我们需要 64-bit long rather than a 32-bit int

ThreadLocalRandom

提示:如果跨线程生成这些值,请使用 class ThreadLocalRandom。引用文档:

When applicable, use of ThreadLocalRandom rather than shared Random objects in concurrent programs will typically encounter much less overhead and contention.

我们可以通过调用 [=51] 以 inclusiveexclusive 为起点指定半开样式的范围=].

    long bound = duration.toNanos() ;
    long nanos1 = ThreadLocalRandom.current().nextLong( 0 , bound ); 
    long nanos2 = ThreadLocalRandom.current().nextLong( 0 , bound ); 
    long nanos = Math.min( nanos1 , nanos2 );  // Select the lesser number.
    Instant instant = early.plusNanos( nanos );
    return instant ;
}

实例

参见code below run live at IdeOne.com

我们提取为每个仅限日期 (LocalDate) 生成的日期时间值的数量,作为调查结果的一种随意方式,以验证我们期望的结果是否偏向较早的日期。

测试工具向您展示了如何将时区 (ZoneId) 分配给 Instant 以获得 ZonedDateTime 对象,并从那里提取 LocalDate .如果您希望通过某个特定区域的 wall-clock time rather than in UTC.

镜头查看 Instant 对象,请将其用作指南
/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

import java.util.concurrent.ThreadLocalRandom ;
import java.util.TreeMap ;

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Ideone app = new Ideone();
        app.doIt();
    }

    public void doIt() {
        ZoneId z = ZoneId.of( "America/Montreal" ) ;
        int count = 10 ;
        LocalDate today = LocalDate.now( z );
        LocalDate laterDate = today.plusDays( count );
        Instant start = today.atStartOfDay( z ).toInstant();
        Instant stop = laterDate.atStartOfDay( z ).toInstant();

        // Collect the frequency of each date. We want to see bias towards earlier dates.
        List<LocalDate> dates = new ArrayList<>( count );
        Map<LocalDate , Integer > map = new TreeMap<LocalDate , Integer >();
        for( int i = 0 ; i <= count ; i ++ ) {
            LocalDate localDate = today.plusDays( i ) ; 
            dates.add( localDate );  // Increment to next date and remember.
            map.put( localDate , new Integer( 0 ) ); // Prepopulate the map with all dates.
        }
        for( int i = 1 ; i <= 10_000 ; i ++ ) {
            Instant instant = this.getRandomInstantBetween( start , stop );
            LocalDate localDate = instant.atZone( z ).toLocalDate();
            Integer integer = map.get( localDate );
            map.put( localDate , integer +  1);  // Increment to count each time get a hit on this date.
        }
        System.out.println( map );
    }

    public Instant getRandomInstantBetween( Instant early , Instant late) {

        Duration duration = Duration.between( early , late ) ;
        // Assert the duration is positive or zero: ( ! duration.isNegative() )

        long bound = duration.toNanos() ;
        ThreadLocalRandom random = ThreadLocalRandom.current() ;
        long nanos1 = random.nextLong( 0 , bound ); // Zero means the `early` date is inclusive, while `bound` here is exclusive.
        long nanos2 = random.nextLong( 0 , bound ); 
        long nanos = Math.min( nanos1 , nanos2 );  // Select the lesser number.
        Instant instant = early.plusNanos( nanos );

        return instant;
    }
}

这是一些示例结果。这些对我来说看起来不错,但我不是统计学家。使用风险自负。

{2017-02-24=1853, 2017-02-25=1697, 2017-02-26=1548, 2017-02-27=1255, 2017-02-28=1130, 2017-03-01=926, 2017-03-02=706, 2017-03-03=485, 2017-03-04=299, 2017-03-05=101, 2017-03-06=0}

{2017-02-25=930, 2017-02-26=799, 2017-02-27=760, 2017-02-28=657, 2017-03-01=589, 2017-03-02=470, 2017-03-03=342, 2017-03-04=241, 2017-03-05=163, 2017-03-06=49, 2017-03-07=0}

{2017-02-25=878, 2017-02-26=875, 2017-02-27=786, 2017-02-28=676, 2017-03-01=558, 2017-03-02=440, 2017-03-03=370, 2017-03-04=236, 2017-03-05=140, 2017-03-06=41, 2017-03-07=0}


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

从哪里获得java.time classes?

  • Java SE 8 and SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and SE 7
  • Android
    • ThreeTenABP 项目专门为 Android 改编 ThreeTen-Backport(如上所述)。
    • 参见

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, andfz more.