【问题标题】:Checking to see if 2 numbers in array sum to 0 in Ruby在 Ruby 中检查数组中的 2 个数字总和是否为 0
【发布时间】:2014-08-08 23:49:27
【问题描述】:

我已经解决这个问题几个小时了,但我不明白为什么我不能让它正常运行。这种方法的最终结果是在一个数组中有 2 个数字相加时等于 0。这是我的代码:

def two_sums(nums)
    i = 0
    j = -1
    while i < nums.count
        num_1 = nums[i]
        while j < nums.count
        num_2 = nums[j]
            if num_1 + num_2 == 0
                return "There are 2 numbers that sum to zero & they are #{num_1} and #{num_2}."
            else
                return "Nothing adds to zero."
            end             
        end
    i += 1
    j -= 1
    end
end

我遇到的问题是,除非数组中的第一个和最后一个数字是同一个数字的正数和负数,否则这将始终返回 false。

例如,如果我有一个 [1, 4, 6, -1, 10] 的数组,它应该返回 true。我确定我的 2 while 语句是造成这种情况的原因,但我想不出办法来解决它。如果有人能指出我正确的方向,那将很有帮助。

【问题讨论】:

    标签: ruby arrays


    【解决方案1】:

    你可以像这样找到第一对加起来为 0:

    nums.combination(2).find { |x, y| x + y == 0 }
    #=> returns the first matching pair or nil
    

    或者如果你想选择所有加起来为 0 的对:

    nums.combination(2).select { |x, y| x + y == 0 }
    #=> returns all matching pairs or an empty array
    

    因此你可以像这样实现你的方法:

    def two_sums(nums)
      pair = nums.combination(2).find { |x, y| x + y == 0 }
      if pair
        "There are 2 numbers that sum to zero & they are #{pair.first} and #{pair.last}."
      else
        "Nothing adds to zero."
      end
    end    
    

    或者如果你想找到所有对:

    def two_sums(nums)
      pairs = nums.combination(2).select { |x, y| x + y == 0 }
      if pairs.empty?
        "Nothing adds to zero."
      else
        "The following pairs sum to zero: #{pairs}..."
      end
    end    
    

    【讨论】:

    • 对于小型案例,这很好。但是因为这些方法是 O(n^2) 如果你有大案子你会想要一个 O(nlogn) 或 O(n) 方法
    • 你是绝对正确的@JKillian。首先对数组进行排序并使用来自数组两端的指针来查找匹配对会更有效。但在我看来,这个问题是关于使代码工作和可读,而不是关于改进运行时。
    • 同意,您的答案适用于大多数情况。为了好玩(和理论上的效率),我使用哈希发布了一个答案。
    【解决方案2】:

    这是另一种方式:

    代码

    def sum_to_zero(arr)
      arr.group_by { |e| e.abs }
         .values
         .select { |a| (a.size > 1 && a.first == 0) || a.uniq.size > 1 }
    end
    

    示例

    sum_to_zero [1, 4, 6, -1, 10]     #=> [[1, -1]]
    sum_to_zero [1, 4, 1, -2, 10]     #=> []
    sum_to_zero [1, 0, 4,  1,  0, -1] #=> [[1, 1, -1], [0, 0]]
    

    这种方法比较快。让我们用一个包含 200,000 个元素的数组来尝试一下,每个元素都是介于 -500,000 和 500,000 之间的随机数。

    require 'time'
    
    t = Time.now
    arr = Array.new(200_000) { rand(1_000_001) - 500_000 }
    arr.size #=> 200000
    
    sum_to_zero(arr).size #=> 16439
    Time.now - t
      #=> 0.23 (seconds) 
    
    sum_to_zero(arr).first(6)
      #=> [[-98747, 98747],
      #    [157848, -157848],
      #    [-459650, 459650],
      #    [176655, 176655, -176655],
      #    [282101, -282101],
      #    [100886, 100886, -100886]]
    

    如果您希望对总和为零的非负值和负值进行分组:

    sum_to_zero(arr).map { |a| a.partition { |e| e >= 0 } }.first(6)
      #=> [[[98747], [-98747]],
      #    [[157848], [-157848]],
      #    [[459650], [-459650]],
      #    [[176655, 176655], [-176655]],
      #    [[282101], [-282101]],
      #    [[100886, 100886], [-100886]]]
    

    如果您只想为每个组提供一个值(例如,一个非负值):

    sum_to_zero(arr).map { |a| a.first.abs }.first(6)
      #=> [98747, 157848, 459650, 176655, 282101, 100886]
    

    【讨论】:

    • 整洁的方法,我认为这也应该是线性时间,因为 group_by 应该是线性的,而 .uniq 应该只需要遍历每个值一次,所以也应该是线性时间。就像一个小小的挑剔一样,我相信 a.size &gt; 1 条件在您的主要方法定义中是多余的
    • @JKillan,我们必须确保包含零的数组的大小必须至少为两个。然而,我所拥有的可以改进,并且我已经修复了。感谢您的提及。
    【解决方案3】:

    我认为最 Ruby 的方式是:

    nums.combination(2).any? { |x,y| (x+y).zero? }
    

    【讨论】:

      【解决方案4】:

      这是一种适用于大型数组的方法。上面遍历两个数字的所有可能组合的方法对于小情况来说非常好,但是对于包含大量元素的数组来说会非常慢并且内存很消耗。

      def two_sums nums
        h = Hash.new
        nums.each do |n|
          return true if h[-n]
          h[n] = true
        end
        false
      end
      

      【讨论】:

      • 我觉得这段代码很丑!有什么想法让它看起来更好,但功能上保持不变?
      • 非常好!我修改它以返回所有满足求和为零的要求的 n >= 0 并针对我在答案中给出的 200,000 个元素的随机数组运行它。这是闪电般的速度!这是代码:h = {}; nums.each_with_object([]) { |n,a| h[n] = true; a &lt;&lt; n if h[-n] }.map(&amp;:abs).uniqtwo_sums(arr).first(6) #=&gt; [361248, 209826, 277499, 324861, 56859, 112086]
      • require 'set'; nums.each_with_object(Set.new) { |n,s| h[n] = true; s &lt;&lt; n.abs if h[-n] }.to_a.
      【解决方案5】:

      好吧,鉴于它被标记为#ruby,这是我能想到的解决这个问题的最“红宝石方式”:

      def two_sums(arr)
        numbers = arr.combination(2).select { |a| a.reduce(:+) == 0 }.flatten
      
        if numbers.empty?
          "Nothing adds to zero."
        else
          "There are 2 numbers that sum to zero & they are #{numbers.first} and #{numbers.last}."
        end
      end
      

      【讨论】:

      • 哇,我对 Ruby 还是很陌生,所以我能识别 90% 的代码。还没有使用 .reduce 或 .combination。感谢您的帮助 wicz! :)
      • 既然您知道a 是一个二元数组,那么使用a.reduce(:+) 是一种非常复杂的计算x+y 的方法。就做select { |x,y| x+y == 0 }
      【解决方案6】:
      array.combination(2).select{|x|x[0] + x[1] == 0}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-29
        • 2021-08-28
        • 2020-03-11
        • 1970-01-01
        • 2011-12-28
        • 2014-06-27
        • 2015-02-21
        • 2012-10-12
        相关资源
        最近更新 更多