数组中的整数除单个整数外,要么全部为奇数,要么全部为偶数

时间:2019-03-07 20:03:23

标签: ruby

为您提供了一个包含整数的数组(长度至少为3,但可能非常大)。除了一个整数N之外,该数组要么完全由奇数整数组成,要么完全由偶数整数组成。编写一个将数组作为参数并返回此异常值N的方法。

到目前为止,这是我的代码,似乎无效:

arr = [160, 3, 1719, 19, 11, 13, -21]
n = arr.length

def getOddOccurrence(arr, arr_size)
  for i in range(0, arr_size)
    count = 0
    for j in range(0, arr_size)
      if arr[i] == arr[j]
        count += 1
      end
      if(count % 2 != 0)
        return arr[i]
      end
    end
  end
  return -1
end

print getOddOccurrence(arr, n)

此代码需要什么更改?

4 个答案:

答案 0 :(得分:2)

这是一个奥秘(难看)的解决方案,但相对简单。它需要O({arr.size)时间,并使用O(1)额外的存储空间。一旦发现异常值,它也会“短路”。

这是基本概念。偶数的最低有效位为零,而奇数为1,因此,如果对相邻的一对数字进行异或运算,则仅当它们缺乏奇偶校验时,最低有效位才为1。在第一对之后的第一次,您已经发现异常值。如果第一对发生这种情况,则需要检查第二对。如果结果为零,则第一个值为离群值,否则为第二个。

def getOddOccurrence(arr)
  arr.each_index do |i|
    return arr[i == 1 && (arr[i] ^ arr[i + 1]) & 1 == 0 ? 0 : i] if i > 0 && (arr[i] ^ arr[i - 1]) & 1 == 1
  end
end

这是相同的概念,但略带些Ruby风格:

def getOddOccurrence(arr)
  arr.each_cons(3) { |x,y,z| return ((y ^ z) & 1 == 1 ? y : x) if (x ^ y) & 1 == 1 }
  arr[-1]
end

如果您希望查看2的子集,请对前3个值进行一次性检查,然后使用cons(2)子集。您还可以用检查均匀性(或奇数)的一致性来代替位测试,以提高可读性:

def getOddOccurrence(arr)
  return arr[0] if (arr[0].odd? ^ arr[1].odd?) && !(arr[1].odd? ^ arr[2].odd?)
  arr.each_cons(2) { |x,y| return y if (x.odd? ^ y.odd?)}
end

我终于有几分钟的空闲时间来制定一个基准:

require 'benchmark/ips'

def getOddOccurrence_cons3(arr)
  arr.each_cons(3) { |x,y,z| return ((y ^ z) & 1 == 1 ? y : x) if (x ^ y) & 1 == 1 }
  arr[-1]
end

def getOddOccurrence_cons2(arr)
  return arr[0] if (arr[0].odd? ^ arr[1].odd?) && !(arr[1].odd? ^ arr[2].odd?)
  arr.each_cons(2) { |x,y| return y if (x.odd? ^ y.odd?) }
end

def getOddOccurrence_cons2_bits(arr)
  return arr[0] if ((arr[0] ^ arr[1]) & 1 == 1) && ((arr[1] ^ arr[2]) & 1 == 0)
  arr.each_cons(2) { |x,y| return y if (x ^ y) & 1 == 1 }
end

def getOddOccurrence_find(arr)
  arr.first(3).count(&:odd?) > 1 ? arr.find(&:even?) : arr.find(&:odd?)
end

def getOddOccurrence_find_bits(arr)
  arr.first(3).sum {|x| x & 1} > 1 ? arr.find { |x| (x & 1) == 0 } : arr.find { |x| (x & 1) == 1 }
end

def find_outlier(ary)
  # fetch first 3 numbers and determine what kind of array
  # are we dealing with here, mostly odd or mostly even?
  mostly_odd = ary.take(3).count(&:odd?) > 1

  # then just go and find the outlier element
  if mostly_odd
    ary.find(&:even?)
  else
    ary.find(&:odd?)
  end
end

arr = Array.new(10_000) { |i| i * 2 }.shuffle << 5

Benchmark.ips do |b|
  b.report('cons3 bits:') { getOddOccurrence_cons3(arr) }
  b.report('cons2 bits:') { getOddOccurrence_cons2_bits(arr) }
  b.report('cons2 even/odd:') { getOddOccurrence_cons2(arr) }
  b.report('find even/odd:') { getOddOccurrence_find(arr) }
  b.report('find bits:') { getOddOccurrence_find_bits(arr) }
  b.report('find sergio:') { find_outlier(arr) }
  b.compare!
end

如您所见,我在偶数数组的末尾添加了一个奇数值,以最大化所需的搜索。

赢家是...

Warming up --------------------------------------
         cons3 bits:   128.000  i/100ms
         cons2 bits:   127.000  i/100ms
     cons2 even/odd:   103.000  i/100ms
      find even/odd:   216.000  i/100ms
          find bits:   217.000  i/100ms
        find sergio:   231.000  i/100ms
Calculating -------------------------------------
         cons3 bits:      1.251k (± 4.9%) i/s -      6.272k in   5.026355s
         cons2 bits:      1.294k (± 3.4%) i/s -      6.477k in   5.010802s
     cons2 even/odd:      1.038k (± 4.4%) i/s -      5.253k in   5.070617s
      find even/odd:      2.284k (± 4.2%) i/s -     11.448k in   5.022831s
          find bits:      2.165k (± 5.3%) i/s -     10.850k in   5.027801s
        find sergio:      2.277k (± 3.3%) i/s -     11.550k in   5.078381s

Comparison:
      find even/odd::     2283.6 i/s
        find sergio::     2276.9 i/s - same-ish: difference falls within error
          find bits::     2164.6 i/s - same-ish: difference falls within error
         cons2 bits::     1294.2 i/s - 1.76x  slower
         cons3 bits::     1251.1 i/s - 1.83x  slower
     cons2 even/odd::     1038.1 i/s - 2.20x  slower

...萨加尔·潘迪亚(Sagar Pandyar)评论的一线话!

基于find的方法明显胜过each_cons。与二进制操作相比,使用Ruby的odd / even方法似乎只产生了很小的影响。有趣的是,使用.each_cons(3)而非.each_cons(2)的相对影响也很小,尽管两者显然都由Sagar&Sergio的方法主导。

答案 1 :(得分:1)

这是一种简单的方法

arr = [160, 3, 1719, 19, 11, 13, -21]
arr.group_by(&:odd?).values.sort_by(&:count)[0][0]
# => 160

group_by(&:odd?)将对奇数和偶数进行2次哈希处理

values将获取哈希值。 2个数组,偶数和奇数

sort_by(&:count)对数组进行排序,第一个值较小的数组将排在第一位

[0][0]抓取第一个数组的第一个数字

答案 2 :(得分:1)

这是线性时间恒定内存算法

def find_outlier(ary)
  # fetch first 3 numbers and determine what kind of array
  # are we dealing with here, mostly odd or mostly even?
  mostly_odd = ary.take(3).count(&:odd?) > 1

  # then just go and find the outlier element
  if mostly_odd
    ary.find(&:even?)
  else
    ary.find(&:odd?)
  end
end

ary = [161, 3, 1719, 19, 11, 160, 13, -21]

find_outlier(ary) # => 160

答案 3 :(得分:1)

欢迎堆栈溢出!

由于您是新手,所以让我首先说,在这里寻求解决方案通常不太受欢迎。这里不是让其他人为您工作的地方,因此您应该查看https://stackoverflow.com/help/how-to-ask,以了解对未来有什么好问题的问题。

那不是让您找到解决方案,而是让我看看是否可以帮助您了解似乎使您绊倒的事情。我将忽略很多可以大大简化事情的“红宝石主义”,因为它们很好,但最终看来您可能仍需要了解基本方法而不是捷径,因为这可以帮助您从长远来看,程序会更好。

if arr[i] == arr[j]
  count +=1
end

上面的代码正在数组中寻找两个相等的数字。这意味着count永远不会递增,除非您的数组包含两个具有相同值的值,而这不是您从任务描述中想要的值。此外,此问题确实不需要您比较数组中的两个数字。您只需要确定每个数字是奇数还是偶数,然后找到异常值即可。

确定数字是否为奇数的最简单(也是最常见)编程方法是使用模运算符(%)。您在检查count变量时使用了该变量,而这实际上并不是您所需要的。相反,您应该针对数组中的每个条目使用它。因此,对于某个整数值nn % 2如果是偶数则为0,如果是奇数则为1。似乎您有点理解了这一点,但是对数组中的每个数字使用它来确定它是偶数还是奇数,而不是count变量,然后您可以对每个数字使用该信息。

一旦有了它,就可以确定数组中的每个数字是偶数还是奇数,您需要一种方法来跟踪您要搜索的是奇数还是偶数。最简单的方法是跟踪变量中的偶数/奇数计数,但是对于偶数计数有一个变量,对于奇数计数有一个单独的变量。因此,当您遇到偶数时,可以将偶数加1,对于奇数也可以加奇数。这样,您知道要查找的类型(偶数或奇数)是在完成遍历数组后等于1的任何一个。这意味着这些变量应该在遍历数组的循环之外,因为您不希望为数组中的每个数字重置它们,并且您可能还希望在循环之后查看它们。

一旦确定要查找的是奇数还是偶数,就可以第二次遍历数组(不是嵌套循环,而是第一个循环之后的第二个),然后从中返回奇数或偶数适当的数组。有一些方法可以不进行第二次循环,但是我正在努力使其保持直接前进。

希望这可以帮助您提出自己的解决方案,以便您可以从解决问题中学习。如果您将其与我的基本布局配合使用,则可以通过多种方法使它在性能或仅代码量方面更好(例如,不使用第二个循环)。很高兴澄清您是否需要。

编码愉快!