在3位数字上使用逻辑或关系运算符进行素性测试

时间:2019-03-19 21:16:34

标签: c logical-operators primality-test relational-operators

我期待使用逻辑和关系运算符检查3位数字是否为素数,该数字由3个变量表示,其中7-1位设置为0,只有位置0上的位是实际数据。假设我们有:

unsigned char x3, x2, x1;

可以假定素数是函数f,如果该数是素数,则输出1,否则输出0

如何使用最优化的按位运算(逻辑运算符)解决此问题?可以假定可以从K.V.中提取最小合取/取和形式。真值表的示意图。

如何使用关系运算符解决此问题?

哪个会更快?

一些有用的数据:

CDF: (~x2 & X1) | (X0 & X2)
CCF: (X1 | X2) & (X0 | ~X2)

3 个答案:

答案 0 :(得分:3)

按位

我认为您在这里能做的最好的事情就是(x3 & x1) | (~x3 & x2)。在布尔代数中,这将表示为AC + (!A)B*似乎没有适用于简化布尔代数表达式的常用规则,并且似乎也有一些在线布尔代数表达式简化器。

* (第二个A通常会写在上面,但我不知道如何在降价中这样做)。

因此,您将获得如下信息(使用uchar作为unsigned char的简写):

uchar f_bitwise(uchar x3, uchar x2, uchar x1) 
{
   return (x3 & x1) | (~x3 & x2);
}

由此产生的程序集(带有-O0并丢弃了函数调用开销),如下所示:

movzx   eax, BYTE PTR [rbp-4]  # move x3 into register eax
and     al, BYTE PTR [rbp-12]  # bitwise AND the lower half of eax with x1
mov     ecx, eax               # store the result in ecx
cmp     BYTE PTR [rbp-4], 0    # compare x3 with 0
sete    al                     # set lower half of eax to 1 if x3 was equal to 0
mov     edx, eax               # store the result in edx (this now equals ~x3)
movzx   eax, BYTE PTR [rbp-8]  # move x2 into eax
and     eax, edx               # bitwise AND ~x3 (in edx) with x2 (in eax)
or      eax, ecx               # finally, bitwise OR eax and ecx

结果存储在eax中。

逻辑

查看值0-7的位,并尝试辨别要键入的简单模式,您会发现对于值0-3,当且仅当x2为1时,该数为质数。同样,对于值4-7,当且仅当x1为1时,该数为质数。此观察结果产生一个简单表达式:x3 ? x1 : x2

我无法证明这是使用逻辑运算符的最短表达式,因此,如果某人的版本较短,则一定要将其发布在注释中。但是,考虑到这实际上只是一个逻辑运算符,似乎不太可能有一个较短的版本,因为您可以看到是否将三元运算符扩展为适当的if / else

uchar f_logical(uchar x3, uchar x2, uchar x1) 
{
   if (x3 != 0) 
      return x1;
   else
      return x2;
}

由此产生的程序集如下(再次使用-O0,并且不计算函数调用的开销):

cmp     BYTE PTR [rbp-4], 0      # compare x3 with 0
je      .L2                      # if equal, jump to label L2
movzx   eax, BYTE PTR [rbp-12]   # move x1 into register eax
jmp     .L4                      # jump to label L4 (i.e., return from function)
.L2: 
movzx   eax, BYTE PTR [rbp-8]    # move x2 into register eax
.L4:
# Function return. Result is once again stored in eax.

我还没有测试这两个函数的性能,但是仅从汇编来看,似乎几乎可以肯定f_logical的运行速度会比f_bitwise快。它使用的指令少得多,尽管更少的指令并不总是等同于更快的指令,但是这些指令似乎都没有特别昂贵的CPU周期。

如果您取消了两个功能共有的指令并比较了剩下的内容,则会得到:

f_logicaljejmp

f_bitwiseand(2),mov(2),seteor

关于为什么的逻辑版本较短,我认为答案是分支。仅按位运算而没有分支,您必须在单个表达式中考虑所有可能性。

例如,在(x3 & x1) | (~x3 & x2)中,最好除掉右侧的~x3,因为已经知道x3假设右侧代表0-3值的检验,则此处为零。但是计算机无法知道这一点,因此您无法将其分解为一个更简单的表达式。

具有分支功能,您可以使用单个比较运算符将问题分为两个子问题。同样,这是可行的,因为对于值0-3,x2位本质上是一个“是素数”位,对于值4-7,x1位是一个“是素数”位。 / p>

此外,alinsoar是正确的,因为查找表会更快,但前提是值不是分成多个位。将位值放在单独的变量中,您要么必须使用x3<<2 | x2<<1 | x1之类的方法来重建数字,要么必须将查找表定义为3D数组,在这种情况下,编译器会生成大量额外的指令来执行索引3D数组所需的地址算法。

答案 1 :(得分:1)

由于没有太多输入,因此可以定义一个预先计算的表PRIME,该表在质数位置上有1个,在其余位置上有0个。

例如,PRIME(0,1,1)= 1而PRIME(1,0,1)= 0,即PRIME(3)= true,PRIME(6)= false。

答案 2 :(得分:1)

较短的解决方案是:

int isPrime(unsigned char x3, unsigned char x2, unsigned char x1) {
  return x1 | (x2 & ~x3);
}
  • x1用于匹配所有奇数。在区间[1..7]中,它们都是素数。
  • (x2 & ~x3)要匹配值2(实际上它匹配2和3)。

使用Compiler Explorer,您可以比较由各种体系结构上的各种编译器生成的代码。 gcc x86_64与ARM64的示例:https://godbolt.org/z/JwtES4

注意:对于像#define这样的小型函数,比函数调用更快,更短。

#define isPrime(x3,x2,x1) ((x1) | ((x2) & ~(x3)))