如果数组包含三次或更多元素,我需要取一个数组并返回true
。如果数组包含少于三次的元素,则需要返回false
。我猜我需要使用count
方法,但我不知道要放在我的块中。我写了以下代码:
def got_three?(array)
array.count {|x|}
end
got_three? ([1, 2, 2, 2, 4, 5, 4, 6, 7])
有人可以帮忙吗?
答案 0 :(得分:7)
您可以使用#count
,但您还需要另一个原语,因为您要检查每个元素。在这种情况下,#any?
就足够了。
def got_three?(array)
array.any? { |y| array.count(y) >= 3 }
end
返回数组中出现的任何元素是否至少出现三次。
答案 1 :(得分:5)
虽然我喜欢西尔维奥的答案,但它可能会多次迭代阵列。
根据数组的大小以及使用此方法的频率,可能会影响性能 - 尤其是在用于服务器应用程序时,例如在Rails中。
以下可能不太可读,但应该表现更好:
def has_three?(array)
h = (Thread.current[:_has_three_buffer] ||= Hash.new(0)).clear
array.each do |i|
h[i] += 1
return true if h[i] >= 3
end
false
end
关于方法优化的快速解释。
<强>速度强>
该方法使用Hash来映射每个元素重复的次数。这样可以减少迭代次数。
该方法不再迭代数组一次,停止true
响应可用的时刻。
此方法将读取阵列中至少3个元素。最多,此方法将遍历数组迭代一次。
内存和对象分配(开销):
创建一个Hash来映射数组是一个昂贵的开销,可以(并且应该)最小化。
开销是由与对象创建和初始化相关的性价格(包括初始内存分配)引起的。
基准测试表明,当三重物体在阵列中较深时(即位于第三个位置之后),开销非常值得。
此方法为每个线程创建一个Hash,无论何时调用该方法都将使用该Hash。这是一种比使用Mutex更好的方法,它可以提供线程安全但防止并行并发(即使用JRuby时)。
使用Thread.current
的内存存储将哈希存储在当前线程中。
Hash:Thread.current[:_has_three_buffer] ||= Hash.new(0)
将保留在线程的内存中,直到线程退出,这样可以避免主要的开销,并替换为访问Hash和&#34;结算&#34;使用clear
的内存。
Hash.new(0)
表示对于缺少的元素,哈希将返回0
而不是nil
。
对不同的解决方案进行基准测试让我感到惊讶。西尔维奥的解决方案并不像我担心的那么缓慢,乔恩的解决方案比我想象的慢......
我也对解决方案使用的内存感到惊讶。我的方法使用了比我想象的更多的内存,但与Wand的方法相比,它仍然是优化的
以下是使用某些样本数组进行100,000次迭代的测量结果。
前两个基准测试是一个10项长的数组,其中三元组(对象出现三次)在开头(由于它的开销导致我的答案处于不利地位)或者结束(其中一些)由于迭代,答案处于不利地位。)
- 数组(10项):[1,1,1,2,3,4,5,6,7,8]
silvio 0.040000 0.000000 0.040000(0.043519)
myst 0.080000 0.000000 0.080000(0.074734)
jon 0.240000 0.010000 0.250000(0.252569)
魔杖0.260000 0.030000 0.290000(0.289101)
- 数组(10项):[8,7,6,5,4,3,2,1,1,1]
silvio 0.310000 0.000000 0.310000(0.313565)
myst 0.300000 0.000000 0.300000(0.298405)
jon 0.530000 0.010000 0.540000(0.534697)
魔杖0.310000 0.030000 0.340000(0.350704)
接下来,我测量了一个10项长阵列,其中三联体位于大约中间位置(从第5项开始)。
我还回顾了100项迭代的10项长阵列评论的近似内存使用情况。内存审查包括用于基准测试和构建报告的内存,它在任何方面都不准确......但它可能显示出一些内容......
- 数组(10项):[1,2,3,4,5,5,5,8,9,10]
使用的内存(通过打印数组):4KB - 显示这不准确
silvio 0.200000 0.000000 0.200000(0.200012)
使用的内存(silvio): 4KB
myst 0.210000 0.010000 0.220000(0.214936)
使用的内存(myst):5,944KB
jon 0.420000 0.000000 0.420000(0.422354)
使用的内存(jon):18,364KB
魔杖0.300000 0.030000 0.330000(0.342656)
使用的内存(魔杖): 118,496KB (~118MB!)
正如您所看到的,在查看短数组(10个项目)时,差异非常小,可能不值得用于优化更长数组方法的内存。
接下来,我做了同样的事情(中间的三连音)和一个50项长的数组...这是迭代的差异真的出现的时候。也就是说,虽然我的答案用了大约1.5秒,但Silvio的方法花费了大约7.5秒 - 大约慢了5倍。
- 数组(50项):[1 ... 47,50,50,50]
silvio 7.560000 0.010000 7.570000(7.570565)
myst 1.480000 0.010000 1.490000(1.487640)
jon 8.290000 0.020000 8.310000(8.316714)
魔杖1.550000 0.150000 1.700000(1.700455)
然而,Jon指出基准测试非常无菌,除了三元组之外没有重复的对象,所以接下来我用100个项目对数组进行基准测试,其中每个项目重复两次(除了三元组)。三胞胎在中间。
这就是Jon的开销(使用unique
)显示它的优势所在。虽然乔恩的方法耗时约8.6秒,但西尔维奥的方法攀升至约14秒。魔杖和我都保持在个位数,我的答案是最快的 - 约1.2秒。
我还回顾了迭代和Benchmarking本身引起的大致内存增长......
有些解决方案使用了大量内存。也就是说,Wand的漂亮解决方案为100个长项目(100K迭代)构建了~0.5GB(!),而Jon的解决方案使用了~50MB,我的解决方案可以达到~5MB和Silvio&# 39; s解决方案似乎根本没有使用内存(这可能是一个小故障,显示内存测试有多么不准确)。
同样,内存使用并不准确。
- 数组(100项):[1..23,1..23,24,25,25,25,26..50,26..50]
使用的内存(通过打印数组):4KB - 显示这不准确
silvio 14.090000 0.010000 14.100000(14.100915)
使用的内存(silvio): 0KB
myst 1.170000 0.010000 1.180000(1.182665)
使用的内存(myst):5,992KB
jon 8.570000 0.020000 8.590000(8.592982)
使用的内存(jon):51720KB
魔杖1.940000 0.160000 2.100000(2.093121)
使用的内存(魔杖): 569,264KB (~569MB!)
基准代码:
require 'benchmark'
def silvio_got_three?(array)
array.any? { |y| array.count(y) >= 3 }
end
def myst_has_three?(array)
h = (Thread.current[:_has_three_buffer] ||= Hash.new(0)).clear
# h = Hash.new(0)
array.each do |i|
h[i] += 1
return true if h[i] >= 3
end
false
end
def jon_has_three?(array)
array.uniq.any? { |x| array.count(x) >= 3 }
end
def wand_has_three?(array)
array.group_by {|i| i }.any? {|_, v| v.size >= 3}
end
def get_total_memory_used
`ps -o rss= -p #{Process.pid}`.to_i
end
# warm-up
myst_has_three? [0]
Benchmark.bm do |bm|
a = [1,1,1] + (2..8).to_a
repeat = 100_000
GC.disable
puts "Array (#{a.length} items): #{a.to_s}"
bm.report('silvio') { repeat.times { silvio_got_three?(a) } }
bm.report('myst') { repeat.times { myst_has_three?(a) } }
bm.report('jon') { repeat.times { jon_has_three?(a) } }
bm.report('wand') { repeat.times { wand_has_three?(a) } }
a.reverse!
puts "Array (#{a.length} items): #{a.to_s}"
bm.report('silvio') { repeat.times { silvio_got_three?(a) } }
bm.report('myst') { repeat.times { myst_has_three?(a) } }
bm.report('jon') { repeat.times { jon_has_three?(a) } }
bm.report('wand') { repeat.times { wand_has_three?(a) } }
a = (1..4).to_a + [5,5,5] + (8..10).to_a
mem = get_total_memory_used
puts "Array (#{a.length} items): #{a.to_s}"
puts "Memory used (by printing the Array): #{get_total_memory_used - mem}KB - showing this isn't accurate"
mem = get_total_memory_used
bm.report('silvio') { repeat.times { silvio_got_three?(a) } }
puts "Memory used (silvio): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('myst') { repeat.times { myst_has_three?(a) } }
puts "Memory used (myst): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('jon') { repeat.times { jon_has_three?(a) } }
puts "Memory used (jon): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('wand') { repeat.times { wand_has_three?(a) } }
puts "Memory used (wand): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
a = (1..47).to_a + [50,50,50]
puts "Array (#{a.length} items): #{a.to_s}"
bm.report('silvio') { repeat.times { silvio_got_three?(a) } }
bm.report('myst') { repeat.times { myst_has_three?(a) } }
bm.report('jon') { repeat.times { jon_has_three?(a) } }
bm.report('wand') { repeat.times { wand_has_three?(a) } }
a = (1..23).to_a*2 + [24,25,25,25] + (26..50).to_a*2
mem = get_total_memory_used
puts "Array (#{a.length} items): [1..23,1..23,24, 25,25,25, 26..50,26..50]"
puts "Memory used (by printing the Array): #{get_total_memory_used - mem}KB - showing this isn't accurate"
mem = get_total_memory_used
bm.report('silvio') { repeat.times { silvio_got_three?(a) } }
puts "Memory used (silvio): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('myst') { repeat.times { myst_has_three?(a) } }
puts "Memory used (myst): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('jon') { repeat.times { jon_has_three?(a) } }
puts "Memory used (jon): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
bm.report('wand') { repeat.times { wand_has_three?(a) } }
puts "Memory used (wand): #{get_total_memory_used - mem}KB"
mem = get_total_memory_used
GC.start
end; nil
答案 2 :(得分:5)
西尔维奥的答案会有效,但正如Myst所指出的那样,它将使用相同的参数多次迭代数组。
如果没有Myst的解决方案的复杂性,这应该会更好一些:
def has_three?(array)
array.uniq.any? { |x| array.count(x) >= 3 }
end
这种微小的差异意味着您只会为每个唯一元素迭代一次数组,而不是对每个重复元素运行额外的检查。
答案 3 :(得分:2)
你也可以这样做:
print [{x:y} for x,z in scrabble_scores for y in z]