查找奇数 int - Ruby 嵌套循环错误

Find the odd int - Ruby Nested Loop Error

我在 codewars 上做这道题:"Given an array, find the int that appears an odd number of times. There will always be only one integer that appears an odd number of times."

代码:

def find_it(seq)
  int = []
  for a in seq do
    count = 0
    for b in seq do
      if a == b
        count += 1
      end
    end
    if count % 2.0 != 0
      int << b
    end
  end      
  puts int.uniq[0].to_i
end

针对几个输入进行了测试,但是这两个数组的答案是错误的:

find_it([1,1,2,-2,5,2,4,4,-1,-2,5]) - returns 5 而不是 -1

find_it([1,1,1,1,1,1,10,1,1,1,1]) - returns 1 而不是 10

我的代码出了什么问题?

if count % 2.0 != 0
      int << b
    end

你在这里遇到的问题是你将 b 而不是 a 推入整数数组,所以发生的事情是你推入了 b 的最后一个值,而不是你计算的值被推入数组中的最后一个值元素不管计数器是奇数的条件,尽管 b 和计数器彼此无关。所以要修复它,您将 b 替换为 a 以便它推入您正在测试的值,与第二个循环中的其他元素进行比较

修复:

if count % 2.0 != 0
      int << a
    end

一个类似但更简单的代码,除了以更短和更容易理解的方式完成类似的工作是:

def find_it(seq)
  numberWithOddCount = 0
  seq.each do |currentElement|
    counter = 0
    seq.each { |elementToCompare| counter += 1 if currentElement == elementToCompare}
    numberWithOddCount = currentElement if counter % 2 != 0
  end
  numberWithOddCount
end

刚刚添加了一些您也可以用来缩短和简化代码的花絮。

编码愉快!

注:

您可以创造性地利用内置的 ruby 方法,使代码在很少的几行(甚至一行)内完成您想要的操作,例如 @iGian 在问题评论中所做的,但是如果如果您还是 ruby 的新手,那么最好在学习这些方法时一个一个地使用它们,否则您会感到困惑。但是如果你现在愿意花时间学习它们,我建议你拿他的代码并将每个方法执行分成单独的行并输出每个方法做了什么以了解什么在做什么。并练习分别使用它们。

def find_it(seq)
  seq.group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}.first[0]
end

以上代码将获取数组中的一个序列。这里我们按元素分组:

例如:

[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}
# => {1=>[1, 1], 2=>[2, 2], -2=>[-2, -2], 5=>[5, 5], 4=>[4, 4], -1=>[-1]}

得到以上结果后,我们发现 whose 元素计数不符合 select 条件。

例如:

[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}

我们将得到结果 {-1=>[-1]}

我们将密钥作为结果元素。

@aimen_alt你的错误是对的

但让我们分解你的问题。

首先,您需要计算每个数字的出现次数。 其次,您需要找到出现次数为奇数的那个。 根据问题,只有一个这样的数字,所以你可以return马上就可以了。

您可以按自己的方式进行 O(N^2) 复杂度,方法是扫描序列中的每个项目(因此 N 序列中的项目乘以序列的大小 N = N*N)。您可以通过构造一个 Hash 来线性*,然后您将能够获得具有奇数值的密钥:

def find_it(seq)
  numbers = {}
  seq.each do |item|
    numbers[item] = numbers[item].to_i + 1
  end
  numbers.select{ |k,v| v.odd? }.first.first
end

为了更加地道,您可以使用 group_by 对数字本身进行分组:

seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }
#=> {1=>[1, 1], 2=>[2, 2], 6=>[6]}

可以看到每一个值都是一个数组,只需要取一个奇数项即可:

seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }
#=> {6=>[6]}

你最不想做的就是获取键的值:

seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }.keys.first

所以,最终的解决方案是

def find_it(seq)
  seq.group_by{ |item| item }
     .select{ |k, v| v.size.odd? }
     .keys
     .first
end

如@pascalbetz 所述:

def find_it(seq)
  seq.group_by{ |item| item }
     .find{ |k, v| v.size.odd? }
     .first
end

谢谢大家的详细回答,我现在把大家的回答都看一遍。我是 Ruby 的新手,我仍在学习使用 them/Big O 表示法的 methods/rules,所以我非常感谢大家的意见。 Codewar 列出了一些排名靠前的解决方案。这似乎是迄今为止最快的:

def find_it(seq)   
   seq.detect { |n| seq.count(n).odd? } 
end

这个怎么样

def find_it(seq)
  seq.reduce(:^)
end

^ -> 这个符号是按位 XOR。 reduce 函数正在获取每个值并执行内部分配的任何工作。在本例中,它获取每个元素并执行异或运算。第一个元素与零进行异或,下一个元素与前一个结果进行异或,依此类推。

这样,我们就找到了奇数元素

异或运算的工作原理

0 ^ 2 = 2
4 ^ 4 = 0

如果你想了解更多异或知识。请参考 this.