【问题标题】:Best way to check if there is a 0 in an integer检查整数中是否有0的最佳方法
【发布时间】:2013-06-28 03:11:53
【问题描述】:

检查整数中是否有0(零)的最佳方法是什么

例如:

505 -> True
555 -> False
44444032 -> True
0000 -> True

我试过了

public bool has0(int no)
{
    if(no==0)return true;
    while(no!=0)
    {
        if(no%10==0)return true;
        no=no/10;
    }
    return false;
}

这行得通,但是考虑到我需要专门针对大数调用此方法 10 亿次 次这一事实,特别是在大数上需要时间

for(int i=0;i<1000000000;i++)has0(i);

那么,使用|&amp;^ 等位级运算符或任何其他方式来检查数字中是否存在 0 的最佳方法是什么。

谢谢..

【问题讨论】:

标签: optimization language-agnostic


【解决方案1】:

只是一个小想法。除以常数可以用乘法和移位序列代替。如果您正在使用的编译器或虚拟机有线索,那么它会自己执行此操作。如果它很愚蠢,您可以通过手动插入乘法和移位序列来获得一些重大的加速。例如,对于 Java HotSpot Client VM,我发现下面的技术(对非负 n 更正)比进行除法的明显方法(已注释掉)快两倍多,快五倍作为使用模数的原始循环。

static boolean has0(int n) {
    do {
        //int divided = n / 10;
        int divided = (int)((n * 0xCCCCCCCDL) >>> 35);
        if (divided * 10 == n) return true;
        n = divided;
    } while (n != 0);
    return false;
}

但是对于脑死亡较少的Java HotSpot Server VM,显而易见的方法已经很快了,而这个技巧并没有帮助,甚至会使其变慢。不幸的是,对于这种非常低级的优化,您必须准备好针对不同的语言和语言实现重新调整它。

在你尝试测量类似的东西之前阅读weird and wonderful complexities of microbenchmarking in Java

【讨论】:

    【解决方案2】:

    虽然接受的答案肯定比原始代码快,但使用查找表的方法会快得多。

    在我运行缓慢的笔记本电脑上,您的原始代码处理 2.5 亿个数字大约需要 28 秒,而接受答案中的代码大约需要 23 秒。使用查找表,只需 7 秒

    看代码就明白为什么了:

    bool HasZero(int num)
    {
        if (num < 100000) return lookup[num];
    
        int upperDigits = num / 100000;
        int lowerDigits = num - (upperDigits * 100000);
    
        return lookup[upperDigits] || lowerDigits < 10000 || lookup[lowerDigits];
    }
    

    代码的最大值为一次除法、一次减法、一次乘法、两次比较和两次数组查找。即使经过优化,逐位计算也可能很多比这更糟糕。并且预先计算查找表需要很短的时间(

    请注意,如果您不使用 32 位整数,代码将无法正常工作,因为那时您需要验证第三组数字(或将查找表的大小从 10^5 增加到10^6;我想它仍然会更快。)

    【讨论】:

    • 返回查找[upperDigits] || lowerDigits
    • 如果内存是一个问题(例如 2 或 3 位数字,因此是 100 或 1000 位的表),或者如果您需要将它与更大的整数一起使用,递归例程只会处理它。这当然是。假定选择的语言支持递归。
    • 对于return语句,三个不同的||s是故意的:我们知道lowerDigits已经小于100000(因为我们做了相当于a % 100000.)的检查
    • 噢!现在我明白了(希望我们有逗号)
    【解决方案3】:

    取模是一种昂贵的整数运算(类似于整数除法)。您可以通过查看数字是否为偶数来消除测试中一半的可能答案。没有奇数的模 10 等于 0。

    如果 ((((no & 0x1) == 0x00) && ((no % 10) == 0)) 返回真;

    您会为偶数支付更多费用,但为奇数支付更少的费用。因此,如果都是偶数,这将无济于事(实际上会受到伤害),但如果是 50/50 甚至 20/80(20% 的奇数),您可能仍然会领先。

    此外,整数乘法的成本较低,因此您可以先进行除法并计算模 10。

    while (no)
    {
      if (no & 0x1))  // odd?
        no /= 10;
      else  // even
      {
        int nNext = no / 10;  // just do integer divide, and calculate modulo in next line
        if ((no - (10 * nNext)) == 0)  // replaces "more expensive" modulo operation with integer multiply and subtraction
          return true;
        no = nNext;
      }
    }
    

    【讨论】:

    • 我的代码用了大约 97 秒来处理 10 亿个数字。您的出色回答将其缩短了 30 秒..感谢您的绝妙想法....跨度>
    【解决方案4】:

    如果 no==0,您的函数将返回 false。除此之外,我看不出你能做得更好。

    【讨论】:

      猜你喜欢
      • 2015-11-03
      • 2011-01-04
      • 2018-02-17
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多