【问题标题】:Iterate bits from left to right for any number对任意数字从左到右迭代位
【发布时间】:2017-11-26 20:41:00
【问题描述】:

我正在尝试在c 中实现模幂(平方和从左到右相乘)算法。

为了从左到右迭代位,我可以使用 link 中解释的掩码 在此示例中,使用的掩码是 0x80,它仅适用于最大为 8 bits 的数字。

为了使它适用于任意位数,我需要动态分配掩码,但这有点复杂。

有没有其他方法可以做到。

提前致谢!

-------------编辑-----------

    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,那么它工作正常。 在运行时选择掩码值似乎是一种开销。

【问题讨论】:

  • 哪一部分复杂?你试过什么?
  • 定义最大数,例如size_t 是您将继续进行的最大值。然后,要获取掩码,请使用size_t mask = ((size_t)1 << (sizeof(size_t) * 8 - 1))。稍后,要遍历您的号码,请使用 size_t mynum = (size_t)input_number 即可。我假设你只使用无符号数字。
  • @GauravPathak 实际问题是什么?
  • @tilz0R 你真的认为它不能吗?提示:阅读标准。是的,有实际机器的字节宽度超过 8 位。
  • 如果您使用从右到左的算法,则无需担心将掩码初始化为适当大的值。

标签: c rsa traversal exponentiation


【解决方案1】:

您的算法似乎不必要地复杂:指数中的位可以以不依赖于整数类型或其最大值的方式从最低有效位到最高有效位进行测试。这是一个简单的实现,对于任何大小的整数都不需要任何特殊情况:

#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。您可以通过将基数、指数和模数作为命令行参数传递来尝试其他数字。

编辑:

如果您必须从最高有效位到最低有效位扫描exponent 中的位,则应将mask 定义为与exponent 相同的类型,如果类型为无符号则以这种方式初始化:

 unsigned long long exponent = 297;
 unsigned long long mask = 0;
 mask = ~mask - (~mask >> 1);

如果类型是有符号的,为了完全可移植,您必须使用来自&lt;limits.h&gt; 的最大值的定义。但是请注意,使用无符号类型会更有效。

 long long exponent = 297;
 long long mask = LLONG_MAX - (LLONG_MAX >> 1);

循环会浪费时间遍历所有最重要的0 位,因此可以先使用更简单的循环来跳过这些位:

 while (mask > exponent) {
     mask >>= 1;
 }

【讨论】:

  • 感谢您的回答,但这是算法的right to left 实现。就我而言,要求是从left to right 实现算法。
【解决方案2】:

如果你想遍历所有位,你首先必须知道你的类型中有多少位。

这是一个令人惊讶的复杂问题:

  • 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) {
    // [...]
}

请注意,应用这个复杂的宏完全没有运行时的缺点,它是一个编译时常量。

【讨论】:

  • 有趣的链接,但由于位移运算符处理的是值,而不是底层表示,因此填充位无关紧要。
  • @DavidBowling 运算符处理值,这就是为什么填充位非常相关的原因。使用sizeof(unsigned) * CHAR_BIT 会给你存储大小,包括 填充位,所以如果有填充位,执行1 &lt;&lt; (sizeof(unsigned) * CHAR_BIT - 1) 会触发未定义的行为,它移动得太远。该宏用于确定实际值位数。
  • @DavidBowling 仍然是一个很好的评论,因为现在,我知道人们怎么会误读我的答案。我会改进措辞。
  • 好吧,我应该更仔细地阅读,但是您的更改确实使问题更加清晰。已经投票了。链接很好,所以谢谢你:)
猜你喜欢
  • 2019-07-10
  • 2022-01-13
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多