【发布时间】:2015-04-20 21:36:13
【问题描述】:
考虑以下函数:
inline unsigned int f(unsigned int n, unsigned int p)
{
return (n*2-1)%p;
}
现在假设n(和p)大于std::numeric_limits<int>::max()。
例如f(4294967295U, 4294967291U)。
数学结果是7,但函数会返回2,因为n*2会溢出。
那么解决方案很简单:我们只需要使用 64 位整数来代替。假设函数的声明必须保持不变:
inline unsigned int f(unsigned int n, unsigned int p)
{
return (static_cast<unsigned long long int>(n)*2-1)%p;
}
一切都很好。至少在原则上。问题是这个函数将在我的代码中被调用数百万次(我的意思是溢出版本),并且 64 位模数比 32 位版本慢得多(例如,参见 here)。
问题如下:是否有任何技巧(数学或算法)来避免执行 64 位版本的模运算。使用这个技巧的f 的新版本会是什么? (保持相同的声明)。
- 注1:
n > 0 - 注2:
p > 2 - 注3:
n可以低于p:n=4294967289U,p=4294967291U - 注4:使用的模运算次数越少越好(3 32位模太大,2很有趣,1肯定会胜过)
- 注意 5:当然结果将取决于处理器。假设在最后一台可用的至强超级计算机上使用。
【问题讨论】:
-
你应该使用一个更好的例子,否则你会得到便宜的答案,利用
n没有预先减少 -
为什么不妥协?仅当答案可能溢出时(即如果 n > 0x80000000),才执行 64 位模数。如果你不经常用大
n调用它,那么这会很好用。 -
@nneonneo:我在帖子里加了个注释,函数经常会被大
n调用。 -
顺便问一下,您愿意接受 x86 汇编中的答案吗?你可以回避这个问题的大部分,因为 32 位模确实需要 64 位除法(受到一些限制,但如果
p > 1和n != 0在这里可以解决) -
请注意,这将非常依赖处理器...我使用以下一些方法进行了快速基准测试(即使它们有缺陷,只是为了了解额外的复杂性是否更多成本高于 64 位操作)。在我的旧 Phenom II 955 上,仅以 64 位进行计算甚至比 32 位略好;如果 n*2 会溢出,则切换到 64 位的成本要高出约 20%,执行 (n%p + n%p) 的成本是两倍。 OTOH,在最近的i7上,如果32位需要1,64位需要2,检查高位成本2.2,做(n%p + n%p)成本1.4。
标签: c++ algorithm c++11 math modulus