使用sort函数来获取切片区间内的最大值和最小值。

huangapple go评论76阅读模式
英文:

Using sort to get max and min values from a interval of a slice

问题

我在playground中有这段代码。我使用排序来从一个切片/数组中获取最大值和最小值。当我需要在x个周期内获取这些值时,问题就出现了。
如果你运行这段代码,你会发现在最后的打印输出中有一个值为1.0407,但在之前的x个周期中没有这个值了,这个值与周期x相差很远。
所以我的问题是,为什么它会选择这个值?而最小值却没有选择。

package main

import (
	"fmt"
	"math"
	"sort"
)

func main() {
	var x = []float64{1.0073, 1.0129, 1.014, 1.017, 1.0173, 1.0171, 1.0171, 1.0174, 1.0154, 1.0142, 1.0116, 1.0116, 1.0118, 1.0084, 1.0087, 1.0086, 1.0077, 1.0154, 1.0155, 1.0207, 1.0218, 1.0273, 1.0257, 1.0244, 1.0243, 1.0237, 1.026, 1.0251, 1.0239, 1.025, 1.0246, 1.0245, 1.0234, 1.024, 1.0247, 1.0229, 1.0274, 1.0326, 1.0319, 1.0314, 1.0331, 1.0301, 1.0311, 1.0307, 1.028, 1.0298, 1.0305, 1.0296, 1.0293, 1.0296, 1.0291, 1.0288, 1.029, 1.029, 1.0313, 1.0315, 1.0318, 1.0366, 1.0387, 1.0363, 1.0366, 1.0353, 1.036, 1.0364, 1.0369, 1.0341, 1.0326, 1.0345, 1.033, 1.0316, 1.0324, 1.032, 1.0325, 1.032, 1.0325, 1.0317, 1.0317, 1.0324, 1.0335, 1.036, 1.0368, 1.034, 1.0347, 1.035, 1.0354, 1.039, 1.0407, 1.0374, 1.0362, 1.0349, 1.0326, 1.0285, 1.0298, 1.0301, 1.0318, 1.0326, 1.0329, 1.032, 1.0303, 1.0305, 1.0305, 1.0311, 1.0303, 1.0303, 1.0295, 1.0304, 1.031, 1.0305, 1.0305, 1.0288, 1.0287, 1.029, 1.0289, 1.0293, 1.0299, 1.0297, 1.0286, 1.028, 1.0292, 1.0292, 1.0286, 1.0295, 1.0282, 1.0291, 1.029, 1.0292, 1.0287, 1.0294, 1.0274, 1.0267, 1.0266, 1.0249, 1.0245, 1.0226, 1.0221, 1.0237, 1.0243, 1.0264, 1.025, 1.0263, 1.0268, 1.0256, 1.0263, 1.0257, 1.0253, 1.0246, 1.0257, 1.0263, 1.0258, 1.026, 1.0264, 1.0266, 1.0295, 1.0283, 1.0309, 1.0296, 1.0299, 1.0294, 1.0287, 1.0277, 1.0285, 1.03, 1.0323, 1.032, 1.0315, 1.0325, 1.0336, 1.0336, 1.0337, 1.0337, 1.0326, 1.0328, 1.0329, 1.0335, 1.0327, 1.0347, 1.0331, 1.0359, 1.0357, 1.0332, 1.0328, 1.0308, 1.0307, 1.0304, 1.0303, 1.0271, 1.0284, 1.0293, 1.0308, 1.0312, 1.0309, 1.0311, 1.0309, 1.0305, 1.0314, 1.0306, 1.0302, 1.0299, 1.0294, 1.0286, 1.0292, 1.0288, 1.0295, 1.0301, 1.0304, 1.0286, 1.0275, 1.0265, 1.0262, 1.0271, 1.027, 1.0263, 1.026, 1.0258, 1.0257, 1.0257, 1.0261, 1.0264, 1.0242, 1.0256, 1.0265, 1.0263, 1.0258, 1.0264, 1.0253, 1.0243, 1.0239, 1.0243, 1.0258, 1.0234, 1.0227, 1.0181, 1.0189, 1.0117, 1.0128, 1.0143, 1.0154, 1.0154, 1.0144, 1.0122, 1.0129, 1.0123, 1.0128, 1.0133, 1.0136, 1.0134, 1.0134, 1.0142, 1.0132, 1.0127, 1.0113, 1.0122, 1.0119, 1.0105, 1.0108, 1.0079, 1.0082, 1.0082, 1.0091, 1.0105, 1.0121, 1.0122, 1.0115, 1.0123, 1.0118, 1.0123, 1.0111, 1.0106, 1.0099, 1.01, 1.0107, 1.0111, 1.0103, 1.0128, 1.0115, 1.0109, 1.0081, 1.0083, 1.0096, 1.0089, 1.0097, 1.0106, 1.0121, 1.0124, 1.0136, 1.0118, 1.0134, 1.0137, 1.0152, 1.0147, 1.0141, 1.0144, 1.0139, 1.0145, 1.0146, 1.0156, 1.0145, 1.014, 1.014, 1.0124}
	var period = 5
	var highest float64
	var lowest float64
	for i, v := range x {
		if i < period {
			continue
		}
		var correctHighest, correctLowest = MaxMin(x[i-period : i])
		fmt.Println("Correct -", "Price:", v, "Highest:", correctHighest, "Lowest:", correctLowest)
	}
	for i, v := range x {
		if i < period {
			continue
		}
		var correctHighest, correctLowest = MaxMin(x[i-period : i])
		fmt.Println("Correct -", "Highest:", correctHighest, "Lowest:", correctLowest)
		highest = Max(x[i-period : i]...)
		lowest = Min(x[i-period : i]...)
		fmt.Println("Price:", v, "Highest:", highest, "Lowest:", lowest)
	}
}

// Both does not work
func Max(n ...float64) float64 {
	sort.Float64s(n)
	if len(n) == 0 {
		return 0.
	}
	return n[len(n)-1]
}
func Min(n ...float64) float64 {
	sort.Float64s(n)
	if len(n) == 0 {
		return 0.
	}
	return n[0]
}

// New code that works
func MaxMin(n []float64) (float64, float64) {
	if len(n) == 0 {
		return 0., 0.
	}
	var max float64
	var min = math.MaxFloat64
	for _, value := range n {
		if value > max {
			max = value
		}
		if value < min {
			min = value
		}
	}
	return max, min
}
英文:

i have this code in playground. I was using a sort to get max and min value from a slice/array. The problem started when I needed to get the values in a interval of x periods.
If you run the code you will in the lasts print there is a value of 1.0407, but the x periods before there isn't this value anymore, this value is way far from the period of x.
So my question is, why did he peeked this value? While min didn't.

package main

import (
	&quot;fmt&quot;
	&quot;math&quot;
	&quot;sort&quot;
)

func main() {
	var x = []float64{1.0073, 1.0129, 1.014, 1.017, 1.0173, 1.0171, 1.0171, 1.0174, 1.0154, 1.0142, 1.0116, 1.0116, 1.0118, 1.0084, 1.0087, 1.0086, 1.0077, 1.0154, 1.0155, 1.0207, 1.0218, 1.0273, 1.0257, 1.0244, 1.0243, 1.0237, 1.026, 1.0251, 1.0239, 1.025, 1.0246, 1.0245, 1.0234, 1.024, 1.0247, 1.0229, 1.0274, 1.0326, 1.0319, 1.0314, 1.0331, 1.0301, 1.0311, 1.0307, 1.028, 1.0298, 1.0305, 1.0296, 1.0293, 1.0296, 1.0291, 1.0288, 1.029, 1.029, 1.0313, 1.0315, 1.0318, 1.0366, 1.0387, 1.0363, 1.0366, 1.0353, 1.036, 1.0364, 1.0369, 1.0341, 1.0326, 1.0345, 1.033, 1.0316, 1.0324, 1.032, 1.0325, 1.032, 1.0325, 1.0317, 1.0317, 1.0324, 1.0335, 1.036, 1.0368, 1.034, 1.0347, 1.035, 1.0354, 1.039, 1.0407, 1.0374, 1.0362, 1.0349, 1.0326, 1.0285, 1.0298, 1.0301, 1.0318, 1.0326, 1.0329, 1.032, 1.0303, 1.0305, 1.0305, 1.0311, 1.0303, 1.0303, 1.0295, 1.0304, 1.031, 1.0305, 1.0305, 1.0288, 1.0287, 1.029, 1.0289, 1.0293, 1.0299, 1.0297, 1.0286, 1.028, 1.0292, 1.0292, 1.0286, 1.0295, 1.0282, 1.0291, 1.029, 1.0292, 1.0287, 1.0294, 1.0274, 1.0267, 1.0266, 1.0249, 1.0245, 1.0226, 1.0221, 1.0237, 1.0243, 1.0264, 1.025, 1.0263, 1.0268, 1.0256, 1.0263, 1.0257, 1.0253, 1.0246, 1.0257, 1.0263, 1.0258, 1.026, 1.0264, 1.0266, 1.0295, 1.0283, 1.0309, 1.0296, 1.0299, 1.0294, 1.0287, 1.0277, 1.0285, 1.03, 1.0323, 1.032, 1.0315, 1.0325, 1.0336, 1.0336, 1.0337, 1.0337, 1.0326, 1.0328, 1.0329, 1.0335, 1.0327, 1.0347, 1.0331, 1.0359, 1.0357, 1.0332, 1.0328, 1.0308, 1.0307, 1.0304, 1.0303, 1.0271, 1.0284, 1.0293, 1.0308, 1.0312, 1.0309, 1.0311, 1.0309, 1.0305, 1.0314, 1.0306, 1.0302, 1.0299, 1.0294, 1.0286, 1.0292, 1.0288, 1.0295, 1.0301, 1.0304, 1.0286, 1.0275, 1.0265, 1.0262, 1.0271, 1.027, 1.0263, 1.026, 1.0258, 1.0257, 1.0257, 1.0261, 1.0264, 1.0242, 1.0256, 1.0265, 1.0263, 1.0258, 1.0264, 1.0253, 1.0243, 1.0239, 1.0243, 1.0258, 1.0234, 1.0227, 1.0181, 1.0189, 1.0117, 1.0128, 1.0143, 1.0154, 1.0154, 1.0144, 1.0122, 1.0129, 1.0123, 1.0128, 1.0133, 1.0136, 1.0134, 1.0134, 1.0142, 1.0132, 1.0127, 1.0113, 1.0122, 1.0119, 1.0105, 1.0108, 1.0079, 1.0082, 1.0082, 1.0091, 1.0105, 1.0121, 1.0122, 1.0115, 1.0123, 1.0118, 1.0123, 1.0111, 1.0106, 1.0099, 1.01, 1.0107, 1.0111, 1.0103, 1.0128, 1.0115, 1.0109, 1.0081, 1.0083, 1.0096, 1.0089, 1.0097, 1.0106, 1.0121, 1.0124, 1.0136, 1.0118, 1.0134, 1.0137, 1.0152, 1.0147, 1.0141, 1.0144, 1.0139, 1.0145, 1.0146, 1.0156, 1.0145, 1.014, 1.014, 1.0124}
	var period = 5
	var highest float64
	var lowest float64
	for i, v := range x {
		if i &lt; period {
			continue
		}
		var correctHighest, correctLowest = MaxMin(x[i-period : i])
		fmt.Println(&quot;Correct -&quot;, &quot;Price:&quot;, v, &quot;Highest:&quot;, correctHighest, &quot;Lowest:&quot;, correctLowest)
	}
	for i, v := range x {
		if i &lt; period {
			continue
		}
		var correctHighest, correctLowest = MaxMin(x[i-period : i])
		fmt.Println(&quot;Correct -&quot;, &quot;Highest:&quot;, correctHighest, &quot;Lowest:&quot;, correctLowest)
		highest = Max(x[i-period : i]...)
		lowest = Min(x[i-period : i]...)
		fmt.Println(&quot;Price:&quot;, v, &quot;Highest:&quot;, highest, &quot;Lowest:&quot;, lowest)
	}
}

// Both does not work
func Max(n ...float64) float64 {
	sort.Float64s(n)
	if len(n) == 0 {
		return 0.
	}
	return n[len(n)-1]
}
func Min(n ...float64) float64 {
	sort.Float64s(n)
	if len(n) == 0 {
		return 0.
	}
	return n[0]
}

// New code that works
func MaxMin(n []float64) (float64, float64) {
	if len(n) == 0 {
		return 0., 0.
	}
	var max float64
	var min = math.MaxFloat64
	for _, value := range n {
		if value &gt; max {
			max = value
		}
		if value &lt; min {
			min = value
		}
	}
	return max, min
}

答案1

得分: 3

正在发生的是,切片是对底层数组的引用,而sort函数会原地对切片进行排序。这意味着对sort.Float64s的调用实际上修改(排序)了x切片的内容(更准确地说,是x的子切片,但底层数组是相同的)。

正如我在上面的评论中提到的,仅仅使用sort来查找最小值/最大值并不是一个好主意,因为它比简单的循环扫描查找最小值/最大值要慢。但是如果你真的想使用它,你需要在将切片传递给sort.Float64s之前对它们进行深拷贝。


另外关于上面的代码的小提示:你的MinMax函数在所有元素都为负数或者都为NaN的情况下并不正确。你可能想改成像这样:https://godbolt.org/z/E5e5q7ePa(未经测试)。

func MinMax(n []float64) (min float64, max float64) {
    min, max = math.NaN(), math.NaN()
    for _, e := range n {
        if e < min || (math.IsNaN(min) && !math.IsNaN(e)) {
            min = e
        }
        if e > max || (math.IsNaN(max) && !math.IsNaN(e)) {
            max = e
        }
    }
    return
}
英文:

What is happening is that slices are references to a backing array and sort sorts the slice in place. What this means is that the calls to sort.Float64s are actually modifying (sorting) the contents of the x slice (more precisely, of subslices of x, but the backing array is the same).

As I mentioned in my comment above, using sort just for finding the min/max is not a good idea, as it's going to be slower than a simple loop that scans for min/max. But if you really want to use it, you need to make deep copies of those slices before passing them to sort.Float64s.


Also small note about the code above: your MaxMin function is not really correct in case all elements are negative, or in case they are all NaN. You may want to do something like this instead: https://godbolt.org/z/E5e5q7ePa (not tested).

func MinMax(n []float64) (min float64, max float64) {
    min, max = math.NaN(), math.NaN()
    for _, e := range n {
        if e &lt; min || (math.IsNaN(min) &amp;&amp; !math.IsNaN(e)) {
            min = e
        }
        if e &gt; max || (math.IsNaN(max) &amp;&amp; !math.IsNaN(e)) {
            max = e
        }
    }
    return
}

huangapple
  • 本文由 发表于 2021年8月3日 05:17:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/68628014.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定