【问题标题】:Algorithm to find optimal day/month/year intervals to in an arbitrary timeframe?在任意时间范围内找到最佳日/月/年间隔的算法?
【发布时间】:2011-12-01 20:00:27
【问题描述】:

如果你有一个时间表,说:

March 19, 2009 - July 15, 2011

是否有一种算法可以将该时间范围分解为:

March 19, 2009  - March 31, 2009    # complete days
April 1, 2009   - December 31, 2009 # complete months
January 1, 2010 - December 31, 2010 # complete years
January 1, 2011 - June 30, 2011     # complete months
July 1, 2011    - July 15, 2011     # complete days

更具体地说,给定任意时间范围,甚至精确到秒,是否有一种算法可以将其划分为最佳数量的任意大小间隔?

因此,也许您不想将上述日期范围划分为天/月/年,而是将其划分为 5 天和 18 个月的块,就像这样随机。这种算法有正式名称吗?一个 ruby​​ 示例会很棒,但任何语言都可以。

我已经能够拼凑一些硬编码的 Ruby 东西来处理日/月/年示例:

...但似乎应该有一种算法来抽象它以处理任何间隔故障。也许它只是归结为简单的数学。

【问题讨论】:

  • 是的,“最佳”可能不是正确的词。我认为您只是在寻找间隔中完整子间隔的数量。只需将整个事情变成几秒钟(或者无论你需要多小),然后做一些基本的数学运算?
  • 它不只是将整个时间范围按天划分为x 天,而是将其划分为最少的月/天/年来填充时间范围。因此,对于第一个月,您必须使用天数,因为您没有完整的月份。然后你必须使用月份,因为你没有完整的年份……在(任何)完整的年份之后,你使用完整的月份,除非你在一月份,然后你使用完整的日子。
  • @viatropos:只是为了确认一下,您的意思是要最小化(num_days + num_months + num_years)?
  • @Oli Charlesworth,这听起来不错,但不能有几个月/年/天/等的分数。

标签: ruby algorithm


【解决方案1】:

这可能是作弊,但您可以通过使用 active_support 提供的日期函数大大简化代码。下面是我使用 active_support 提出的一些代码。该算法非常简单。算出第一个月的最后一天,算出最后一个月的第一天。然后打印第一个月,将中间分成年份并打印它们,然后打印最后一个月。当然,由于边缘情况,这个简单的算法在很多方面都失败了。下面的算法试图优雅地解决这些边缘情况。希望对你有帮助。

require 'active_support/all'

# Set the start date and end date
start_date = Date.parse("March 19, 2009")
end_date = Date.parse("July 15, 2011")

if end_date < start_date
  # end date is before start date, we are done
elsif end_date == start_date
  # end date is the same as start date, print it and we are done
  print start_date.strftime("%B %e, %Y")
elsif start_date.year == end_date.year && start_date.month == end_date.month
  # start date and end date are in the same month, print the dates and we
  # are done
  print start_date.strftime("%B %e, %Y"), " - ",
    end_date.strftime("%B %e, %Y"), "\n"
else
  # start date and end date are in different months
  first_day_of_next_month = Date.new((start_date + 1.month).year,
    (start_date + 1.month).month, 1);
  first_day_of_end_month = Date.new(end_date.year, end_date.month, 1);

  # print out the dates of the first month
  if (first_day_of_next_month - 1.day) == start_date
    print start_date.strftime("%B %e, %Y"), "\n"
  else
    print start_date.strftime("%B %e, %Y"), " - ",
      (first_day_of_next_month - 1.day).strftime("%B %e, %Y"), "\n"
  end

  # now print the inbetween dates
  if first_day_of_next_month.year == (first_day_of_end_month - 1.day).year &&
    (first_day_of_end_month - 1.day) > first_day_of_next_month
    # start date and end date are in the same year, just print the inbetween
    # dates 
    print first_day_of_next_month.strftime("%B %e, %Y"), " - ",
      (first_day_of_end_month - 1.day).strftime("%B %e, %Y") + "\n"
  elsif first_day_of_next_month.year < (first_day_of_end_month - 1.day).year
    # start date and end date are in different years so we need to split across
    # years
    year_iter = first_day_of_next_month.year

    # print out the dates from the day after the first month to the end of the
    # year
    print first_day_of_next_month.strftime("%B %e, %Y"), " - ",
      Date.new(first_day_of_next_month.year, 12, 31).strftime("%B %e, %Y"),
      "\n"
    year_iter += 1

    # print out the full intermediate years
    while year_iter < end_date.year
      print Date.new(year_iter, 1, 1).strftime("%B %e, %Y"), " - ",
        Date.new(year_iter, 12, 31).strftime("%B %e, %Y"), "\n"
      year_iter += 1
    end

    # print from the begining of the last year until the last day before the the
    # end month
    print Date.new(first_day_of_end_month.year, 1, 1).strftime("%B %e, %Y"),
      " - ", (first_day_of_end_month - 1.day).strftime("%B %e, %Y"), "\n"
  end

  # finally print out the days of the last month
  if first_day_of_end_month == end_date
    print end_date.strftime("%B %e, %Y"), "\n"
  else
    print first_day_of_end_month.strftime("%B %e, %Y"), " - ",
      end_date.strftime("%B %e, %Y"), "\n"
  end
end

【讨论】:

    【解决方案2】:

    最初的问题在某种意义上很简单,因为一年是由几个月组成的,而一个月是由几天组成的。因此,您可以只查看您的范围中的哪些部分是完整的年份,将它们取出,然后迭代地查看下一个较小单位的完整实例剩余的部分。

    在任意情况下,这不一定是真的。在您的示例中,并非每 18 个月的跨度都可以分成五天的间隔。这使事情变得相当复杂,因为您不能再依赖这种单位层次结构。

    任意情况的另一个复杂因素是,并非每个范围都有可行的细分。在您的示例中,2011 年 1 月 1 日 - 2011 年 1 月 4 日不能分解为 5 天 18 个月的块。

    因此,我建议您尝试制定一个更受约束的问题版本 :)

    【讨论】:

      【解决方案3】:

      一旦您将不同大小的月份混合在一起,这会变得稍微复杂一些,但是对于一个好的日期库,同样的概念应该可以工作(并且可能会使“进步”更容易一些)。

      以 psudocode 开头的内容以 python 结尾 - 对此感到抱歉。

      intervals = [1, 60, 3600] # second minute hour
      epoc = 0 # simple example case
      start = 4 # 4 seconds past epoc
      end = 7292 # 2:1:32
      
      i = start
      while i < end:
        for iidx, interval in enumerate(intervals):
          nIval = 0
          if(iidx+1 < len(intervals)):
            nIval = intervals[iidx+1]
          # wind down - find the next smallest interval that doesn't push us past END
          if (i-epoc) % interval == 0 and (i + nIval > end or not nIval):
            # move as close to end as possible
            pre = i
            while i+interval <= end:
              i += interval
            print "Stepped down %i to %i via %i" % (pre, i, interval)
          # wind up - find the next biggest interval boundary not past END
          elif nIval and (i-epoc) % nIval != 0:
            # advance to that interval boundry
            pre = i
            while (i-epoc) % nIval != 0 and i+interval <= end:
              i += interval
            print "Stepped up %i to %i via %i" % (pre, i, interval)
          if i == end:
            break
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-20
        • 1970-01-01
        • 1970-01-01
        • 2019-11-08
        • 2023-03-23
        • 1970-01-01
        • 2017-08-22
        • 2011-03-02
        相关资源
        最近更新 更多