ruby 数组中的整数要么全奇数,要么全偶数,只有一个整数例外

nc1teljy  于 2023-11-18  发布在  Ruby
关注(0)|答案(4)|浏览(158)

给定一个包含整数的数组(长度至少为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)

字符串
我需要对这个代码做什么修改?

llycmphe

llycmphe1#

这里有一个简单的方法

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]获取第一个数组的第一个数字

uidvcgyl

uidvcgyl2#

这里有一个很难理解(很难看)但相对简单的解决方案,它需要O(arr.size)时间和O(1)额外的存储空间,而且一旦找到异常值,它就会“短路”。
基本思路是这样的:偶数的最低有效位为0,奇数的最低有效位为1,因此,如果对相邻的一对数字进行异或运算,则只有在它们缺少奇偶校验的情况下,最低有效位才会为1。在第一对数字发生异或运算后,第一次出现异或运算时,您已经找到了离群值。如果第一对数字发生了异或运算,则需要检查第二对数字。如果第二对数字的异或运算结果为0,第一个值是离群值,否则是第二个值。

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


......萨加尔·潘迪亚的一句话!
基于find的方法明显优于each_cons。使用Ruby的odd/even方法与二进制操作相比,似乎只有很小的影响。有趣的是,使用.each_cons(3)而不是.each_cons(2)的相对影响也很小,尽管两者都明显被萨加尔和Sergio的方法所主导。

v2g6jxz6

v2g6jxz63#

这里是一个线性时间常数记忆算法

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

字符串

dy1byipe

dy1byipe4#

与其给予你一个解决方案,不如让我看看我是否能帮助你理解什么似乎是绊倒你的。我将忽略许多“Ruby主义”,它们可以缩短很多东西,因为它们很好,但最终似乎你仍然需要理解底层方法,而不是捷径,因为这是从长远来看帮助你更好地编程的东西。

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

字符串
上面的代码是在数组中寻找两个相等的数字。这意味着count永远不会增加,除非你的数组包含两个相同的值,这不是你想要的任务描述。此外,这个问题真的不需要你比较数组中的两个数字。你只需要确定每个数字是奇数还是偶数,并找到离群值。
最简单的(也可能是最常见的)确定一个数是否为奇数的编程方法是使用模运算符(%)。你在检查你的count变量时使用了这个,这也不是你真正需要的。相反,你应该对数组中的每个条目使用它。所以对于某个整数值n,如果n % 2是偶数,它将为0,如果它是奇数,它将为1。看起来你有点理解这一点,但是对数组中的每个数字使用它来确定它是偶数还是奇数,而不是count变量,然后你可以对每个数字的信息进行操作。
一旦你确定了数组中的每个数字是偶数还是奇数,你就需要一种方法来跟踪你是在搜索奇数还是偶数。最简单的方法是在一个变量中跟踪偶数/奇数计数,但是有一个变量用于偶数计数,另一个变量用于奇数计数。所以当你遇到偶数时,你可以在偶数计数上加1,奇数也是如此,但只限于奇数计数。这样你就知道你要找的类型了(偶数或奇数)是在你完成遍历数组后,计数等于1的值。这意味着这些变量应该在遍历数组的循环之外,因为你不希望它们为数组中的每个数字重置,你可能也会在循环之后看它们。
一旦你确定了你是在寻找奇数还是偶数,你可以第二次遍历数组(不是嵌套循环,而是在第一次循环之后的第二次循环),并根据需要从数组中返回奇数或偶数。有很多方法可以不需要第二次循环,但我试图让它直接前进。
希望这能帮助你想出自己的解决方案,这样你就可以从解决问题中学习。如果你让它与我的基本布局一起工作,有几种方法可以让它在性能或代码量方面更好(比如不使用第二个循环)。

相关问题