在位掩码中设置下一个工作日的算法
Posted
技术标签:
【中文标题】在位掩码中设置下一个工作日的算法【英文标题】:An algorithm to get the next weekday set in a bitmask 【发布时间】:2008-12-08 23:50:12 【问题描述】:我有一个小问题 - 给定工作日的位掩码(例如,Sunday = 0x01, Monday = 0x02, Tuesday = 0x04
等...)和今天(以Sunday = 1, Monday = 2, Tuesday = 3
等的形式) - 什么是最优雅的从今天开始找出第二天的方法,这是在位掩码中设置的?优雅的意思是,有没有办法在没有 if/switch/etc... 的情况下做到这一点,因为我知道非优雅的方式?
编辑我可能应该提到(为了更清楚地说明)持有位掩码的变量可以设置好几天,例如(大致):
uDay = Sunday | Monday;
today = Tuesday;
我需要得到“星期天”
【问题讨论】:
顺便说一句,“下一个”是指“今天或更晚”,还是“严格迟于今天”? (例如“uDay = Sunday | Tuesday; today = Tuesday;”,它应该说什么?) 错过了这条评论 :) next = 今天或更晚 【参考方案1】:int getNextDay(int days_mask, int today)
if (!days_mask) return -1; // no days set
days_mask |= days_mask << 7; // duplicate days into next week
mask = 1 << (today % 7); // keep track of the day
while (!(mask & days_mask))
mask <<= 1;
++today;
return today % 7;
所以这只是一个 if 开头和 while 循环。怎么样?
编辑:我刚刚意识到有一种退化的情况,如果今天使用超过>=14(或大于设置的最高位),while 循环将变为无限。第 4 行的 (today % 7) 修复了这种情况。
如果我可能会(轻松地)抱怨另一个版本获得复选标记,我的版本只有 2 个模数调用,而选中的解决方案将有最少 1 个和最多 6 个模数调用。
另外,如果设置了今天,函数是否返回“今天”的评论很有趣。如果函数今天不应该返回,除非今天是集合中唯一的一天,则需要您在我的解决方案的第 3 行预增加今天。
【讨论】:
复制位然后继续的方式非常聪明:) 我打勾是因为他首先提出了解决方案 - 抱歉 :) 当我使用编译器时,我会检查所有代码,然后再决定最终标记。感谢您为此付出的时间和精力。 嗯,实际上我认为它不起作用(或者我搞砸了)-例如,如果 days_mask = 0x14 和 today = 3,则函数返回 4,而我希望它返回 3 (或 5 个)。 4 是从 0 开始的一周中的第 5 天。 0x14 设置了基于 0 的位 2 和 4。重读要求,我发现我的回报需要 +1:return today%7 + 1。 是的,就我而言,星期日是从 1 开始的。【参考方案2】:您根本不需要任何额外的变量。最简单的想法——从“明天”开始,查看连续的日子,直到找到面具中的一天——也是最优雅的实现方式。做到这一点的诀窍是将日子想象为 Sunday=0、Monday=1 等等(仅在此函数内部)。那么,“今天”实际上是t-1
(其中t是函数的输入,所以它从1到7),而“明天”是(t-1+1)%7
,即t%7
等
这很简单,并且已经针对 litb 的代码进行了详尽的测试,以确保 :-)
int getNextDay(int m, int t)
if((m&127)==0) return t; //If no day is set, return today
t=t%7; //Start with tomorrow
while((m&(1<<t))==0) t = (t+1)%7; //Try successive days
return t+1; //Change back to Sunday=1, etc.
编辑:如果您希望“下一个”表示“今天或以后”,则应将“t=t%7”行更改为t=t-1
或--t
。
【讨论】:
【参考方案3】:我是这样理解你的问题的:
// returns t (today) if no weekday is set in the mask.
int getNextDay(int m, int t)
int i, idx;
for(i = 0, idx=t%7; i<7 && !((1<<idx)&m); i++, idx=(idx+1)%7)
/* body empty */ ;
return (i == 7) ? t : (idx + 1);
// getNextDay(8|2, 2) == 4, getNextDay(64, 2) == 7
// getNextDay(128, 2) == 2
【讨论】:
谢谢 - 我需要计算一下它的作用,然后看看它是否能按照我需要的方式工作:) 我现在只是在纸上做这个:) 但是 getNextDay(128, 2) 不应该返回 5 吗? 128 是 10000000 ,也就是说,对于所有工作日,我们都有零位。那就不可能有第二天了。那我今天就回来【参考方案4】:我的意思是优雅,有没有办法在没有 if/switch/etc 的情况下做到这一点...
你打赌!这是否意味着任何通常意义上的“优雅”,嗯:
static unsigned next_day_set (unsigned today, unsigned set)
unsigned arev = bitreverse (highest_bit_set (bitreverse ((set << 7) | set)
& (bitreverse (today) - 1)));
return ((arev >> 7) | arev) & 0x7f;
假设您有“优雅”的功能来反转单词中的位并找到最左边的位集——请参阅Hacker's Delight。如果你以相反的顺序表示工作日位,假设我没有搞砸,它会更简单,实际上甚至有点优雅:
enum
Sunday = 1 << 6
Monday = 1 << 5
Tuesday = 1 << 4,
/* etc */
Saturday = 1 << 0
;
static unsigned next_day_set (unsigned today, unsigned set)
unsigned a = highest_bit_set (((set << 7) | set) & ((today << 7) - 1));
return ((a >> 7) | a) & 0x7f;
【讨论】:
以上是关于在位掩码中设置下一个工作日的算法的主要内容,如果未能解决你的问题,请参考以下文章