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))