【问题标题】:Divisiblity of 5 without using % and / operator不使用 % 和 / 运算符的 5 整除
【发布时间】:2013-06-14 16:59:04
【问题描述】:

如何在不使用 % 和 / 运算符的情况下检查一个数是否可​​被 5 整除。 我想要一个最快的算法来解决这个问题。

【问题讨论】:

  • 太棒了!为什么需要这样做?到目前为止,您尝试过什么?
  • 将数字转换为字符串,提取最后一个“字符”并查看它是 0 还是 5 – 或者其他什么......?
  • @Oli Charlesworth - 用数字减去 5 直到我得到 0 或负数。 0 表示能被 5 整除,负数表示不能被 5 整除。但是对于一个大数字,它会花费太多时间。
  • @user2484070:没有任何机制可以像简单地执行% 一样快。所以我建议使用%!
  • +1 任务不清楚,可能 OP 希望有人做他们的作业,但我们有一些很好的答案,可能对有这个问题的人非常有帮助。跨度>

标签: c algorithm


【解决方案1】:

一个很好的起点是研究如何通过乘法和位移来完成除法。 This question 是一个值得一看的地方。

特别是,您可以按照随附的帖子来了解以下策略。首先,使用乘法和位移“除以 5”:

 int32_t div5(int32_t dividend) {
     int64_t invDivisor = 0x33333333;
     return 1 + (int32_t) ((invDivisor * dividend) >> 32);
 }

然后,将结果乘以 5:

int result = div5(dividend) * 5;

那么,result == dividend 当且仅dividend 能被 5 整除。

if(result == dividend) {
    // dividend is divisible by 5
}
else {
    // dividend is not divisible by 5
}

【讨论】:

  • 如果说股息是5^5,你必须循环运行它,对吗?我想这仍然是一个很好的运行时。
  • @roliu:不,没有循环。 div5(3125) 的结果将是 625,它乘以 53125,它等于 3125 可以被 5 整除的被除数。注意div5(3125 + 1) 的结果将是625 但乘以5 得到3125 不是3125 + 1 表示3125 + 1 不能被5 整除。
  • 这个公式给出了一个近似值,事实上,它将返回num/5 - 1。因此,您需要在 return 语句中使用 + 1。这仅适用于除数为 5 的特殊情况。
  • @Jason 哦,对了。出于某种原因,我将dividend 读作quotient...不太清楚为什么。所以基本上这可以总结为x / 5 * 5 = x(带整数除法)iffx % 5 = 0。然后你用幻想代替整数除法。
【解决方案2】:

我认为需要这种算法有两个原因:(1) 作业,或 (2) 为没有高效除法指令的微控制器编写高效代码。假设你的原因是第二个,但考虑到它可能是第一个的可能性,我不会给你一个完整的解决方案,但会建议如果你把你的数字分成每个是四位的倍数的块,仅当原始数字为时,所有这些块的总和才能被 5 整除;请注意,在执行此类计算时,您必须避免溢出,或者将已发生的溢出次数添加到结果中。我不知道在 C 中执行后者的任何有效方法,但在许多机器语言中它很容易。举个简单的例子,在 8051 上,如果有一个 32 位整数,可以这样:

    mov a,Number   ; Byte 0
    add a,Number+1 ; Byte 1
    adc a,Number+2 ; Byte 2, plus carry from last add
    adc a,Number+3 ; Byte 3, plus carry from last add
    adc a,#0       ; Add in carry, if any (might overflow)
    adc a,#0       ; Add in carry, if any (can't overflow)

请注意,在机器代码中,将进位加回数字比执行 16 位数学运算要快得多。

一旦该值减小到 0-255 的范围内,就可以将高 4 位添加到低 4 位,以获得 0 到 30 范围内的值。可以测试七个这样的值,它们是5 的倍数,或努力进一步减少可能值的数量 [例如如果该值至少为 15,则减 15;如果至少 10,则减 10;如果是 5,则减 5;如果为零,则为五的倍数]。

【讨论】:

  • @OliCharlesworth: (16%5) 的值是多少? (16%5) 的平方值是多少?立方?升到492次方? 255%5 的值是多少?使一个进位值加 1 而不是 256 将导致每次溢出后的值比进位加 256 时的值少 255。255%5 的值是多少?
  • 啊哈,是的,这是有道理的。 (a+16)%5 == (a+1)%5。好东西!
  • 我发现代码 cmets 中的溢出参数令人信服,如果不严格的话。 “向后工作”,adc  A, Number+3 的结果可能看起来和 0x1ff 一样大,需要adc  A, #0。但是向前推进,A(和进位)将包含至少一个零位,以 add  A, Number+1 的结果开头 - 这里没有添加进位,以字节为单位,后面的零已经 某处 .
【解决方案3】:

让我们表示以 2 为底的数字。我们有:

abcdefgh*101 = ABCDEFGHIJ

+abcdefgh00
+  abcdefgh
 ----------
 ABCDEFGHIJ

我们得到ABCDEFGHIJ,想找到abcdefgh

如果你交替使用 - 和 + ABCDEFGH 连续右移 2,你会得到......

+  ABCDEFGH
-    ABCDEF
+      ABCD
-        AB
-----------
+  abcdefgh
+    abcdef
-    abcdef
-      abcd
+      abcd
+        ab
-        ab
-----------
   abcdefgh

答案!

【讨论】:

  • 当你得到除法结果时,你可以将它乘以 5 来检查余数
【解决方案4】:

它终于解锁了,所以我可以解释一下我的评论,顺便说一下,它生成的代码比 GCC 为x % 5 == 0 生成的代码更好。见here,填写

#include <stdint.h>
bool divisible_by_5(uint32_t x)
{
   return x % 5 == 0;
}
bool divisible_by_5_fast(uint32_t x)
{
   return x * 0xCCCCCCCD <= 0x33333333;
}

我将假设无符号输入,因为 OP 建议了一种仅适用于正输入的算法。这个方法可以扩展为有符号输入,但是有点乱。

0xCCCCCCCD 是 5 的 modular multiplicative inverse,模 232。将 k 的倍数(例如,n * k)乘以(模)乘法逆元相当于除以 k,因为

(n * k) * inv(k) =
// use associativity
n * (k * inv(k)) =
// use definition of multiplicative inverse
n * 1 =
// multiplicative identity
n

2 的模幂,一个数有一个模乘逆,当且仅当它是奇数。

由于乘以奇数是可逆的并且实际上是双射,它不能将任何非 k 的倍数映射到 0 - (232-1)/k 范围。

所以当它超出这个范围时,它不可能是 k 的倍数。

0x33333333 是 (232-1)/5,所以如果 x * 0xCCCCCCCD 更高,x 不能是 5 的倍数。

【讨论】:

【解决方案5】:

添加所有字节并检查(通过查表)总和是否可被 5 整除。

【讨论】:

  • 实际上可行,因为 256 % 5 = 1,因此每个数字的“乘数”为 1(即,只需将它们相加即可)。如果它不是 1,那么带有数字和的算法仍然有效,但您必须将每个数字按一定的比例缩放。
  • @harold 在想bits。如果 Egor 编辑它(-1 锁定),我会很高兴 +1。
【解决方案6】:

继续减去 5 的倍数,例如 50、500,100 等。从大数字开始。如果结果为负数,则减去一个较小的数字,直到达到 0。否则该数字不可整除。

【讨论】:

    【解决方案7】:
    bool trythis(int number){
      Int start = number;
      Do{
        start = start - 5;
      } while (start > 5)
    
      If (start == 5 || start == 0) {
        Return true;
      } else return false;
    }
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    【解决方案8】:

    在十进制中,如果数字之和可被 3 或 9 整除

    ,则数字可被 3 或 9 整除

    这同样适用于 b - 1base b 中的任何除数。例如,我们可以将基数为 16 的数字相加,然后取模 3、5 或 15 得到模 3、5 或 15 的数。见How to find x mod 15 without using any Arithmetic Operations?

    事实上,我们可以在任何基数 24k 中检查被 5 整除,例如 16、256、4096...

    使用该属性,我们有以下解决方案

    unsigned mod5(unsigned x) {
        unsigned mod = 0;
        while (x) {
            mod += x & 0x0F;
            x >>= 4;
        }
        while (mod >= 15)
        {
            if (mod == 15) return 0;
            mod = (mod >> 4) + (mod & 0x0F);
        }
        return mod;   
    }
    

    或者可以在最后一步使用位查找表进一步优化like this

    unsigned isDivisibleBy5(unsigned x) {
        x = (x >> 16) + (x & 0xffff);
        x = (x >> 8)  + (x & 0x00ff);
        x = (x >> 4)  + (x & 0x000f);
        return !!((0x1084210842108421ULL >> x) & 1);
    }
    

    【讨论】:

    【解决方案9】:

    Typecast 或转换成字符串,然后看最后一个字符是 5 还是 0。

    【讨论】:

      猜你喜欢
      • 2012-12-02
      • 1970-01-01
      • 1970-01-01
      • 2017-02-27
      • 2019-06-01
      • 2016-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多