有人可以解释这个解决方案背后的数学

时间:2018-02-01 01:25:10

标签: ruby algorithm range

一个问题:

  • 取一系列从1到n的数字(其中n> 0)。
  • 在该序列中,有两个数字,a和b。
  • a和b的乘积应等于除a和b之外的序列中所有数字的总和。
  • 给出一个数字n,你能告诉我从序列中排除的数字吗?

我的计划是获得范围的总和,然后使用组合枚举器创建一个数组以获得范围的所有可能对,然后检查该对的乘积是否等于范围的总和减去总和这对。这个解决方案有效,但耗时太长了:

def removNb(n)
  arr = [*1..n]
  sum = arr.inject(:+) 
  ab = []

  [*(n/2)..n].combination(2).to_a.each do |pair|
    if pair.inject(:*) == sum - pair.inject(:+) 
        ab << pair 
        ab << [pair[1],pair[0]]
    end 
  end
  ab
end

我找到了一个解决方案:

def removNb(n)
  res = []
  total = (n*n + n) / 2 
  range = (1..n)

  (1..n).each do |a|
    b = ((total - a) / (a * 1.0 + 1.0))
     if b == b.to_i && b <= n
      res.push([a,b.to_i])

    end
  end
  return res
end

但无法理解它是如何运作的。我理解总数背后的等式。

2 个答案:

答案 0 :(得分:4)

你可以形成一个等式

  

a * b =(从1到n的序列之和) - (a + b)

来自此声明

  

a和b 的产品应该等于 所有数字的总和   序列,不包括a和b

从1到n的序列之和(表示为总数)= n(n + 1)/ 2 =(n * n + n)/ 2

重新排序上面的等式,你得到

  

b =(总 - a)/(a + 1)

剩下的工作是测试是否存在与该等式匹配的整数a和b

答案 1 :(得分:0)

代码返回序列中具有所需属性的所有数字对的数组。让我们一步一步。

初始化要返回的数组。

res = []

计算序列中元素的总和。任何算术序列的元素总和等于第一个元素加上最后一个元素,乘以序列中元素的数量,乘以除以2.这是total = n*(1+n)/2,可以表示为

total = (n*n + n) / 2 

range = (1..n)是不必要的,因为后来没有引用range

循环遍历序列的元素

(1..n).each do |a|

对于a的每个值,我们寻找序列b的另一个元素,以便

a*b = total - a - b

解决b

b = (total - a)/ (a * 1.0 + 1.0)

如果b在范围内,请保存对[a, b]

if b == b.to_i && b <= n
  res.push([a,b.to_i])
end

返回数组res

res

此方法包含两个错误:

  • 如果将[a,b]添加到res,则会将其添加两次
  • [a,a]可以添加到res(例如n=5a=b=3

我会写如下。

def remove_numbers(n)
  total = n*(n+1)/2 
  (1..n-1).each_with_object([]) do |a,res|
    next unless (total-a) % (a+1) == 0
    b = (total-a)/(a+1)
    res << [a,b] if (a+1..n).cover?(b)
  end
end

例如,

remove_numbers 10
  #=> [[6, 7]]
remove_numbers 1000
  #=> []

出于诽谤:

(2..10_000).map { |x| [x, remove_numbers(x).size] }.max_by(&:last)
  #=> [3482, 4]
remove_numbers 3482
  #=> [[1770, 3423], [2023, 2995], [2353, 2575], [2460, 2463]]