使用复杂的位掩码确定哪个位设置为某个日期
我有一个移位掩码,代表一周内的几天:
Sunday = 1
Monday = 2
Tuesday = 4
...
Saturday = 64
我正在使用位掩码,因为几天(至少一天)可能会设置为1。
问题
然后我得到一个约会。 任何日期。 根据date.DayOfWeek
我需要返回位掩码中设置的第一个最接近的日期。 所以我的方法可以返回date
和date + 6
之间的同一天或任何其他date + 6
。
例1
我的位掩码定义了所有日期设置为1.在这种情况下,我的方法应该返回相同的日期,因为date.DayOfWeek
设置date.DayOfWeek
掩码中。
例2
我的位掩码定义只有星期三设置为1.如果我的传入日期是星期二,我应该返回date+1
(即星期三)。 但是如果传入日期是星期四,我应该返回date+6
(这又是星期三)。
题
解决这个问题最快和最优雅的方法是什么? 为什么最快? 因为我需要运行几次,所以如果我可以使用某种缓存结构来更快地获取日期,那么这将是首选。
你能提出一些指导方案来解决这个问题吗? 我不想结束一个充满ifs和switch-case语句的冗长的意大利面条代码......
重要提示 :需要注意的是,如果掩码有助于提高性能和简化代码,则可能会更改掩码或将其替换为其他内容。 所以位掩码不是一成不变的...
一种可能的方法
每天生成一组偏移量并将其保存在私有类变量中会很明智。 生成一次,然后重新使用它,如下所示:
return date.AddDays(cachedDayOffsets[date.DayOfWeek]);
这样我们根本不使用位掩码,唯一的问题是如何以最快的速度和尽可能短的代码生成数组。
你可能讨厌这个答案,但也许你可以在一个新的方向上运行它。 你说性能是非常重要的,所以也许最好是在一些数据结构中索引所有的答案。 这个数据结构可能有点复杂,但它可以封装在自己的小世界中,不会干扰你的主代码。 我想到的数据结构将是一系列整数。 如果您允许星期一,星期五和星期六,这些整数将是:
[1][0][3][2][1][0][0]
好吧奇怪的权利? 这基本上是本周的“天数”列表。 星期天,“下一个星期允许一天”。 星期一,星期二有3天。 现在,一旦你建立了这个列表,你可以很容易,很快地找出你需要添加多少天来获得下一次发生。 希望这有助于?
生成这些偏移量
这是生成这些偏移量的代码:
this.dayOffsets = new int[] {
this.Sundays ? 0 : this.Mondays ? 1 : this.Tuesdays ? 2 : this.Wednesdays ? 3 : this.Thursdays ? 4 : this.Fridays ? 5 : 6,
this.Mondays ? 0 : this.Tuesdays ? 1 : this.Wednesdays ? 2 : this.Thursdays ? 3 : this.Fridays ? 4 : this.Saturdays ? 5 : 6,
this.Tuesdays ? 0 : this.Wednesdays ? 1 : this.Thursdays ? 2 : this.Fridays ? 3 : this.Saturdays ? 4 : this.Sundays ? 5 : 6,
this.Wednesdays ? 0 : this.Thursdays ? 1 : this.Fridays ? 2 : this.Saturdays ? 3 : this.Sundays ? 4 : this.Mondays ? 5 : 6,
this.Thursdays ? 0 : this.Fridays ? 1 : this.Saturdays ? 2 : this.Sundays ? 3 : this.Mondays ? 4 : this.Tuesdays ? 5 : 6,
this.Fridays ? 0 : this.Saturdays ? 1 : this.Sundays ? 2 : this.Mondays ? 3 : this.Tuesdays ? 4 : this.Wednesdays ? 5 : 6,
this.Saturdays ? 0 : this.Sundays ? 1 : this.Mondays ? 2 : this.Tuesdays ? 3 : this.Wednesdays ? 4 : this.Thursdays ? 5 : 6
};
这一个产生前向偏移。 因此,对于任何给定的日期,您可以简单地通过以下方式获得实际适用日期:
SomeDate.AddDays(this.dayOffsets[(int)SomeDate.DayOfWeek]);
如果您需要取得最近的过去日期,则可以重新使用相同的数组并使用以下公式进行计算:
SomeDate.AddDays((this.dayOffsets[(int)SomeDate.DayOfWeek] - 7) % 7);
我会用一个掩码,一些移位和一个位扫描来解决这个问题。 这不是一个非常明显的例程,但它应该很快,因为它从不分支:
original_date = Whatever //user input
bitmask = Whatever //user input
bitmask |= (bitmask << 7) //copy some bits so they don't get
//lost in the bitshift
bitmask >>= original_date.dayOfWeek() //assuming Sunday.dayOfWeek() == 0
return original_date + bitscan(bitmask) - 1 //the position of the least
//significant bit will be one greater
//than the number of days to add
Bitscan--特别是你的,因为它只关心七位 - 很容易在查找表中实现。 事实上,如果您创建了自定义表格,则可以调用LSB位0,并在return语句中跳过减法。 我猜想所有这些中最慢的部分是dayOfWeek()函数,但这取决于它的实现。
希望这可以帮助!
编辑:示例bitscan表(将lsb视为索引1 - 您可能希望将其视为零,但这是一个更好的示例):
int[128] lsb = {
0, //0 = 0b00000000 - Special case!
1, //1 = 0b00000001
2, //2 = 0b00000010
1, //3 = 0b00000011
3, //4 = 0b00000100
1, //5 = 0b00000101
2, //6 = 0b00000110
....
1 //127 = 0b01111111
};
然后,要在mask
上使用您的桌子,您只需使用:
first_bit_index = lsb[mask & 127];
在&
让你写一个短小的表,因为你真正关心的七个最低位。
PS:至少有一些处理器实现了一个你可以用来代替的bitscan指令,但是除非有一个包装器函数,否则你不可能用C#来获取它们。
这是我会做的,变量dateDiff
将是你正在寻找的。
DoW mask = DoW.Wednesday | DoW.Friday;
DoW? todayDoW = null;
int dateDiff = 0;
do
{
DateTime date = DateTime.Today.AddDays(dateDiff);
todayDoW = (DoW)Enum.Parse(typeof(DoW), date.DayOfWeek.ToString());
if ((mask & todayDoW.Value) != 0)
{
todayDoW = null;
}
else
{
dateDiff++;
}
}
while(todayDoW.HasValue);
enum DoW
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64
}
链接地址: http://www.djcxy.com/p/9343.html
上一篇: Determine which bit is set, for a date, using complex bit masks
下一篇: Is it possible to attach a debugger to a running PHP process?