为您提供了一个包含整数的数组(长度至少为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)
此代码需要什么更改?
答案 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
变量时使用了该变量,而这实际上并不是您所需要的。相反,您应该针对数组中的每个条目使用它。因此,对于某个整数值n
,n % 2
如果是偶数则为0,如果是奇数则为1。似乎您有点理解了这一点,但是对数组中的每个数字使用它来确定它是偶数还是奇数,而不是count
变量,然后您可以对每个数字使用该信息。>
一旦有了它,就可以确定数组中的每个数字是偶数还是奇数,您需要一种方法来跟踪您要搜索的是奇数还是偶数。最简单的方法是跟踪变量中的偶数/奇数计数,但是对于偶数计数有一个变量,对于奇数计数有一个单独的变量。因此,当您遇到偶数时,可以将偶数加1,对于奇数也可以加奇数。这样,您知道要查找的类型(偶数或奇数)是在完成遍历数组后等于1的任何一个。这意味着这些变量应该在遍历数组的循环之外,因为您不希望为数组中的每个数字重置它们,并且您可能还希望在循环之后查看它们。
一旦确定要查找的是奇数还是偶数,就可以第二次遍历数组(不是嵌套循环,而是第一个循环之后的第二个),然后从中返回奇数或偶数适当的数组。有一些方法可以不进行第二次循环,但是我正在努力使其保持直接前进。
希望这可以帮助您提出自己的解决方案,以便您可以从解决问题中学习。如果您将其与我的基本布局配合使用,则可以通过多种方法使它在性能或仅代码量方面更好(例如,不使用第二个循环)。很高兴澄清您是否需要。
编码愉快!