mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 03:07:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			90 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"cmp"
 | 
						|
	"slices"
 | 
						|
)
 | 
						|
 | 
						|
const increment = 50 // ops/sec
 | 
						|
 | 
						|
// findOptimalSplit computes and returns a value that separates the given pass
 | 
						|
// and fail measurements optimally, such that the number of mis-classified
 | 
						|
// passes (pass values that fall above the split) and fails (fail values that
 | 
						|
// fall below the split) is minimized.
 | 
						|
//
 | 
						|
// The following gives a visual representation of the problem:
 | 
						|
//
 | 
						|
//		                     Optimal partition (=550) -----> |
 | 
						|
//	                                                         |
 | 
						|
//	  Passes:   o          o        o              o o o oo  |
 | 
						|
//	  Fails:                         x             x         |x    x  x     x x        x
 | 
						|
//	  |---------|---------|---------|---------|---------|----|----|---------|---------|---------|---> x
 | 
						|
//	  0        100       200       300       400       500   |   600       700       800       900
 | 
						|
//
 | 
						|
// The algorithm works by computing the error (i.e. mis-classifications) at
 | 
						|
// various points along the x-axis, starting from the origin and increasing by
 | 
						|
// the given increment.
 | 
						|
func findOptimalSplit(pass, fail []int) int {
 | 
						|
	// Not enough data to compute a sensible score.
 | 
						|
	if len(pass) == 0 || len(fail) == 0 {
 | 
						|
		return -1
 | 
						|
	}
 | 
						|
 | 
						|
	// Maintain counters for the number of incorrectly classified passes and
 | 
						|
	// fails. All passes are initially incorrect, as we start at 0. Conversely,
 | 
						|
	// no fails are incorrectly classified, as all scores are >= 0.
 | 
						|
	pCount, fCount := len(pass), 0
 | 
						|
	p, f := make([]int, len(pass)), make([]int, len(fail))
 | 
						|
	copy(p, pass)
 | 
						|
	copy(f, fail)
 | 
						|
 | 
						|
	// Sort the inputs.
 | 
						|
	slices.Sort(p)
 | 
						|
	slices.Sort(f)
 | 
						|
 | 
						|
	// Find the global min and max.
 | 
						|
	min, max := p[0], f[len(fail)-1]
 | 
						|
 | 
						|
	// Iterate over the range in increments.
 | 
						|
	var result [][]int
 | 
						|
	for x := min; x <= max; x = x + increment {
 | 
						|
		// Reduce the count of incorrect passes as x increases (i.e. fewer pass
 | 
						|
		// values are incorrect as x increases).
 | 
						|
		for len(p) > 0 && p[0] <= x {
 | 
						|
			pCount--
 | 
						|
			p = p[1:]
 | 
						|
		}
 | 
						|
 | 
						|
		// Increase the count of incorrect fails as x increases (i.e. more fail
 | 
						|
		// values are incorrect as x increases).
 | 
						|
		for len(f) > 0 && f[0] < x {
 | 
						|
			fCount++
 | 
						|
			f = f[1:]
 | 
						|
		}
 | 
						|
 | 
						|
		// Add a (x, score) tuple to result slice.
 | 
						|
		result = append(result, []int{x, pCount + fCount})
 | 
						|
	}
 | 
						|
 | 
						|
	// Sort the (x, score) result slice by score ascending. Tie-break by x
 | 
						|
	// ascending.
 | 
						|
	slices.SortFunc(result, func(a, b []int) int {
 | 
						|
		if v := cmp.Compare(a[1], b[1]); v != 0 {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
		return cmp.Compare(a[0], b[0])
 | 
						|
	})
 | 
						|
 | 
						|
	// If there is more than one interval, split the difference between the min
 | 
						|
	// and the max.
 | 
						|
	splitMin, splitMax := result[0][0], result[0][0]
 | 
						|
	for i := 1; i < len(result); i++ {
 | 
						|
		if result[i][1] != result[0][1] {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		splitMax = result[i][0]
 | 
						|
	}
 | 
						|
 | 
						|
	return (splitMin + splitMax) / 2
 | 
						|
}
 |