我想从(反向)线性分布值得到一个随机的毫秒值(如果我得到正确的术语)。
本质上,我希望在两个时间点t
和Date
之间有一个随机的时间点early
(在我的情况下为late
),其中t
1}}朝向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();
}
答案 0 :(得分:3)
将this answer的小猪退回到类似的问题,您可以至少接听两个兰特电话:
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中减去平方根以反转分布,即将更大的密度放在范围的底部。
答案 1 :(得分:1)
accepted Answer by Parker似乎是正确的并且做得很好。
该问题使用过时的麻烦的日期时间类,这些类现在是遗留的,由java.time类取代。下面是相同类型的代码,以及Parker的解决方案,在java.time中重写。
Instant
首先,如果您必须使用java.util.Date
个对象,请转换为Instant
。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。要转换,请查看添加到旧类的新方法。
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
类以总秒数加上小数秒(以纳秒为单位)表示这样的跨度。
Duration duration = Duration.between( early , late ) ;
要进行随机数学运算,我们需要一个整数。要处理纳秒分辨率,我们需要64-bit long
而不是32-bit int
。
ThreadLocalRandom
提示:如果跨线程生成这些值,请使用类ThreadLocalRandom
。引用文档:
如果适用,在并发程序中使用ThreadLocalRandom而不是共享Random对象通常会遇到更少的开销和争用。
我们可以通过调用ThreadLocalRandom::nextLong( origin , bound )
以半开放式样式指定范围,其中原点为包含,界限为独占。
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而不是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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和&amp; SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和fz more。
答案 2 :(得分:0)
也许您可以将类比应用于日期,如本答案中所示。