【问题标题】:Where is the proof for complement of base 10 integer formula? [closed]以 10 为底的整数公式补码的证明在哪里? [关闭]
【发布时间】:2021-01-25 17:31:27
【问题描述】:

所以,对于https://leetcode.com/problems/complement-of-base-10-integer/discuss/879910/Java-100-faster-solution-trick-to-find-2(digits-of-N)-1-greater-(11111)-and-subtract-N-from-it,任何人都可以证明为什么 2^(二进制数字的长度) - 1 给出了完整的 111....1111 二进制数。如果有人可以更整洁地重写这个问题,请这样做。

【问题讨论】:

    标签: java math optimization binary proof


    【解决方案1】:

    任何人都可以证明为什么 2^(二进制数字的长度) - 1 给出了完整的 111....1111 二进制数。

    在(无符号)二进制表示法中,由N个“个”位组成的数字表示

    2^0 + 2^1 + 2^2 + .... + 2^(N-1)
    

    其中 N >= 1。

    所以我们真正需要证明的是命题P(N)

    2^N - 1 = 2^0 + 2^1 + 2^2 + .... + 2^(N-1) where N >= 1.
    

    归纳证明

    1. 基本情况,N = 1。

      2^N - 1 = 2^1 - 1
              = 2 - 1
              = 1
              = 2^0
      
      Thus `P(1)` is proven
      
    2. 鉴于 P(N) 为真,我们需要证明 P(N+1) 也为真;即证明P(N) => P(N+1) where N >= 1

      假设 P(N) 为真;即2^N - 1 = 2^0 + 2^1 + 2^2 + .... + 2^(N-1)

      (2^N - 1) * 2     = 2 * (2^0 + 2^1 + 2^2 + .... + 2^(N-1))
                        = 2 * 2^0 + 2 * 2^1 + 2 * 2^2 + 2 * 2^(N-1)
                        = 2^1 + 2^2 + 2^3 + ... + 2^N
      
      (2^N - 1) * 2 + 1 = 1   + (2^N - 1) * 2
                        = 2^0 + (2^N - 1) * 2
                        = 2^0 + 2^1 + 2^2 + 2^3 + ... + 2^N
      

      但是

      (2^N - 1) * 2 + 1 = 2 * 2^N - 2 * 1 + 1
                        = 2^(N+1) - 2 + 1
                        = 2^(N+1) - 1
      

      因此

      2^(N+1) - 1       = 2^0 + 2^1 + 2^2 + 2^3 + ... + 2^N
      

      因此P(N) => P(N+1) where N >= 1 被证明

    3. 从第 1 步开始,P(1) 被证明

      从第 2 步开始,P(N) => P(N+1) where N >= 1 被证明

      因此,通过归纳,P(N) for all N >= 1 被证明。

    QED。

    【讨论】:

      【解决方案2】:

      链接到代码的效率Math.pow (相对于基本的位操作)非常昂贵,并且计算 N 的位界限的循环也不必要地低效。

      为什么 2^5-1 总是以位为单位,是一个全 1 位的序列?

      因为2^x是二进制数系统的字面定义。如果对你更方便,我们来谈谈十进制:

      10^1 是什么?现在是 10。10^2 是什么?是 100。10^3 是 1000,等等。从其中任何一个中减去 1,您分别得到 999999:最高位(十进制为 9,但二进制为 1)。所以,x^y-1 其中 x 是一个基数(十进制是“基数 10”,二进制是“基数 2”),y 是任何正整数,为您提供该基数中的最高数字,重复 y 次,如果您打印该基数中的数字。 2^18-1 二进制是 18 倍 1。出于同样的原因,10^18-1 给你 18 倍 9

      我们怎样才能更有效地执行 Math.pow(2,n)

      相同的解释:因为2^x 字面意思描述了二进制数学,而计算机非常擅长这些,而按位运算是二进制数学。全部为二进制:

      1 是十进制的 1。 10 是十进制 2。 101 是十进制 5。 1010 是十进制的 10。

      一般的原则是,如果你只是在最后加一个 0,这与将数字乘以 2 相同。回到十进制,如果我给你任何数字,比如说 12493821345,写在一张纸上,我要求你写那个数字乘以 10(记住,十进制 = 以 10 为底,二进制 = 以 2 为底),你只需.. 在末尾添加一个零。二进制相同:末尾加 0 与乘以 2 相同。

      因此,您可以只添加 n 个零,而不是 Math.pow(2, n)。您使用位移运算符执行的操作:“添加 5 个零”与“将位向左移动 5 个位置”相同。除了位移大大更有效。

      因此,如果我想要2^10-1,那么有效的方法是:

      int n = 10;
      int y = (1 << n) -1;
      // y is now 1023, which is indeed 2^10 -1.
      

      因此,应该是int pow = (1 &lt;&lt; n) - 1;

      如何快速从 N=5 变为 111

      您对这个问题的真正理解是:输入数字中最高 1 位的位置是什么。

      二进制中的 5 是 101,所需的数字是 2(因为计算机中的计数是从 0 开始的,位置 2 是您找到第 3 位的位置),如:我们需要 3 个 1 位。换句话说,您的问题是:最高 1 位是多少。 Java 有一个方法:Integer.highestOneBit(5)。这不会返回最高 1 位的位置,而是返回只有该位设置为 1 的数字。换句话说,5 是 101,7 是 111。对于这两者,highestOneBit 返回100 (4)。将其左移 1 并减去 1,然后将 5 和 7 都变成 7,这就是您想要的。

      注意这将如何执行更少的操作。另请注意,这些功能是高度调整的。从字面上看,Java 性能工程师对这些进行了深入研究,以找到在大多数处理器上运行速度最快的实现,同时考虑到预测流水线和 CPU 缓存的变幻莫测。他们倾向于把这种事情做对,所以,你应该使用他们的东西。 Java 是开源的,如果需要,可以查看实现。

      您能否进一步优化?

      但我们当然可以。

      我真正需要做的就是首先翻转所有位,然后“取消翻转”前导零。假设有 5 个字节:0000 0101,我们想要翻转所有位,但不翻转前导零:0000 0010 = 2,这是正确的答案。 Java 有一个“翻转所有位”运算符(NOT 运算符,~),所以我们只需要“翻转所有位”+“将所有前导位归零”。

      这让我们:

      public static int complement(int n) {
      if (n == 0) return 1; // apparently an edge case; I'd say it's 0.
      return ~n & ((Integer.highestOneBit(n) << 1) - 1);
      }
      

      没有循环。 (hOB 的 impl 也没有循环),没有昂贵的数学运算。所有基本的有点玩弄。除了你的边缘案例,单线。

      让我们测试一下吧!

      complement(5) --> 101 -> 010 -> 2
      complement(7) --> 111 -> 000 -> 0
      complement(8) --> 1000 -> 0111 -> 7
      

      似乎工作得很好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-31
        • 2018-07-12
        • 1970-01-01
        • 2010-09-08
        相关资源
        最近更新 更多