-1

So my problem is a little bit like knapsnack problem but with a bit of difference. Let's say I went to shopping and I am obliged to buy things that meet the requirements or the closest I can get (over the requirement or lower). For a better explanation let's say I need to know how much items do I buy from each shopping item that brings me the closest to my requirements with at least one item each. For example my requirements are:

  1. Weight: 20 kg
  2. Price: 20 usd
  3. Calories: 300 kcal

and each item has a weight, price and calories with the possibility that any one of those can be 0:

  1. Item_1: { weight: 1kg, price: 0.5 usd, calories: 0 kcal, }
  2. Item_2: { weight: 3kg, price: 3 usd, calories: 70 kcal, }

I need to find the closest combination of n items that brings me close to my requirements (over it or lower than it as long as it is the closest)

I tried the knapsack problem and packing problem but with no luck.

----- Edit -----

As suggested let me clear some things, What I mean by closest is how many I should buy from each item to get as close as I can to the required weight, price and calories, as per example provided above I would need 6 from item_2 and 4 from item_1 meaning I would get a total of:

{ weight: 6 * 3 + 4 * 1 = 22 kg

price: 6 * 3 + 4 * 0.5 = 20 usd

calories: 6 * 70 + 4 * 0 = 420 kcal }

Which is (I think) the closest I can get to the requirements

taha boud
  • 1
  • 2
  • That sounds like a https://en.wikipedia.org/wiki/Linear_programming problem, so you might look that up. For example, if x is the number of item_1 and y the number of item_2, the weight constraint is: 1x + 3y <= 20 . Representing this (and the other constraints) by lines will help you visualize and thus think about the problem. – Swifty Feb 21 '23 at 17:25
  • Welcome to [Stack Overflow.](https://stackoverflow.com/ "Stack Overflow"). This site is not a code-writing or tutoring service, it is best used when you have a specific problem with written code. Your question, reads like "Can someone code this for me" or "Show me a tutorial or information on how to code this" which are off-topic for Stackoverflow. Please take the [tour](https://stackoverflow.com/tour), read [what's on-topic here](https://stackoverflow.com/help/on-topic). – itprorh66 Feb 21 '23 at 18:10
  • @itprorh66 I know what this website is about as I am a member on this website longer than you, I am not looking for someone to write code for me, I am simply looking for guidance to an article or anything that can help me solve this issue, which Swifty thankfully understood and helped me except it was not what I needed. – taha boud Feb 21 '23 at 19:03
  • I think your problem statement needs further clarification. Since, any optimization solution should have a limiting control such as maximize calories and minimize price subject to total weight <= capacity. As written your problem doesn't have any limiting criterion. – itprorh66 Feb 21 '23 at 19:08
  • You should probably begin by defining 'closest', which means defining the distance you'll use (for exemple the sum of the absolute distances from your combination's (weight, price, calories) to (20, 20, 300), or any other distance relevant to your needs) – Swifty Feb 21 '23 at 19:51

1 Answers1

0

While a solution might be possible using either a knapsack or linear programming solution, given your stated set of requirements, (i.e., minimize difference from target W, P, C and contains at least 1 of each product). I believe the simplest approach would be as follows:

create list of product options.
create a solution containing 1 each of items because you must include 1 of each product
compute the solution difference from target ideal
push solution onto a queue, using diff as key
while queue exists:
a. pop lowest diff solution from queue.
b. cur_sol = queue solution
c. cur_diff = queue key
d. for itm in product list:
i. add item to cur_sol to form new_sol
ii. compute new_diff
iii. if new_dif <= cur_diff
         - push new_sol, new_diff onto queue  
return cur_sol

The following code implements the above solution

from __future__ import annotations
from dataclasses import dataclass
from copy import deepcopy



@dataclass
class Product:
    weight: float    # product weight
    price: float    # product price in usd
    cals: float   # product calories

class Solution:
    
    def __init__(self, Wght: float, Prc: float, Cals: float, Prds: list[Product]):
        self.Tgt_Wght = Wght
        self.Tgt_Prc = Prc
        self.Tgt_Cals = Cals
        self.Prods_in = self.buildProds_in(Prds)
        self.Cur_Wght = self.computeFactor('W')
        self.Cur_Prc = self.computeFactor('P')
        self.Cur_Cals = self.computeFactor('C')
        self.Cur_diff = self.evaluate
        
    @property
    def evaluate(self) -> float:
        return (abs((self.Tgt_Wght - self.Cur_Wght)/self.Tgt_Wght) +
               abs((self.Tgt_Prc - self.Cur_Prc)/self.Tgt_Prc) +
               abs((self.Tgt_Cals - self.Cur_Cals)/self.Tgt_Cals))
    
    
    def best(self, other: Solution) -> bool:
        return self.evaluate <= other.evaluate
    
    def buildProds_in(self, pds) -> list[list[int, Product]]:
        rslt = []
        for p in pds:
            rslt.append([1, p])
        return rslt
            
    def addProduct(self, idx: int) -> None:
        self.Prods_in[idx][0] += 1
        self.Cur_Wght = self.computeFactor('W')
        self.Cur_Prc = self.computeFactor('P')
        self.Cur_Cals = self.computeFactor('C')
        self.Cur_diff = self.evaluate
            
    def computeFactor(self, f: str) -> float:
        rslt = 0
        if f == 'W':
            for cnt, p in self.Prods_in:
                rslt += (cnt * p.weight)
        elif f == 'P':
            for cnt, p in self.Prods_in:
                rslt += (cnt * p.price)
        elif f == 'C':
            for cnt, p in self.Prods_in:
                rslt += (cnt * p.cals)
        else:
            raise ValueError(f'Factor {f} must be one of W, P, C' )
        
        return rslt
    
    def __repr__(self):
        s = f'Weight= {self.Cur_Wght},\n'
        s += f'Price= {self.Cur_Prc},\n'
        s += f'Calories= {self.Cur_Cals}\n'
        s += f'Product Qty: [{", ".join([str(x[0]) for x in self.Prods_in])}]'
        return s
        

def readInput(indat: str)-> dict:
    rslt = {'W': None, 'P': None, 'C': None,
           'Options':[]}
    lines = indat.split('\n')
    W, P, C =  map( float, lines[0].strip().split())
    rslt['W'] = W
    rslt['P'] = P
    rslt['C'] = C
    prodList = []
    for i in range(len(lines)-1):
        ln = lines[i+1].strip().split()
        if ln:
            pw, pp, pc = map( float, ln)
            prodList.append(Product(pw, pp, pc)) 
    
    rslt['Options'] = prodList
    return rslt


def solveProblem(indat):
    defDict = readInput(indat)  # read input parameters
    q = []
    Cbs = Solution(defDict["W"], defDict["P"], defDict["C"],
                defDict["Options"])
    q.append((Cbs.Cur_diff, Cbs))
    while q:
        q = sorted(q, reverse=True)
        ky, bs = q.pop()
        if bs.best(Cbs):
            Cbs = bs
        for i in range(len(bs.Prods_in)):
            s = deepcopy(bs)
            s.addProduct(i)
            if s.best(Cbs):
                q.append((s.Cur_diff, s))
    return(Cbs)     

Then given an input of the form:

InputA = """36. 40. 70.
            5.2 8.3 15.
            7.6 9.7 22.7
            4.5 3.6 9.2
            """   

Where first line contains objective W, P, C respectively and subsequent lines identify product options (W, P, c), then executing:

  print(solveProblem(InputA))  

Yields: Weight= 33.9, Price= 38.5, Calories= 88.0 Product Qty: [1, 2, 3] # The qty of each product bought

itprorh66
  • 3,110
  • 4
  • 9
  • 21