Price list behind API

Revision as of 11:34, 25 November 2020 by rosettacode>Paddy3118 (New draft task with Python solution)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

There is a list of around 100_000 prices in the range £0 to £100_000, expressed in whole £, (no pence); and prices may be duplicated.
The API allows access to the maximum item price via function get_max_price(); and the number of items equal-to and between two given price points via function get_prange_count(pricemin, pricemax).
Assume that for the purposes of testing, you have access to the actual number of priced items to split.

Price list behind API is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task
  1. Write functions to randomly generate around 100K prices and provide the get_prange_count and get_max_price API calls.
  2. Write functions to provide non-overlapping min and max price ranges that provide product counts where most are close to, but no more than, 5_000.
  3. Ensure that all priced items are covered by all the ranges of prices shown
  4. Show ascending price ranges and the number of items covered by each range.
  5. Show output from a sample run here.

Python <lang python>import random

  1. %%Sample price generation

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

delta_price = 1 # Minimum difference between any two different prices.

  1. %% API

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)
  1. %% Solution

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    # Floor
       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)
       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:")</lang>
Output:
Using 99838 random prices from 0 to 99999
Splits into 20 bins of approx 5000 elements
  From      0.0 ...   4876.0 with 4999 items.
  From   4877.0 ...   9973.0 with 4997 items.
  From   9974.0 ...  14954.0 with 4999 items.
  From  14955.0 ...  20041.0 with 4997 items.
  From  20042.0 ...  25132.0 with 4999 items.
  From  25133.0 ...  30221.0 with 5000 items.
  From  30222.0 ...  35313.0 with 5000 items.
  From  35314.0 ...  40263.0 with 5000 items.
  From  40264.0 ...  45249.0 with 4997 items.
  From  45250.0 ...  50264.0 with 5000 items.
  From  50265.0 ...  55251.0 with 5000 items.
  From  55252.0 ...  60301.0 with 4997 items.
  From  60302.0 ...  65239.0 with 5000 items.
  From  65240.0 ...  70220.0 with 4998 items.
  From  70221.0 ...  75193.0 with 4999 items.
  From  75194.0 ...  80229.0 with 4996 items.
  From  80230.0 ...  85191.0 with 4997 items.
  From  85192.0 ...  90214.0 with 5000 items.
  From  90215.0 ...  95249.0 with 4999 items.
  From  95250.0 ... 104742.0 with 4864 items.