Кондитерская продает торты с белковым кремом по це...
作成日: 2025年9月17日
作成日: 2025年9月17日
Кондитерская продает торты с белковым кремом по цене 370 руб. Себестоимость изготовления торта составляет 200 руб. Если сразу изготавливать большое количество тортов (40 и больше), то их себестоимость снизится до 180 руб. Если торт не продан в течение суток, то его придется выбросить. Результаты продаж за последнее время сведены в таблицу:
Спрос 30 35 40 45 50 55 60 65 70
Случаев 2 3 5 8 10 11 12 6 2
pythondemand = [30,35,40,45,50,55,60,65,70] cases = [2,3,5,8,10,11,12,6,2] total_cases = sum(cases) probs = [c/total_cases for c in cases] total_cases, probs[:3], sum(probs) (59, [0.03389830508474576, 0.05084745762711865, 0.0847457627118644], 1.0)
pythonimport numpy as np price = 370 def unit_cost(q): return 180 if q>=40 else 200 def expected_profit(q): rev=0 for d,p in zip(demand, probs): rev += price*min(q,d)*p cost = unit_cost(q)*q return rev - cost res = [(q, expected_profit(q)) for q in range(10,81)] max(res, key=lambda x: x[1])[:5] (55, 8474.57627118644)
python[(q, round(expected_profit(q),2)) for q in range(40,71,5)] [(40, 7380.51), (45, 8016.95), (50, 8402.54), (55, 8474.58), (60, 8201.69), (65, 7552.54), (70, 6715.25)]
python[(q, expected_profit(q)) for q in range(50,61)] [(50, 8402.542372881355), (51, 8416.949152542376), (52, 8431.35593220339), (53, 8445.762711864409), (54, 8460.169491525423), (55, 8474.57627118644), (56, 8420.0), (57, 8365.42372881356), (58, 8310.847457627118), (59, 8256.271186440677), (60, 8201.694915254237)]
pythonimport numpy as np mean_d = sum(d*p for d,p in zip(demand, probs)) mean_d 52.20338983050848
pythondef expected_unsold(q): # E[(q-D)+] s=0 for d,p in zip(demand, probs): s += max(q-d,0)*p return s def stockout_prob(q): # P(D>q) return sum(p for d,p in zip(demand, probs) if d>q) expected_uns = expected_unsold(55) stockout = stockout_prob(55) expected_uns, stockout (5.338983050847457, 0.33898305084745767)
Задача: выбрать объём выпечки , чтобы максимизировать ожидаемую прибыль за день.
Цена: 370 ₽.
Себестоимость: 200 ₽, а если — 180 ₽.
Непроданное выбрасывается (ликвидационной стоимости нет).
Плотность спроса задаётся выборкой (всего 59 наблюдений):
30,35,40,45,50,55,60,65,70 шт с частотами 2,3,5,8,10,11,12,6,2.
Прибыль при спросе и выпуске :
Ожидаемая прибыль:
где вероятности берём как «случаев/59».
Ожид. прибыль, ₽ | |
---|---|
40 | 7 381 |
45 | 8 017 |
50 | 8 403 |
55 | 8 475 |
60 | 8 202 |
65 | 7 553 |
70 | 6 715 |
(Лучшее среди ≈ 6 442 ₽ — заметно хуже, из-за высокой себестоимости 200 ₽.)
Выпекать 55 тортов в день.
Ожидаемая прибыль ≈ 8 475 ₽/день (с учётом себестоимости 180 ₽ за штуку).
Дополнительно (для понимания рисков при ):
Если нужно, могу показать детальный расчёт для любого другого или построить график «ожидаемая прибыль vs. выпуск».
pythonimport numpy as np, pandas as pd price=370 def cost(q): return 200 if q<40 else 180 demands=[30,35,40,45,50,55,60,65,70] freq=[2,3,5,8,10,11,12,6,2] n=sum(freq) probs=[f/n for f in freq] n, probs[:3], sum(probs) (59, [0.03389830508474576, 0.05084745762711865, 0.0847457627118644], 1.0)
pythondef profit(q,d): c=200 if q<40 else 180 sold=min(q,d) return price*sold - c*q qs=[30,35,40,45,50,55,60,65,70] payoff=pd.DataFrame(index=qs, columns=demands, dtype=float) for q in qs: for d in demands: payoff.loc[q,d]=profit(q,d) payoff.head()[:3] 30 35 40 45 50 55 60 65 70 30 5100.0 5100.0 5100.0 5100.0 5100.0 5100.0 5100.0 5100.0 5100.0 35 4100.0 5950.0 5950.0 5950.0 5950.0 5950.0 5950.0 5950.0 5950.0 40 3900.0 5750.0 7600.0 7600.0 7600.0 7600.0 7600.0 7600.0 7600.0
pythonemv=payoff.dot(np.array(probs)) emv 30 5100.000000 35 5887.288136 40 7380.508475 45 8016.949153 50 8402.542373 55 8474.576271 60 8201.694915 65 7552.542373 70 6715.254237 dtype: float64
pythonbest_by_demand=payoff.max(axis=0) regret=best_by_demand - payoff best_by_demand, regret.head(3) (30 5100.0 35 5950.0 40 7600.0 45 8550.0 50 9500.0 55 10450.0 60 11400.0 65 12350.0 70 13300.0 dtype: float64, 30 35 40 45 50 55 60 65 70 30 0.0 850.0 2500.0 3450.0 4400.0 5350.0 6300.0 7250.0 8200.0 35 1000.0 0.0 1650.0 2600.0 3550.0 4500.0 5450.0 6400.0 7350.0 40 1200.0 200.0 0.0 950.0 1900.0 2850.0 3800.0 4750.0 5700.0)
pythoneol=regret.dot(np.array(probs)) emv, eol, (best_by_demand*probs).sum() (30 5100.000000 35 5887.288136 40 7380.508475 45 8016.949153 50 8402.542373 55 8474.576271 60 8201.694915 65 7552.542373 70 6715.254237 dtype: float64, 30 4762.711864 35 3975.423729 40 2482.203390 45 1845.762712 50 1460.169492 55 1388.135593 60 1661.016949 65 2310.169492 70 3147.457627 dtype: float64, 9862.71186440678)
Ниже — всё, что нужно.
Цена ₽. Себестоимость:
Прибыль при спросе и выпуске :
Спрос (частоты → вероятности, всего 59 наблюдений):
D | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 |
---|---|---|---|---|---|---|---|---|---|
случаев | 2 | 3 | 5 | 8 | 10 | 11 | 12 | 6 | 2 |
0.0339 | 0.0508 | 0.0847 | 0.1356 | 0.1695 | 0.1864 | 0.2034 | 0.1017 | 0.0339 |
Строки — объём выпечки , столбцы — спрос .
q\D | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 |
---|---|---|---|---|---|---|---|---|---|
30 | 5100 | 5100 | 5100 | 5100 | 5100 | 5100 | 5100 | 5100 | 5100 |
35 | 4100 | 5950 | 5950 | 5950 | 5950 | 5950 | 5950 | 5950 | 5950 |
40 | 3900 | 5750 | 7600 | 7600 | 7600 | 7600 | 7600 | 7600 | 7600 |
45 | 3750 | 5600 | 7450 | 8550 | 8550 | 8550 | 8550 | 8550 | 8550 |
50 | 3600 | 5450 | 7300 | 8400 | 9500 | 9500 | 9500 | 9500 | 9500 |
55 | 3450 | 5300 | 7150 | 8250 | 9350 | 10450 | 10450 | 10450 | 10450 |
60 | 3300 | 5150 | 7000 | 8100 | 9200 | 10300 | 11400 | 11400 | 11400 |
65 | 3150 | 5000 | 6850 | 7950 | 9050 | 10150 | 11250 | 12350 | 12350 |
70 | 3000 | 4850 | 6700 | 7800 | 8900 | 10000 | 11100 | 12200 | 13300 |
Потеря = (максимальная прибыль в столбце при данном ) – .
q\D | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 |
---|---|---|---|---|---|---|---|---|---|
30 | 0 | 850 | 2500 | 3450 | 4400 | 5350 | 6300 | 7250 | 8200 |
35 | 1000 | 0 | 1650 | 2600 | 3550 | 4500 | 5450 | 6400 | 7350 |
40 | 1200 | 200 | 0 | 950 | 1900 | 2850 | 3800 | 4750 | 5700 |
45 | 1350 | 350 | 150 | 0 | 1000 | 1900 | 2850 | 3800 | 4750 |
50 | 1500 | 500 | 300 | 150 | 0 | 950 | 1900 | 2850 | 3800 |
55 | 1650 | 650 | 450 | 300 | 150 | 0 | 950 | 1900 | 2850 |
60 | 1800 | 800 | 600 | 450 | 300 | 150 | 0 | 950 | 1900 |
65 | 1950 | 950 | 750 | 600 | 450 | 300 | 150 | 0 | 950 |
70 | 2100 | 1100 | 900 | 750 | 600 | 450 | 300 | 150 | 0 |
Ожидаемая прибыль :
EMV, ₽ | |
---|---|
30 | 5 100.00 |
35 | 5 887.29 |
40 | 7 380.51 |
45 | 8 016.95 |
50 | 8 402.54 |
55 | 8 474.58 |
60 | 8 201.69 |
65 | 7 552.54 |
70 | 6 715.25 |
Решение по EMV: выпускать тортов в день.
Ожидаемая прибыль ≈ 8 474.6 ₽.
Проверка через ожидаемую упущенную выгоду (EOL): минимальна также при (EOL≈1 388.14 ₽), что согласуется с EMV.
pythonimport math D=2600 S=800 prices=[350,330,320,300] breaks=[1,100,400,1000] # min quantity for each price tier ranges=[(1,99),(100,399),(400,999),(1000,10**9)] candidates=[] for P,(L,U) in zip(prices,ranges): h=0.15*P Q=math.sqrt(2*D*S/h) feasible=L<=Q<=U if feasible: candidates.append((P,L,U,'EOQ',Q)) # also consider lower bound if not feasible? For all price tiers, for higher discount tiers, we can order at least L candidates.append((P,L,U,'LB',L)) candidates[:10],len(candidates) ([(350, 1, 99, 'LB', 1), (330, 100, 399, 'EOQ', 289.89723013579146), (330, 100, 399, 'LB', 100), (320, 400, 999, 'LB', 400), (300, 1000, 1000000000, 'LB', 1000)], 5)
pythonfor P,(L,U) in zip(prices,ranges): h=0.15*P Q=math.sqrt(2*D*S/h) print(P,h,Q,L,U)
pythondef total_cost(P,Q): D=2600;S=800;h=0.15*P return D*P + S*D/Q + h*Q/2 for (P,L,U,tag,Q) in candidates: print(tag,P,Q,total_cost(P,Q))
pythonD=2600; S=800 divisors=[] for K in range(1,51): if D%K==0: Q=D//K # determine price bracket if Q>=1000: P=300 elif Q>=400: P=320 elif Q>=100: P=330 else: P=350 h=0.15*P total= D*P + K*S + h*Q/2 divisors.append((K,Q,P,total)) divisors[:10], len(divisors) ([(1, 2600, 300, 839300.0), (2, 1300, 300, 810850.0), (4, 650, 320, 850800.0), (5, 520, 320, 848480.0), (8, 325, 330, 872443.75), (10, 260, 330, 872435.0), (13, 200, 330, 873350.0), (20, 130, 330, 877217.5), (25, 104, 330, 880574.0), (26, 100, 330, 881275.0)], 12)
pythondef plan_cost(orders): D=2600;S=800 total_purchase=0 total_ordering=S*len(orders) holding=0 for Q,P in orders: total_purchase+=Q*P h=0.15*P holding+= h * Q*Q /(2*D) return total_purchase+total_ordering+holding plans=[ [('two1300',)], # placeholder ] print(plan_cost([(1000,300),(1000,300),(600,320)]))
Разберём «военный госпиталь» как EOQ с по-ассортиментными (all-units) скидками.
Дано
интервал | цена |
---|---|
Для каждого ценового уровня считаем классический и проверяем, попадает ли он в соответствующий интервал.
Если не попадает — рассматриваем нижнюю границу интервала (минимум, чтобы получить цену). Полная годовая стоимость:
Расчёты (руб.):
Вывод (a): оптимально брать (получаем цену 300 руб.),
число заказов в среднем (≈ 3 поставки в год),
минимальная полная годовая стоимость руб.
Требование: и количество заказов — целое.
Поскольку цена 300 руб. действует при , выгодно закупать только с этой ценой, а для минимизации складских затрат — равными партиями (суммарные годовые издержки хранения при разных равны , минимум — при равных внутри уровня цены).
Лучший план: 2 заказа по 1300 (оба → цена 300).
Проверка затрат:
Итого руб.
Для сравнения, вариант «1000+1000+600» (две партии по 1000 по 300 руб. и одна 600 по 320 руб.) даёт руб. — хуже из-за более высокой закупочной цены и больших складских.
pythonD=255*52 S=26 H=0.93 Q=(2*D*S/H)**0.5 D,S,H,Q (13260, 26, 0.93, 861.0571147367111)
pythonimport math z=70/104 Phi=0.5*(1+math.erf(z/math.sqrt(2))) Phi 0.7495508228047897
pythonimport math def z_from_p(p): lo,hi=-10,10 for _ in range(80): mid=(lo+hi)/2 if 0.5*(1+math.erf(mid/math.sqrt(2)))<p: lo=mid else: hi=mid return (lo+hi)/2 for p in [0.994,0.965,0.91,0.75]: print(p, z_from_p(p))
Ниже — расчёт по классической модели (EOQ + точка заказа с нормальным спросом).
Дополнительно:
Для циклового уровня обслуживания:
(безопасный резерв ед.)
Затраты, зависящие только от уровня сервиса, — это годовые затраты на поддержание страхового запаса:
Разница: /годP_1P_2$).
Риск дефицита на цикл .