【问题标题】:Create a method to find if a number is a power of 2?创建一个方法来查找一个数字是否是 2 的幂?
【发布时间】:2015-10-12 23:32:26
【问题描述】:

如果 num 是 2 的幂,我有此代码返回 true

def is_power_of_two?(num)
  result = num.inject(0) {|n1, n2| n2 ** n1}
  if result == num
    true
  else
    false
  end
end

p is_power_of_two?(16)

但我不断收到错误消息。我该如何修复和简化这段代码?

【问题讨论】:

  • 为什么不用log2函数呢?如果输入在 ^2 上,应该返回整数。谷歌log2 1024
  • 不是 Ruby 专家,但您似乎在整数而不是数组上使用注入方法
  • 我尝试使用 num.to_a 将 num 转换为数组,但这对我不起作用
  • Log2 看起来是个好主意,但我怎样才能合并它以确保我只得到整数?

标签: ruby methods fixnum


【解决方案1】:

解决此问题的另一种方法是与此处的大多数答案相反 - 我们可以使用数字 1 开始并找出该数字是否是 2 的幂。像这样:

def power_of_two?(num)
  product = 1

  while product < num
    product *= 2
  end
  product == num
end

我们从 1 开始。然后我们将 1 乘以 2,并继续乘以 2,直到乘积大于 num (product &lt; num)。一旦我们达到那个条件,我们就会停止,退出循环,并检查它是否等于num (product == num)。如果是,num 是 2 的幂。

【讨论】:

    【解决方案2】:

    显然,n 是一个非负整数。

    代码

    def po2?(n)
      n.to_s(2).count('1') == 1
    end
    

    示例

    po2?  0     #=> false
    po2?  1     #=> true
    po2? 32     #=> true
    po2? 33     #=> false
    

    说明

    Fixnum#to_s 提供给定基数的整数(接收者)的字符串表示形式。该方法的参数(默认为 10)是基数。例如:

    16.to_s     #=> "16" 
    16.to_s(8)  #=> "20" 
    16.to_s(16) #=> "10"
    15.to_s(16) #=>  "f"
    

    我们感兴趣的是底数 2。对于 2 的幂:

     1.to_s(2)  #=>      "1" 
     2.to_s(2)  #=>     "10" 
     4.to_s(2)  #=>    "100" 
     8.to_s(2)  #=>   "1000"
    16.to_s(2)  #=>  "10000"
    

    对于一些不是 2 的幂的自然数:

     3.to_s(2)  #=>    "11" 
     5.to_s(2)  #=>   "101" 
    11.to_s(2)  #=>  "1011" 
    

    因此,我们希望匹配包含一个1 的二进制字符串。

    另一种方式

    R = /
        \A      # match beginning of string ("anchor")
        10*     # match 1 followed by zero or more zeroes
        \z      # match end of string ("anchor")
        /x      # free-spacing regex definition mode
    
    def po2?(n)
      (n.to_s(2) =~ R) ? true : false
    end
    
    po2?(4)  #=> true
    po2?(5)  #=> false
    

    还有一个用于公路

    这使用Fixnum#bit_lengthFixnum#[]

    def po2?(n)
      m = n.bit_length-1
      n[m] == 1 and m.times.all? { |i| n[i].zero? }
    end
    
    po2?  0     #=> false
    po2?  1     #=> true
    po2? 32     #=> true
    po2? 33     #=> false
    

    【讨论】:

    • 您能否更深入地了解此答案的语法?可惜没看懂
    • 卡里,我相信没有负数可以是 2 的幂的情况。负数 N 将导致小于 1 的数,正非零 N 将导致数大于1。如果N是负数,用例无效,po2?应该是假的
    • @EA,这是我两年多前写的。回首往事,我想知道当时我在想什么,但也想知道为什么有人(你)花了这么长时间才指出显而易见的事情。谢谢你让我知道。我会编辑。
    【解决方案3】:

    我在训练营应用程序准备中遇到了这个问题。我不是数学人,也不懂其中一些方法,所以我想为像我这样的人提交一个常识方法。这需要很少的数学知识,除了知道一个数字的二次方将是某个数字自身相乘的结果。

    def is_power_of_two?(num)
      num.times  {|n| return true if (n+1) * (n+1) == num}
      false
    end
    

    此方法从 1 开始计数到 ​​num 变量,如果(序列中的任何这些数字乘以自身)等于 num 并且如果 num 不为 0(下面将详细介绍),则返回 true。

    示例: 数 = 9

    1 * 1 == 9 #=> false
    2 * 2 == 9 #=> false
    3 * 3 == 9 #=> true 
    

    返回true,方法完成运行。

    #times 方法需要一个大于 0 的整数,因此这种边缘情况是“处理”的,因为 #times 不使用“0”作为变量,并且在 #times 迭代之外返回 false。

    【讨论】:

    • 虽然起初这对我来说看起来很优雅,但我现在意识到它的效率很低,因为所有的人都出去了,因为它可能通过比获得答案所需的更多的数字。看起来不错。
    【解决方案4】:

    我会做这样的事情,使用 Ruby 的 Math 模块。

    def power_of_two?(n)
      Math.log2(n) % 1 == 0
    end
    

    或者,如果你想变得很酷:

    def power_of_two?(n)
      (Math.log2(n) % 1).zero?
    end
    

    一些IRB输出:

    2.1.0 :004 > power_of_two?(2)
     => true
    2.1.0 :005 > power_of_two?(32768)
     => true
    2.1.0 :006 > power_of_two?(65536)
     => true
    

    这个方法假设输入是一个正整数。

    Source

    【讨论】:

    • (Math.log2(n) % 1).zero?
    • 这也很合理——将添加到答案中。
    • power_of_two? -3 #=&gt; Math::DomainError: Numerical argument is out of domain - "log2".
    • 好点;此方法假定输入为正整数。
    【解决方案5】:

    在我看来,最简单——但可能有点长——做你需要做的事情的方法就是像这样编写这个递归方法:

    def power_of_two?(number)
        continue = true
        if number == 1
            return true
        end
        if number % 2 != 0
            return false
        else
            while continue == true do
                if number.to_f / 2.0 == 2.0
                    continue = false
                    return true
                else
                    if number % 2 != 0
                        continue = false 
                        return false
                    else
                        number /= 2
                        continue = true
                    end
                end
            end
        end
    end
    

    1 是 2 的幂 (2^0),因此它首先检查给定的数字是否为 1。如果不是,则检查它是否为奇数,因为 1 是唯一一个为 2 的幂的奇数。

    如果是奇数,则返回 false 并继续执行 else 语句。它将检查数字除以 2 是否为 2,因为显然它是 2 的幂。它以浮点数的形式执行此操作,因为 Ruby 中的 5/2 将返回 2。

    如果这是错误的,它会再次检查数字是否为奇数——第一轮不需要,之后需要。如果数字不是奇数,它会将数字除以二,然后再循环一次。

    这将一直持续到程序通过获取 2 或任何奇数自行解决,并分别返回 true 或 false。

    【讨论】:

      【解决方案6】:

      这是另一种使用递归的解决方案:

      def power_of_2?(number)
       return true if number == 1
       return false if number == 0 || number % 2 != 0
       power_of_2?(number / 2)
      end
      

      【讨论】:

        【解决方案7】:

        正如上面 cmets 中所指出的,您遇到了错误,因为您尝试在不可迭代(一个 int)上使用 inject 方法。这是使用建议的log2的解决方案

        def is_power_of_two?(num)
          result = Math.log2(num)
          result == Integer(result)
        end
        

        注意:在接近二进制的非常大的数字(如 2 ^ 64 - 1)时会失败。一个万无一失的版本(但速度较慢)是:

        def is_power_of_two?(num)
          while (num % 2 == 0 and num != 0)
            num /= 2
          end
          num == 1
        end
        

        请评论你们任何人可能发现的任何改进。

        【讨论】:

        • 当然我们从不使用conditional_statement ? true : false,因为根据定义conditional_statement本身将评估为truefalse
        • 是的,因为这是我第一次尝试 Ruby,所以我玩得很安全,并使用 OP 的代码作为基础。然后意识到if 是多么毫无意义。
        • 随时更新您的代码以改进它。所以它不会再次被提及,或者其他人不必编辑它。 :) 欢迎来到 Ruby!
        • 当然不是代码审查网站,但在这一点上,我什至不会使用局部变量结果。直接比较两个值,会返回真假,简单易读。
        • @vgoff 你应该来 CR 看看我们,也许顺便去The Second Monitor
        【解决方案8】:

        试试:

        def is_power_of_two?(num)
          num != 0 && (num & (num - 1)) == 0
        end
        

        here 解释得很好(对于 C#,但@GregHewgill 的解释也适用于这里)

        【讨论】:

          猜你喜欢
          • 2010-10-10
          • 1970-01-01
          • 2017-01-09
          • 1970-01-01
          • 2010-09-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多