我有一个程序,用于在ruby中找到通过除一次测试之外的所有峰值(局部最大值)。就我个人而言,我认为我的程序还可以,但是我可能没有考虑到问题中未指定的假定邻域大小,但是其他人也曾尝试过它。
这是我到目前为止所拥有的。
def pick_peaks(arr)
pos = []
peaks =[]
peak_set = {pos: [], peaks: []}
for i in 1..arr.length-2
if arr[i-1] < arr[i] && arr[i] >= arr[i+1]
unless edge_plateau?(arr, i)
peak_set[:pos] << i
peak_set[:peaks] << arr[i]
end
end
end
peak_set_alt = peak_set.collect{|k,v| [k.to_s, v]}.to_h
peak_set_alt
end
def edge_plateau?(array, position)
edge_plateau_left = true
edge_plateau_right = true
i = 1
until i == position
edge_plateau_left = false if array[0] != array[i]
i += 1
end
i = array.length-2
until i == position
edge_plateau_right = false if array[i] != array.last
i -= 1
end
edge_plateau_left or edge_plateau_right
end
这是它需要通过的测试,但我不知道原始数组,所以这是一个挑战。
Expected: {"pos"=>[2, 7, 14, 20], "peaks"=>[5, 6, 5, 5]}, instead got: {"pos"=>[2, 7, 11, 14, 20], "peaks"=>[5, 6, 3, 5, 5]}
我在中间得到一个额外的峰值,但如果它是局部最大值那应该没问题,对吗?
更新
感谢我的建议,我找到了测试数组
[1, 2, 5, 4, 3, 2, 3, 6, 4, 1, 2, 3, 3, 4, 5, 3, 2, 1, 2, 3, 5, 5, 4, 3]
答案 0 :(得分:1)
这是一种更类似Ruby的方法来查找局部最大值。
<强>代码强>
def locale_maxima(arr)
last_idx = arr.size - 1
peaks, pos =
([[-Float::INFINITY, nil]] +
arr.each_with_index.reject { |v,i| i < last_idx && v == arr[i+1] } +
[[-Float::INFINITY, nil]]
).each_cons(3).
select { |(n1,_), (n2,_), (n3,_)| n1 < n2 && n2 > n3 }.
map { |_,max_pair,_| max_pair }.
transpose
{ pos: pos, peaks: peaks }
end
示例强>
arr = [1, 2, 5, 4, 3, 2, 3, 6, 4, 1, 2, 3, 3, 4, 5, 3, 2, 1, 2, 3, 5, 5, 4, 3]
locale_maxima arr
#=> { :pos =>[2, 7, 14, 21],
# :peaks=>[5, 6, 5, 5] }
<强>解释强>
步骤如下。
last_idx = arr.size - 1
#=> 23
如果有连续的相等值,可能代表拐点(并发症),则删除除最后一个之外的所有值。要报告局部最大值的索引,我们需要在删除重复项之前保存索引。
b = arr.each_with_index.reject { |v,i| i < last_idx && v == arr[i+1] }
#=> [[1, 0], [2, 1], [5, 2], [4, 3], [3, 4], [2, 5], [3, 6], [6, 7],
# [4, 8], [1, 9], [2, 10], [3, 12], [4, 13], [5, 14], [3, 15],
# [2, 16], [1, 17], [2, 18], [3, 19], [5, 21], [4, 22], [3, 23]]
请注意[3,11]
和[5, 20]
已被删除。
在开头和结尾处对不能成为局部最大值的对(nil
是任意的)。
c = [[-Float::INFINITY, nil]] + b + [[-Float::INFINITY, nil]]
#=> [[-Infinity, nil], [1, 0], [2, 1], [5, 2], [4, 3], [3, 4], [2, 5], [3, 6],
# [6, 7], [4, 8], [1, 9], [2, 10], [3, 12], [4, 13], [5, 14], [3, 15], [2, 16],
# [1, 17], [2, 18], [3, 19], [5, 21], [4, 22], [3, 23], [-Infinity, nil]]
使用Enumerable#each_cons生成一个枚举器,用于识别局部最大值。
d = c.each_cons(3)
#=> #<Enumerator:
# [[-Infinity, nil], [1, 0], [2, 1], [5, 2], [4, 3], [3, 4], [2, 5],
# [3, 6], [6, 7], [4, 8], [1, 9], [2, 10], [3, 12], [4, 13], [5, 14],
# [3, 15], [2, 16], [1, 17], [2, 18], [3, 19], [5, 21], [4, 22],
# [3, 23], [-Infinity, nil]]:each_cons(3)>
e = d.select { |(n1,_), (n2,_), (n3,_)| n1 < n2 && n2 > n3 }
#=> [[[2, 1], [5, 2], [4, 3]],
# [[3, 6], [6, 7], [4, 8]],
# [[4, 13], [5, 14], [3, 15]],
# [[3, 19], [5, 21], [4, 22]]]
f = e.map { |_,max_pair,_| max_pair }
#=> [[5, 2], [6, 7], [5, 14], [5, 21]]
peaks, pos = f.transpose
#=> [[5, 6, 5, 5], [2, 7, 14, 21]]
{ pos: pos, peaks: peaks }
#=> {:pos=>[2, 7, 14, 21], :peaks=>[5, 6, 5, 5]}
答案 1 :(得分:0)
您的代码中有多个错误。
尝试使用以下数据的代码
pick_peaks([0,1,10,1,2,2,3,1,10,1,0])
你会得到
{&#34; pos&#34; =&gt; [2,4,6,8],&#34;峰值&#34; =&gt; [10,2,3,10]}
显然2
这是一个错误。所以这里的错误来源是arr[i] >= arr[i+1]
除非在任务中明确说明了shomehow,否则你似乎处理错误的边缘。考虑
pick_peaks([0,0,10,1,2,2,3,1,10,0,0])
你会得到
{&#34; pos&#34; =&gt; [4,6],&#34;峰值&#34; =&gt; [2,3]}
在左侧和右侧缺少10
。
没有确切的任务,很难肯定地说,但乍一看似乎你的算法在代码和Big-O
方面都要复杂化。你为什么不穿过阵列,跟踪你是爬山,平原还是跌倒?
<强>更新强>
这是一段代码,用于说明我的上一个建议。我不擅长Ruby,所以我的示例代码将使用JavaScript,因此您可以在浏览器控制台中运行它:
function pick_peaks(arr) {
var prevUp = true; //let the start be a peak
var peaks = [];
var lastUpInd = 0;
for(var i = 0; i < arr.length-1; i++) {
if (arr[i] < arr[i+1]) {
prevUp = true;
lastUpInd = i + 1;
}
else if (arr[i] > arr[i+1]) {
if(prevUp) {
for(var j = lastUpInd; j <= i; j++) {
peaks.push([j, arr[j]]);
}
}
prevUp = false;
}
}
// additionally handle the end to let it be a peak
if(prevUp)
{
for(var j = lastUpInd; j <= i; j++) {
peaks.push([j, arr[j]]);
}
}
return peaks;
}
如果您不希望结束可能是峰值,只需使用prevUp
初始化false
,并在评论后删除内部if
的最后for