Skip to content

Commit

Permalink
alg/heuristic: 2-SUM-like loop in Approximation (#103)
Browse files Browse the repository at this point in the history
The Approximation.Suggest() function is now the largest consumer in CPU profiles. Like #99 it is currently "accidentally cubic" but can be optimized to use a linear outer loop, similar to the 2-SUM problem.

Updates #60
Updates #25
  • Loading branch information
mmcloughlin committed May 13, 2021
1 parent 5e94a59 commit 653b8b2
Showing 1 changed file with 36 additions and 22 deletions.
58 changes: 36 additions & 22 deletions alg/heuristic/heuristic.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import (

// Heuristic suggests insertions given a current protosequence.
type Heuristic interface {
// Suggest insertions given a target and protosequence f. Protosequence must
// contain sorted distinct integers.
Suggest(f []*big.Int, target *big.Int) []*big.Int

// String returns a name for the heuristic.
String() string
}

Expand Down Expand Up @@ -124,29 +128,39 @@ func (Approximation) Suggest(f []*big.Int, target *big.Int) []*big.Int {
delta := new(big.Int)
var mindelta *big.Int
var best *big.Int
for i, a := range f {
for _, b := range f[i:] {
// Compute the delta f-(a+b).
delta.Add(a, b)
delta.Sub(target, delta)
if delta.Sign() < 0 {
continue
}

// Proposed insertion is a+delta.
insert := new(big.Int).Add(a, delta)

// If it's actually in the sequence already, use it.
if bigints.Contains(insert, f) {
return []*big.Int{insert}
}

// Keep it if its the closest we've seen.
if best == nil || delta.Cmp(mindelta) < 0 {
mindelta = bigint.Clone(delta)
best = insert
}

// Leverage the fact that f contains sorted distinct integers to apply a
// linear algorithm, similar to the 2-SUM problem. Maintain left and right
// pointers and adjust them based on whether the sum is above or below the
// target.
for l, r := 0, len(f)-1; l <= r; {
a, b := f[l], f[r]

// Compute the delta f-(a+b).
delta.Add(a, b)
delta.Sub(target, delta)
if delta.Sign() < 0 {
// Sum exceeds target, decrement r for smaller b value.
r--
continue
}

// Proposed insertion is a+delta.
insert := new(big.Int).Add(a, delta)

// If it's actually in the sequence already, use it.
if bigints.Contains(insert, f) {
return []*big.Int{insert}
}

// Keep it if its the closest we've seen.
if best == nil || delta.Cmp(mindelta) < 0 {
mindelta = bigint.Clone(delta)
best = insert
}

// Advance to next a value.
l++
}

return []*big.Int{best}
Expand Down

0 comments on commit 653b8b2

Please sign in to comment.