Java 检查一个数字被2或3整除是否有一个小技巧?

Java 检查一个数字被2或3整除是否有一个小技巧?,java,python,c++,c,bit-manipulation,Java,Python,C++,C,Bit Manipulation,我正在寻找一个相当于(num%2)==0 | |(num%3)==0的位测试 我可以用num&1替换num%2,但我仍然坚持使用num%3和逻辑or 这个表达式也相当于(num%2)*(num%3)=0,但我不知道这有什么帮助。是的,尽管它不是很漂亮,但你可以做一些类似于旧的“将所有十进制数字相加,直到只剩下一个”的技巧来测试一个数字是否可以被9整除,除了二进制数和可被3整除外。您也可以对其他数字使用相同的原理,但许多基数/除数组合引入了恼人的比例因子,因此您不再只是对数字求和 无论如何,16n

我正在寻找一个相当于
(num%2)==0 | |(num%3)==0
的位测试

我可以用
num&1
替换
num%2
,但我仍然坚持使用
num%3
和逻辑or


这个表达式也相当于
(num%2)*(num%3)=0
,但我不知道这有什么帮助。

是的,尽管它不是很漂亮,但你可以做一些类似于旧的“将所有十进制数字相加,直到只剩下一个”的技巧来测试一个数字是否可以被9整除,除了二进制数和可被3整除外。您也可以对其他数字使用相同的原理,但许多基数/除数组合引入了恼人的比例因子,因此您不再只是对数字求和

无论如何,16n-1可以被3整除,所以可以使用基数16,也就是说,对半字节求和。然后你只剩下一个字节(实际上是5位),你可以查一下。例如,在C#(略经测试)编辑中:蛮力测试,绝对有效

static bool IsMultipleOf3(uint x)
{
    const uint lookuptable = 0x49249249;
    uint t = (x & 0x0F0F0F0F) + ((x & 0xF0F0F0F0) >> 4);
    t = (t & 0x00FF00FF) + ((t & 0xFF00FF00) >> 8);
    t = (t & 0x000000FF) + ((t & 0x00FF0000) >> 16);
    t = (t & 0xF) + ((t & 0xF0) >> 4);
    return ((lookuptable >> (int)t) & 1) != 0;
}


我评论中的诀窍,
x*0xaaaaaaab我想我参加这次聚会有点晚了,但这里有一个比harold的解决方案稍微快一点(也稍微漂亮一点)的解决方案:

bool is_multiple_of_3(std::uint32_t i)
{
    i = (i & 0x0000FFFF) + (i >> 16);
    i = (i & 0x00FF) + (i >> 8);
    i = (i & 0x0F) + (i >> 4);
    i = (i & 0x3) + (i >> 2);
    const std::uint32_t lookuptable = 0x49249249;
    return ((lookuptable >> i) & 1) != 0;
}
这是C++11,但这对这段代码来说并不重要。它还对32位无符号整数进行了蛮力测试。对于前四个步骤中的每一步,它至少为您节省了一点时间。它还可以漂亮地扩展到64位,只需在开始时增加一个步骤

最后两行显然是无耻地取自哈罗德的解决方案(很好,我不会这么优雅地做)

可能的进一步优化:

  • 前两个步骤中的
    &
    操作将通过在拥有它们的体系结构(例如x86)上使用下半寄存器来优化
  • 第三步的最大可能输出为
    60
    ,第四步的最大可能输出为
    15
    (函数参数为
    0xffffff
    )。考虑到这一点,我们可以消除第四步,使用64位的
    可查找的
    ,然后直接切换到第三步之后的步骤。这对于在32位模式下的Visual C++ 2013来说是个坏主意,因为右移位变成了非内联调用,它进行大量的测试和跳跃。然而,如果64位寄存器本机可用,这应该是一个好主意
  • 如果将函数修改为采用64位参数,则需要重新计算上述要点。最后两个步骤(即在开始添加一个步骤后的步骤4和步骤5)的最大输出分别为
    75
    21
    ,这意味着我们不能再消除最后一个步骤

前四个步骤基于这样一个事实:32位数字可以写成

(high 16 bits) * 65536 + (low 16 bits) = 
(high 16 bits) * 65535 + (high 16 bits) + (low 16 bits) = 
(high 16 bits) * 21845 * 3 + ((high 16 bits) + (low 16 bits))
所以整件事可以被3整除,当且仅当右括号可以被3整除。以此类推,因为这适用于
256=85*3+1
16=5*3+1
,以及
4=3+1
。(当然,这通常适用于2的偶数次幂;奇数次幂比3的最近倍数小1。)


在某些情况下,输入到以下步骤中的数字将分别大于16位、8位和4位,但这不是问题,因为右移时不会删除任何高阶位

是的,确实不太漂亮。我将对它进行一些测试。谢谢:)@barakmanos您可以通过乘法、旋转(在本例中实际上没有旋转,但通常可能有旋转)和比较来做一些更漂亮的事情,但这不是按位的。你对它感兴趣吗?只要它不包含循环-是的(我在问题中写它不是为了让它清晰和简单)。谢谢。那是2号和3号还是3号?另外,你确定没有任何东西在“某种程度上”捕捉到GCD为6大于1的数字吗?这只是为了3的可整除性,我不确定如何将它们组合在一起,我想:对于3,代码可以检查&0x03,&0x06,&0x09,&0x0C,&0x0F,它应捕获所有可被整除的值3@user3629249那么0x12呢?请注意,具有常数除数的模运算可以非常有效地实现,并且常用的编译器已经包含了这些有效的方法。如果
num
是无符号整数类型,则生成的代码可能会快一点。您可能希望检查二进制可执行文件的反汇编(例如,通过
objdump
)进行确认。如果目标是速度,您可能还想研究使用逻辑or而不是布尔or。@njuffa:Yep,我现在看到了
((num%2)|(num%3))==0
选项。好主意,谢谢(介意我把它编辑成问题吗?)。至于另一个建议,我可以在调试代码时直接查看dis-assembly,但我要寻找的是无论底层架构(编译器+硬件)如何都能工作的东西。当然,某些DSP可能在硬件级别上支持这一点(因此,随它们提供的编译器也将支持这一点),但这是一个依赖于平台的解决方案。我正在寻找一种不可知(跨平台)的解决方案。@njuffa大多数编译器使用传统的“常数除法”技巧,然后将其相乘并比较数字是否与以前相同。当然,它比使用实际除法要快,但我从未见过编译器能正确地完成它。我在GCC、Clang和ICC上试过。
mov eax, edi
mov edx, -1431655765
mul edx
shr edx
lea eax, [rdx+rdx*2]
cmp edi, eax
sete    al
movzx   eax, al
ret
imul eax, edi, 0xaaaaaaab
cmp eax, 0x55555555
setbe al
movzx eax, al
ret
bool is_multiple_of_3(std::uint32_t i)
{
    i = (i & 0x0000FFFF) + (i >> 16);
    i = (i & 0x00FF) + (i >> 8);
    i = (i & 0x0F) + (i >> 4);
    i = (i & 0x3) + (i >> 2);
    const std::uint32_t lookuptable = 0x49249249;
    return ((lookuptable >> i) & 1) != 0;
}
(high 16 bits) * 65536 + (low 16 bits) = 
(high 16 bits) * 65535 + (high 16 bits) + (low 16 bits) = 
(high 16 bits) * 21845 * 3 + ((high 16 bits) + (low 16 bits))