2014-03-31 2 views
3

Joda 시간 간격 (임의이지만 일정한 간격)에서 완전 연속 기간 수를 계산하려고합니다.Joda 시간 간격의 기간 수

내가 선형 검색 while 루프를 사용하고 함께 왔어요 간단한 해결책 :

public static long periodsInAnInterval(Interval interval, Period period) { 
    int periods = -1; 
    DateTime marker = interval.getStart(); 
    while (marker.isBefore(interval.getEnd()) || marker.isEqual(interval.getEnd())) { 
     marker = marker.plus(period); 
     periods++; 
    } 
    return periods; 
} 

오 (n)이 솔루션은 분명 꽤 무서운, 그래서 사람이 더 나은 방법을 생각할 수 있습니까? 나는 여기에

테스트 케이스의 ... 이진 검색의 어떤 종류를 사용할 수 있는지 궁금 해서요 : https://gist.github.com/Mahoney/9899832

편집 - 초 알려진 수없는 기간을 기억; Period.toStandardDuration()은 년이 365 일, 월이 30 일, 일이 24 시간이라고 가정 할 때의 대략적인 수치입니다. (실제로 빠른 테스트는 Period.toStandardDuration 폭탄이 그 기간에 몇 년 또는 몇 달이있는 경우 예외가 있음을 보여줍니다.)

편집 2 - 첫 번째 기간이 해당 간격의 시작에서 시작한다고 가정하는 것이 좋습니다. 그렇지 않으면 남은 시간이 시작, 끝 또는 둘 다인지에 따라 대답이 달라질 수 있습니다.

+0

'long'을 가져 와서 정수 나누기를 수행 하시겠습니까? – fge

+0

그래서 기간을 최대 및 최소 밀리 초로 설정할 수 있어야한다고 생각합니다. 간격으로 밀리를 나누면 이진 검색의 상한 및 하한에주기 수를 찾을 수 있습니다. –

답변

1

다음은 내 권장 솔루션입니다. 기간의 평균 길이를 사용하여 가장 좋은 추측을 작성한 다음 수정하십시오. 이것은 그것을하는 가장 능률적이고 우아한 방법 보인다.

import com.google.common.base.Function; 
import com.google.common.collect.ImmutableMap; 
import org.joda.time.*; 

import static com.google.common.collect.FluentIterable.from; 
import static java.util.Arrays.asList; 
import static org.joda.time.DurationFieldType.*; 

public class PeriodArithmetic { 

    public static long periodsInAnInterval(Interval interval, Period period) { 
     int bestGuess = (int) (interval.toDurationMillis()/toAverageMillis(period)); 
     if (bestGuess < 0) return 0; 
     if (startPlusScaledPeriodIsAfterEnd(interval, period, bestGuess + 1)) { 
      return searchDownwards(interval, period, bestGuess); 
     } else { 
      return searchUpwards(interval, period, bestGuess); 
     } 
    } 

    private static long searchDownwards(Interval interval, Period period, int currentGuess) { 
     if (startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess)) { 
      return searchDownwards(interval, period, currentGuess - 1); 
     } else { 
      return currentGuess; 
     } 
    } 

    private static long searchUpwards(Interval interval, Period period, int currentGuess) { 
     if (!startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess + 1)) { 
      return searchUpwards(interval, period, currentGuess + 1); 
     } else { 
      return currentGuess; 
     } 
    } 

    private static boolean startPlusScaledPeriodIsAfterEnd(Interval interval, Period period, int scalar) { 
     return interval.getStart().plus(period.multipliedBy(scalar)).isAfter(interval.getEnd()); 
    } 

    private static final long MILLIS_IN_DAY = Days.ONE.toStandardSeconds().getSeconds() * 1000L; 
    private static final long MILLIS_IN_YEAR = Days.ONE.toStandardSeconds().getSeconds() * 365250L; 

    private static final ImmutableMap<DurationFieldType, Long> averageLengthMillis 
      = ImmutableMap.<DurationFieldType, Long>builder() 
      .put(millis(), 1L) 
      .put(seconds(), 1000L) 
      .put(minutes(), Minutes.ONE.toStandardSeconds().getSeconds() * 1000L) 
      .put(hours(),  Hours.ONE.toStandardSeconds().getSeconds() * 1000L) 
      .put(halfdays(), MILLIS_IN_DAY/2) 
      .put(days(),  MILLIS_IN_DAY) 
      .put(weeks(),  Weeks.ONE.toStandardSeconds().getSeconds() * 1000L) 
      .put(months(), MILLIS_IN_YEAR/12) 
      .put(years(),  MILLIS_IN_YEAR) 
      .put(weekyears(), MILLIS_IN_YEAR) 
      .put(centuries(), MILLIS_IN_YEAR * 100) 
      .put(eras(),  Long.MAX_VALUE) 
      .build(); 

    private static long toAverageMillis(Period period) { 
     final Iterable<Long> milliValues = from(asList(period.getFieldTypes())).transform(toAverageMillisForFieldType(period)); 
     return total(milliValues); 
    } 

    private static Function<DurationFieldType, Long> toAverageMillisForFieldType(final Period period) { 
     return new Function<DurationFieldType, Long>() { 
      @Override 
      public Long apply(DurationFieldType durationFieldType) { 
       final Long averageDuration = averageLengthMillis.get(durationFieldType); 
       return period.get(durationFieldType) * averageDuration; 
      } 
     }; 
    } 

    private static long total(Iterable<Long> milliValues) { 
     long acc = 0; 
     for (Long milliValue : milliValues) { 
      acc += milliValue; 
     } 
     return acc; 
    } 
} 
0

는 여기 이진 검색 솔루션의 시작 넣어했습니다 https://gist.github.com/Mahoney/9899936

그것은 훨씬 더 복잡한 선형 검색 이외의를; 반면에 1,000 년 동안의 달 수를 찾는 데는 약 100 배 더 빠릅니다.

아직 완료되지 않았습니다. 무엇보다 실험이 더 많으므로 테스트되지 않은 가장자리 케이스 (음수 기간)가있을 것이라고 확신합니다.

누구든지 좀 더 세련된 솔루션이 있는지 알고 싶다면 (또는 쓸 필요가없는 경우에만 &을 테스트하십시오!).