使用复杂位掩码确定为日期设置了哪个位
我有一个代表一周中的几天的位移掩码:
Sunday = 1
Monday = 2
Tuesday = 4
...
Saturday = 64
我正在使用位掩码,因为几天(至少一天)可能设置为 1。
问题
然后我得到一个日期。任何日期。基于 date.DayOfWeek
我需要返回位掩码中设置的第一个最接近的日期。因此,我的方法可以返回同一天或 date
和 date + 6
之间的任何其他日期。
示例 1
我的位掩码定义所有日期均设置为 1。在这种情况下,我的方法应返回相同的日期,因为在位掩码中设置了 date.DayOfWeek
。
示例 2
我的位掩码定义只有星期三设置为 1。如果我的传入日期是星期二,我应该返回date+1
(即星期三)。但如果传入日期是星期四,我应该返回date+6
(这又是星期三)。
问题
解决这个问题最快、最优雅的方法是什么?为什么也最快?因为我需要运行几次,所以如果我可以使用某种缓存结构来更快地获取日期,那将是首选。
您能建议一些指导来以优雅的方式解决这个问题吗?我不想最终得到充满 if 和 switch-case 语句的长意大利面条代码......
重要:请务必注意,如果位掩码有助于提高性能和简化代码,则可以更改或替换为其他内容。所以位掩码并不是一成不变的......
一种可能的方法
每天生成一个偏移量数组并将其保存在私有类变量中是明智的。生成一次并随后重复使用它,如下所示:
return date.AddDays(cachedDayOffsets[date.DayOfWeek]);
这样我们根本不使用位掩码,唯一的问题是如何以最快的速度并使用尽可能短的代码生成数组。
I have a bit shift mask that represents days in a week:
Sunday = 1
Monday = 2
Tuesday = 4
...
Saturday = 64
I'm using a bitmask because several (at least one) days may be set to 1.
The problem
Then I get a date. Any date. And based on the date.DayOfWeek
I need to return the first nearest date after it that is set in the bitmask. So my method can return the same day or any other day between date
and date + 6
.
Example 1
My bitmask defines all days being set to 1. In this case my method should return the same date, because date.DayOfWeek
is set in the bitmask.
Example 2
My bitmask defines that only Wednesday is set to 1. If my incoming date is Tuesday, I should return date+1
(which is Wednesday). But if incoming date is Thursday I should return date+6
(which is again Wednesday).
Question
What is the fastest and most elegant way of solving this? Why also fastest? Because I need to run this several times so if I can use some sort of a cached structure to get dates faster it would be preferred.
Can you suggest some guidance to solve this in an elegant way? I don't want to end up with a long spaghetti code full of ifs and switch-case statements...
Important: It's important to note that bitmask may be changed or replaced by something else if it aids better performance and simplicity of code. So bitmask is not set in stone...
A possible approach
It would be smart to generate an array of offsets per day and save it in a private class variable. Generate it once and reuse it afterwards like:
return date.AddDays(cachedDayOffsets[date.DayOfWeek]);
This way we don't use bitmask at all and the only problem is how to generate the array the fastest and with as short code as possible.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我会用位掩码、一些移位和位扫描来解决这个问题。这不是一个非常明显的例程,但它应该很快,因为它从不分支:
Bitscan - 特别是你的,因为它只关心七位 - 很容易在查找表中实现。事实上,如果您做了自定义表,您可以调用 LSB 位 0,并跳过 return 语句中的减法。我猜想其中最慢的部分是 dayOfWeek() 函数,但这取决于它的实现。
希望这有帮助!
编辑:示例位扫描表(将 lsb 视为索引 1 - 您可能希望将其视为零,但这是一个更好的示例):
然后,在
mask
,您只需使用:&
可以让您编写一个较小的表,因为您实际上只关心七个最低位。PS: 至少有些处理器实现了您可以使用的位扫描指令,但您似乎不太可能使用 C# 来获取它们,除非某处有包装函数。
I'd go about this with a bitmask, some shifting, and a bitscan. It's not a very obvious routine, but it should be fast, as it never branches:
Bitscan - especially yours, 'cause it only cares about seven bits - is easy to implement in a lookup table. In fact, if you did a custom table, you could call the LSB bit 0, and skip the subtraction in the return statement. I'd guess the slowest part of all of this would be the dayOfWeek() function, but that would depend on it's implementation.
Hope this helps!
Edit: An example bitscan table (that treats the lsb as index 1 - you'll probably want to treat it as zero, but this makes a better example):
Then, to use your table on
mask
, you'd just use:The
&
lets you write a smallish table, 'cause you really only care about the seven lowest bits.PS: At least some processors implement a bitscan instruction that you could use instead, but it seems unlikely that you could get at them with C#, unless there's a wrapper function somewhere.
您可能讨厌这个答案,但也许您可以沿着新的方向前进。您说性能非常重要,所以也许最好在某些数据结构中预先索引所有答案。该数据结构可能有点复杂,但它可以封装在自己的小世界中,并且不会干扰您的主代码。我想到的数据结构是一个整数数组。如果你允许周一、周五和周六,这些整数将是:
好吧,很奇怪吧?这基本上就是本周的“休假天数”列表。周日,“距离一周中允许的下一天还有 1 天”。周一还有 0 天,周二还有 3 天。现在,一旦您构建了此列表,您就可以非常轻松且快速地计算出需要在日期中添加多少天才能获得下一次出现。希望这有帮助?
生成这些偏移量
这是生成这些偏移量的代码:
该代码生成前向偏移量。因此,对于任何给定的日期,您可以简单地获取其后的实际适用日期:
如果您需要获取最近的过去日期,您可以重复使用相同的数组并使用以下公式计算出来:
You might hate this answer, but perhaps you'll be able to run with it in a new direction. You said performance is extremely important, so maybe it's best to just index all the answers up front in some data structure. That data structure might be somewhat convoluted, but it could be encapsulated in its own little world and not interfere with your main code. The data structure I have in mind would be an array of ints. If you allow Monday, Friday, and Saturday, those ints would be:
Ok weird right? This is basically the "days away" list for the week. On Sunday, there's "1 day until next allowed day of week". On Monday, there's 0. On Tuesday, it's 3 days away. Now, once you build this list, you can very easily and very quickly figure out how many days you need to add to your date to get the next occurance. Hopefully this helps??
Generating these offsets
This is the code that generates these offsets:
This one generates forward offsets. So for any given date you can get the actual applicable date after it by simply:
If you need to get nearest past date you can reuse the same array and calculate it out by using this formula:
这就是我要做的,变量
dateDiff
将是您正在寻找的内容。Here is what I'd do, the variable
dateDiff
will be what you are looking for.这是填充查找表的算法。你只需要做一次,所以我不确定它的效率有多重要......
然后只需使用:
Here's an algorithm to populate the lookup table. You only have to do it once, so I'm not sure that it matters how efficient it is...
Then just use:
我知道你说要考虑性能,但我会从一个简单、易于理解的方法开始,并验证其性能是否足够,然后再跳到更复杂的方法,这可能会让未来的代码维护者有点迷失。
话虽如此,使用预初始化查找的可能解决方案:
假设之前的枚举:
最后是
CreateMaps
实现:I understand that you said performance is to be taken in consideration but I would start with a simple, easy to understand approach and verify if its performance is sufficient before jumping to a more complex method that can leave future maintainers of the code a bit lost.
Having said that, a possible solution using pre-initialized lookups:
Assuming the previous enumeration:
And finally the
CreateMaps
implementation:这应该很容易做到。考虑
DayOfWeek.Sunday == 0
、Monday == 1
等。您的掩码与此相对应。也就是说,在您的掩码中:
现在,给定一周中的某一天,我们可以轻松确定符合您条件的日期:
例如,您要匹配的日期是星期三,但掩码仅包含星期一。您可以看到算法将选择星期一作为最佳日期,然后遍历其余的日子,而不选择任何内容。
如果掩码包含星期一、星期二和星期四,则算法将选择星期一作为最佳候选,忽略星期二,然后选择星期四作为最佳候选并退出。
这不会像查找表那么快,但它应该非常快。而且它比查找表使用更少的内存。
查找表会快得多,而且只需要 1 KB 的内存。考虑到上面的 FindNextDay 方法,构造起来很容易:
现在,要获取掩码和当前日期的任意组合的第二天:
毫无疑问,有一种更快的方法来生成表。但它足够快,而且由于它会在程序启动时执行一次,因此优化它似乎没有太大意义。如果您希望启动速度更快,请编写一个小程序,将表输出为 C# 代码。类似于:
然后您可以将其复制并粘贴到您的程序中。
This should be pretty easy to do. Consider that
DayOfWeek.Sunday == 0
,Monday == 1
, etc.Your mask corresponds to to this. That is, in your mask:
Now, given a day of week, we can easily determine the day that will match your criteria:
For example, the day you want to match is Wednesday, but the mask contains only Monday. You can see that the algorithm will select Monday as the best day and then go through the rest of the days, not selecting anything.
If the mask contains Monday, Tuesday, and Thursday, the algorithm will select Monday as the best candidate, ignore Tuesday, and then select Thursday as the best candidate and exit.
This won't be as fast as a lookup table, but it should be pretty darned fast. And it'll use a lot less memory than a lookup table.
A lookup table would be much faster, and it wouldn't take but a kilobyte of memory. And given the
FindNextDay
method above, it's easy enough to construct:Now, to get the next day for any combination of mask and current day:
There's undoubtedly a faster way to generate the table. But it's fast enough and since it would be executed once at program startup, there doesn't seem much point in optimizing it. If you want startup to be faster, write a little program that will output the table as C# code. Something like:
Then you can copy and paste that into your program.