我正在尝试在c
中实现Modular Exponentiation(方形和从左到右乘法)算法。
为了从左到右迭代这些位,我可以使用这个link中解释的掩码。
在此示例中,使用的掩码为0x80
,该掩码仅适用于最大8 bits
的数字。
为了使其适用于任意数量的位,我需要动态分配掩码,但这会使它有点复杂。
是否还有其他可以解决的解决方案。
提前致谢!
------------- EDIT -----------------------
long long base = 23;
long long exponent = 297;
long long mod = 327;
long long result = 1;
unsigned int mask;
for (mask = 0x80; mask != 0; mask >>= 1) {
result = (result * result) % mod; // Square
if (exponent & mask) {
result = (base * result) % mod; // Mul
}
}
如在此示例中,如果我将使用掩码0x80
,它将无效,但如果我使用0x100
,则它可以正常工作。
在运行时选择掩码值似乎是一种开销。
答案 0 :(得分:4)
如果要迭代所有位,首先要知道类型中有多少位。
这是一个令人惊讶的复杂问题:
sizeof
为您提供字节数,但 字节的字节数超过8位。limits.h
为您提供CHAR_BIT
知道字节中的位数,但即使您将其乘以sizeof
类型,结果仍可能是错误的,因为无符号类型允许包含不属于数字表示的填充位,而sizeof
以字节为单位返回存储大小,其中包含这些填充位 幸运的是,this answer有一个巧妙的宏,可以根据相应类型的最大值计算实际值位的数量:
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
+ (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))
无符号类型的最大值非常容易获得:只需将-1
投射到无符号类型。
所以,总而言之,您的代码可能如下所示,包括上面的宏:
#define UNSIGNED_BITS IMAX_BITS((unsigned)-1)
// [...]
unsigned int mask;
for (mask = 1 << (UNSIGNED_BITS-1); mask != 0; mask >>= 1) {
// [...]
}
请注意,应用这个复杂的宏根本没有运行时缺陷,它是一个编译时常量。
答案 1 :(得分:2)
您的算法似乎不必要地复杂化:来自指数的位可以以不依赖于整数类型及其最大值的方式从最低有效位到最高有效位进行测试。这是一个简单的实现,对于任何大小的整数都不需要任何特殊情况:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
unsigned long long base = (argc > 1) ? strtoull(argv[1], NULL, 0) : 23;
unsigned long long exponent = (argc > 2) ? strtoull(argv[2], NULL, 0) : 297;
unsigned long long mod = (argc > 3) ? strtoull(argv[3], NULL, 0) : 327;
unsigned long long y = exponent;
unsigned long long x = base;
unsigned long long result = 1;
for (;;) {
if (y & 1) {
result = result * x % mod;
}
if ((y >>= 1) == 0)
break;
x = x * x % mod;
}
printf("expmod(%llu, %llu, %llu) = %llu\n", base, exponent, mod, result);
return 0;
}
没有任何命令行参数,它会产生:expmod(23, 297, 327) = 185
。您可以通过将base,exponent和modulo作为命令行参数传递来尝试其他数字。
修改强>
如果必须将exponent
中的位从最高有效位扫描到最低有效位,mask
应定义为与exponent
相同的类型,如果类型为无符号,则以此方式初始化:
unsigned long long exponent = 297;
unsigned long long mask = 0;
mask = ~mask - (~mask >> 1);
如果类型已签名,为了完全可移植性,您必须使用定义作为<limits.h>
的最大值。但请注意,使用无符号类型会更有效。
long long exponent = 297;
long long mask = LLONG_MAX - (LLONG_MAX >> 1);
循环将浪费时间运行所有最重要的0
位,因此可以首先使用更简单的循环来跳过这些位:
while (mask > exponent) {
mask >>= 1;
}