Task: Price list behind API




Go: package main

import (
    "fmt"
    "log"
    "math"
    "math/rand"
    "time"
)

var minDelta = 1.0

func getMaxPrice(prices []float64) float64 {
    max := prices[0]
    for i := 1; i < len(prices); i++ {
        if prices[i] > max {
            max = prices[i]
        }
    }
    return max
}

func getPRangeCount(prices []float64, min, max float64) int {
    count := 0
    for _, price := range prices {
        if price >= min && price <= max {
            count++
        }
    }
    return count
}

func get5000(prices []float64, min, max float64, n int) (float64, int) {
    count := getPRangeCount(prices, min, max)
    delta := (max - min) / 2
    for count != n && delta >= minDelta/2 {
        if count > n {
            max -= delta
        } else {
            max += delta
        }
        max = math.Floor(max)
        count = getPRangeCount(prices, min, max)
        delta /= 2
    }
    return max, count
}

func getAll5000(prices []float64, min, max float64, n int) [][3]float64 {
    pmax, pcount := get5000(prices, min, max, n)
    res := [][3]float64{{min, pmax, float64(pcount)}}
    for pmax < max {
        pmin := pmax + 1
        pmax, pcount = get5000(prices, pmin, max, n)
        if pcount == 0 {
            log.Fatal("Price list from", pmin, "has too many with same price.")
        }
        res = append(res, [3]float64{pmin, pmax, float64(pcount)})
    }
    return res
}

func main() {
    rand.Seed(time.Now().UnixNano())
    numPrices := 99000 + rand.Intn(2001)
    maxPrice := 1e5
    prices := make([]float64, numPrices) 
    for i := 0; i < numPrices; i++ {
        prices[i] = float64(rand.Intn(int(maxPrice) + 1))
    }
    actualMax := getMaxPrice(prices)
    fmt.Println("Using", numPrices, "items with prices from 0 to", actualMax, "\b:")
    res := getAll5000(prices, 0, actualMax, 5000)
    fmt.Println("Split into", len(res), "bins of approx 5000 elements:")
    total := 0
    for _, r := range res {
        min := int(r[0])
        tmx := r[1]
        if tmx > actualMax {
            tmx = actualMax
        }
        max := int(tmx)
        cnt := int(r[2])
        total += cnt
        fmt.Printf("   From %6d to %6d with %4d items\n", min, max, cnt)
    }
    if total != numPrices {
        fmt.Println("Something went wrong - grand total of", total, "doesn't equal", numPrices, "\b!")
    }
}




Python: import random


price_list_size = random.choice(range(99_000, 101_000))
price_list = random.choices(range(100_000), k=price_list_size)

delta_price = 1     


def get_prange_count(startp, endp):
    return len([r for r in price_list if startp <= r <= endp])

def get_max_price():
    return max(price_list)


def get_5k(mn=0, mx=get_max_price(), num=5_000):
    "Binary search for num items between mn and mx, adjusting mx"
    count = get_prange_count(mn, mx)
    delta_mx = (mx - mn) / 2
    while count != num and delta_mx >= delta_price / 2:
        mx += -delta_mx if count > num else +delta_mx
        mx = mx // 1    
        count, delta_mx = get_prange_count(mn, mx), delta_mx / 2
    return mx, count

def get_all_5k(mn=0, mx=get_max_price(), num=5_000):
    "Get all non-overlapping ranges"
    partmax, partcount = get_5k(mn, mx, num)
    result = [(mn, partmax, partcount)]
    while partmax < mx:
        partmin = partmax + delta_price 
        partmax, partcount = get_5k(partmin, mx, num)
        assert partcount > 0, \
            f"price_list from {partmin} with too many of the same price"
        result.append((partmin, partmax, partcount))
    return result

if __name__ == '__main__':
    print(f"Using {price_list_size} random prices from 0 to {get_max_price()}")
    result = get_all_5k()
    print(f"Splits into {len(result)} bins of approx 5000 elements")
    for mn, mx, count in result:
        print(f"  From {mn:8.1f} ... {mx:8.1f} with {count} items.")

    if len(price_list) != sum(count for mn, mx, count in result):
        print("\nWhoops! Some items missing:")