基本上我有一个伽玛探测器系统,每个探测器分成4个晶体,在只有2个晶体记录命中的情况下,我们可以确定该对是否垂直或平行于产生伽马射线的反应平面。在为此写出逻辑的过程中,我写了一个巨大而丑陋的开关语句组合,在每个探测器中检查晶体数的组合(在整个探测器阵列及其晶体中是唯一的)。这是代码,包括有问题的功能。
//The Parallel and Perpendicular designations are used in addition to the Double
//designation for the 90 degree detectors if we get a diagonal scatter in those detectors
//then we use the Double designation
enum ScatterType{Single, Double, Triple, Quadruple, Parallel, Perpendicular};
ScatterType EventBuffer::checkDoubleGamma(int det)
{
int num1=evList[crysList[0]].crystalNum;
int num2=evList[crysList[1]].crystalNum;
switch(det)
{
case 10: //first of the 90 degree detectors
if( (num1==40 && num2==41) || //combo 1
(num1==41 && num2==40) || //combo 1 reverse
(num1==42 && num2==43) || //combo 2
(num1==43 && num2==42) )//combo 2 reverse
{ return Parallel; }
else if( (num1==40 && num2==42) || //combo 1
(num1==42 && num2==40) || //combo 1 reverse
(num1==41 && num2==43) || //combo 2
(num1==43 && num2==41) )//combo 2 reverse
{ return Perpendicular; }
else
{ return Double;}
break;
case 11: //second of the 90 degree detectors
if( (num1==44 && num2==45) || //combo 1
(num1==45 && num2==44) || //combo 1 reverse
(num1==46 && num2==47) || //combo 2
(num1==47 && num2==46) )//combo 2 reverse
{ return Parallel; }
else if( (num1==44 && num2==47) || //combo 1
(num1==47 && num2==44) || //combo 1 reverse
(num1==45 && num2==46) || //combo 2
(num1==46 && num2==45) )//combo 2 reverse
{ return Perpendicular; }
else
{ return Double;}
break;
case 13: //third of the 90 degree detectors
if( (num1==52 && num2==53) || //combo 1
(num1==53 && num2==52) || //combo 1 reverse
(num1==54 && num2==55) || //combo 2
(num1==55 && num2==54) )//combo 2 reverse
{ return Parallel; }
else if( (num1==52 && num2==55) || //combo 1
(num1==55 && num2==52) || //combo 1 reverse
(num1==53 && num2==54) || //combo 2
(num1==54 && num2==53) )//combo 2 reverse
{ return Perpendicular; }
else
{ return Double;}
break;
case 14: //fourth of the 90 degree detectors
if( (num1==56 && num2==57) || //combo 1
(num1==57 && num2==56) || //combo 1 reverse
(num1==58 && num2==59) || //combo 2
(num1==59 && num2==58) )//combo 2 reverse
{ return Parallel; }
else if( (num1==56 && num2==59) || //combo 1
(num1==59 && num2==56) || //combo 1 reverse
(num1==57 && num2==58) || //combo 2
(num1==58 && num2==57) )//combo 2 reverse
{ return Perpendicular; }
else
{ return Double;}
break;
default:
throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
break;
}
}
我知道,因为晶体数是全局的而不是每个探测器,我可以取消switch语句并且有一组由条件或语句链接的大量条件,基本上将事物减少到3个控制路径,一个返回并行,一个返回垂直,一个返回Double,而不是12个控制路径,每个控制路径有4个。我最初写它是因为它没有思考,并且说实话,考虑到这一点,这种方法减少了布尔语句的平均案例数量。
我刚刚研究了如何通过启用evList[crysList[0]].crystalNum
来提高效率,我可以减少相当多的评估,产生这样的结果:
ScatterType EventBuffer::checkDoubleGamma()
{
int crysNum = crysList[1].crystalNum;
switch(evList[crysList[0]].crystalNum)
{
case 40:
if (crysNum == 41) {return Parallel;}
else if (crysNum == 42) {return Perpendicular;}
else {return Double;}
break;
case 41:
if (crysNum == 40) {return Parallel;}
else if (crysNum == 43) {return Perpendicular;}
else {return Double;}
break;
case 42:
if (crysNum == 43) {return Parallel;}
else if (crysNum == 40) {return Perpendicular;}
else {return Double;}
break;
case 43:
if (crysNum == 42) {return Parallel;}
else if (crysNum == 41) {return Perpendicular;}
else {return Double;}
break;
case 44:
if (crysNum == 45) {return Parallel;}
else if (crysNum == 47) {return Perpendicular;}
else {return Double;}
break;
case 45:
if (crysNum == 44) {return Parallel;}
else if (crysNum == 46) {return Perpendicular;}
else {return Double;}
break;
case 46:
if (crysNum == 47) {return Parallel;}
else if (crysNum == 45) {return Perpendicular;}
else {return Double;}
break;
case 47:
if (crysNum == 46) {return Parallel;}
else if (crysNum == 44) {return Perpendicular;}
else {return Double;}
break;
case 52:
if (crysNum == 53) {return Parallel;}
else if (crysNum == 55) {return Perpendicular;}
else {return Double;}
break;
case 53:
if (crysNum == 52) {return Parallel;}
else if (crysNum == 54) {return Perpendicular;}
else {return Double;}
break;
case 54:
if (crysNum == 55) {return Parallel;}
else if (crysNum == 53) {return Perpendicular;}
else {return Double;}
break;
case 55:
if (crysNum == 54) {return Parallel;}
else if (crysNum == 52) {return Perpendicular;}
else {return Double;}
break;
case 56:
if (crysNum == 57) {return Parallel;}
else if (crysNum == 59) {return Perpendicular;}
else {return Double;}
break;
case 57:
if (crysNum == 56) {return Parallel;}
else if (crysNum == 58) {return Perpendicular;}
else {return Double;}
break;
case 58:
if (crysNum == 59) {return Parallel;}
else if (crysNum == 57) {return Perpendicular;}
else {return Double;}
break;
case 59:
if (crysNum == 58) {return Parallel;}
else if (crysNum == 56) {return Perpendicular;}
else {return Double;}
break;
default:
throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
break;
}
}
问题仍然存在,是否有任何技巧可以缩短它?更高效?更具可读性?
提前致谢!
我认为您可以将几乎所有内容移动到一个简单的表中,并通过单个表查找来逃避。我没有详细研究过你的条件,但看起来这样的事情就可以了:
// fill the following table in advance using your existing function, or hard-code the
// values if you know they will never change:
ScatterType hitTable[60][60];
ScatterType EventBuffer::checkDoubleHit(int det)
{
// read the crystal Nums once:
unsigned a = evList[cryList[0]].crystalNum;
unsigned b = evList[cryList[1]].crystalNum;
switch(det)
{
case 10:
case 11:
case 13:
case 14:
// better safe than sorry:
assert (a < 60);
assert (b < 60);
return hitTable[a][b];
break;
default:
throw string("made it to default case in checkDoubleHit switch statement, something is wrong");
break;
}
}
使其更短的一种解决方案是在比较/切换之前对晶体值进行分类
int nummin=evList[crysList[0]].crystalNum;
int nummax=evList[crysList[1]].crystalNum;
if (nummin > nummax)
{
tmp = nummin;
nummin = nummax;
nummax = tmp;
}
// or like Jarod42 said: std::minmax(numin, numax);
if ((nummin == 40 && nummax == 41) || // no need to compare the reverse
(nummin == 42 && nummax == 43)) // and reduce haft of the comparison
{ ... }
结合4x4查找表和按位运算的解决方案。
说明:这对于所有四种情况都是相同的,所以让我们看一下det=10
的情况。
在这种情况下,有趣的数字是{40,41,42,43}。如果我们在二进制表示中查看这些数字,就会出现一个漂亮的模式。
上面的数字是我们的面具,由det*4
计算。所以在这种情况下我们的面具是10*4=40
。
0b101000 (40) 0b101000 0b101000 0b101000
^0b101000 (40) ^0b101001 (41) ^0b101010 (42) ^0b101011 (43)
=0b000000 =0b000001 =0b000010 =0b000011
在掩码和我们允许的数字之间进行xor(^
)之后,我们看到它们在集合{0,1,2,3}中都有一个值。如果是,例如num1 ^ mask < 4
,这意味着,只有最右边的两个位与mask
不同,或者num1
最多比mask
大3。如果num1 < mask
至少剩下最右边的两个位将会翻转,而num1 ^ mask
将至少为4.所以如果num1 ^ mask < 4
为真,num1
位于{40,41,42,43}。
此外,因为它位于{0,1,2,3},我们也可以将它用作查找表中的索引。
现在,如果前面的计算对于num1
和num2
都是正确的,我们有一个查找表的组合索引。
int checkDoubleGamma(int det){
static const int hitTable[4][4] = {
{Double, Parallel, Perpendicular, Double},
{Parallel, Double, Double, Perpendicular},
{Perpendicular, Double, Double, Parallel},
{Double, Perpendicular, Parallel, Double}
};
const int num1 = evList[crysList[0]].crystalNum;
const int num2 = evList[crysList[1]].crystalNum;
switch(det) {
case 10: //0b101000
case 11: //0b101100
case 13: //0b110100
case 14: //0b111000
{
const unsigned int mask = 4 * det;
const unsigned int a = num1 ^ mask;
if(a < 4){
const unsigned int b = num2 ^ mask;
if(b < 4)
return hitTable[a][b];
}
return Double;
}
default:
throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
break;
}
//Never reaches here
}
编辑:这可以减少到1x4查找表
如果我们查看qorxswpoi的xor-table,我们会发现它看起来与我们的4x4查找表非常相似。
a^b
这给了我们
^ 00 01 10 11
00 00 01 10 11
01 01 00 11 10
10 10 11 00 01
11 11 10 01 00
因此,我们可以将旧的查找表减少到1x4查找表,并使用00,11 = Double
01 = Parallel
10 = Perpendicular
作为索引。
通过仔细观察a^b
,我们看到a^b
等于num1^mask^num2^mask
,然后我们将其用作索引并保存xor指令。
这仍然检查num1^num2
是否在num2
内。如果{40,41,42,43}
在两个最右边的位中仅与num1
不同,并且mask
在两个最右边的位中仅与num2
不同,那么num1
仅在最右边的两位中与num2
不同。因此,遗漏mask
不会改变程序的行为。
num2^mask