问题陈述是计算每个月的 13 号在[1900-01-01, 2300-01-01) 范围内的给定工作日的频率。
使用date.h 可以非常轻松高效地完成,如下所示:
#include "date/date.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
unsigned freq[7] = {};
for (auto ym = 1900_y/January; ym < 2300_y/January; ym += months{1})
freq[weekday{ym/13}.c_encoding()]++;
for (unsigned i = 0; i < 7; ++i)
std::cout << weekday{i} << " : " << freq[i] << '\n';
}
ym 是一个 date::year_month 对象。你可以把它想象成time_point,但它的精度非常粗略,为months。
您只需遍历每年和每年的每个月,并计算该月 13 日的星期几,然后将 weekday 转换为 unsigned。
高级语法非常简单易读。
底层算法是days_from_civil 和weekday_from_days。这些低级日期算法都不是迭代的,因此它们非常有效。因此,您可以获得两全其美:可读的高级语法和高性能。
这个简单程序的输出也很可读:
Sun : 687
Mon : 685
Tue : 685
Wed : 687
Thu : 684
Fri : 688
Sat : 684
事实证明,13 号星期五比一周中的其他日子更有可能发生。
在 C++17 中,您甚至可以使用这些结果创建 constexpr std::array<unsigned, 7>(出于某种原因,在编译时拥有这样的数字是否很重要):
#include "date/date.h"
#include <array>
#include <chrono>
#include <iostream>
constexpr
std::array<unsigned, 7>
compute_freq() noexcept
{
using namespace date;
using namespace std::chrono;
decltype(compute_freq()) freq = {};
for (auto ym = 1900_y/January; ym < 2300_y/January; ym += months{1})
freq[weekday{ym/13}.c_encoding()]++;
return freq;
}
constexpr auto freq = compute_freq();
int
main()
{
using namespace date;
using namespace std::chrono;
static_assert(freq[Sunday.c_encoding()] == 687);
static_assert(freq[Monday.c_encoding()] == 685);
static_assert(freq[Tuesday.c_encoding()] == 685);
static_assert(freq[Wednesday.c_encoding()] == 687);
static_assert(freq[Thursday.c_encoding()] == 684);
static_assert(freq[Friday.c_encoding()] == 688);
static_assert(freq[Saturday.c_encoding()] == 684);
}
生成此程序集:
_freq:
.long 687 ## 0x2af
.long 685 ## 0x2ad
.long 685 ## 0x2ad
.long 687 ## 0x2af
.long 684 ## 0x2ac
.long 688 ## 0x2b0
.long 684 ## 0x2ac
而且你无法获得比这更高效的方法。
在 C++20 中,这一切都在您的 std::lib 中可用。要将上述程序移植到 C++20,请删除 #include "date/date.h" 和 using namespace date;。还将_y 后缀更改为y。