diff --git a/.gitignore b/.gitignore index 5b90e79..a92abe9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,143 @@ -# ---> Go -# If you prefer the allow list template instead of the deny list, see community template: -# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore -# -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions *.so -*.dylib -# Test binary, built with `go test -c` -*.test +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST -# Output of the go coverage tool, specifically when used with LiteIDE -*.out +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec -# Dependency directories (remove the comment below to include it) -# vendor/ +# Installer logs +pip-log.txt +pip-delete-this-directory.txt -# Go workspace file -go.work -go.work.sum +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ -# env file +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments .env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +logs/ +*.log + +# Project specific +config/local_*.yaml diff --git a/feed_formula_calculator.py b/feed_formula_calculator.py new file mode 100644 index 0000000..ae4e6d5 --- /dev/null +++ b/feed_formula_calculator.py @@ -0,0 +1,36 @@ +import optimize_feed + +# 示例调用 +if __name__ == "__main__": + # 原料数据(加工后,单位:%或MJ/kg,价格元/吨) + ingredients = { + '构树叶': {'蛋白': 15, '能量': 9, '纤维': 10, '赖氨酸': 0.5, '蛋氨酸': 0.1, '钙': 1.5, '磷': 0.2, '价格': 600}, + '玉米胚芽': {'蛋白': 11, '能量': 13, '纤维': 7, '赖氨酸': 0.4, '蛋氨酸': 0.2, '钙': 0.1, '磷': 0.3, + '价格': 1700}, + '麦麸': {'蛋白': 15, '能量': 10, '纤维': 12, '赖氨酸': 0.4, '蛋氨酸': 0.2, '钙': 0.1, '磷': 1.0, '价格': 1300}, + '米糠': {'蛋白': 13, '能量': 11, '纤维': 10, '赖氨酸': 0.4, '蛋氨酸': 0.2, '钙': 0.1, '磷': 1.5, '价格': 1100}, + '牧草': {'蛋白': 17, '能量': 8.5, '纤维': 20, '赖氨酸': 0.6, '蛋氨酸': 0.2, '钙': 1.2, '磷': 0.3, '价格': 800}, + '玉米': {'蛋白': 9, '能量': 13.5, '纤维': 2.5, '赖氨酸': 0.3, '蛋氨酸': 0.17, '钙': 0.02, '磷': 0.3, + '价格': 2367}, + '豆粕': {'蛋白': 46, '能量': 10.5, '纤维': 4, '赖氨酸': 2.8, '蛋氨酸': 0.6, '钙': 0.3, '磷': 0.7, '价格': 3300}, + '植物油': {'蛋白': 0, '能量': 36, '纤维': 0, '赖氨酸': 0, '蛋氨酸': 0, '钙': 0, '磷': 0, '价格': 7500}, + '赖氨酸': {'蛋白': 78, '能量': 0, '纤维': 0, '赖氨酸': 78, '蛋氨酸': 0, '钙': 0, '磷': 0, '价格': 15000}, + '蛋氨酸': {'蛋白': 99, '能量': 0, '纤维': 0, '赖氨酸': 0, '蛋氨酸': 99, '钙': 0, '磷': 0, '价格': 25000}, + '预混料': {'蛋白': 0, '能量': 0, '纤维': 0, '赖氨酸': 0, '蛋氨酸': 0, '钙': 10, '磷': 5, '价格': 3000} + } + + requirements = { + '蛋白_下限': 17, + '能量_下限': 13, + '纤维_上限': 5, + '赖氨酸_下限': 0.75, + '蛋氨酸_下限': 0.25, + '钙_下限': 0.5, + '磷_下限': 0.4 + } + + # 运行优化 + result = optimize_feed.optimize_feed(requirements, ingredients, ['比例', '成本'], 'min') + print("优化结果:") + for key, value in result.items(): + print(f"{key}: {value}") diff --git a/optimize_feed.py b/optimize_feed.py new file mode 100644 index 0000000..e9c123d --- /dev/null +++ b/optimize_feed.py @@ -0,0 +1,48 @@ +import pulp +def optimize_feed(requirements, ingredients, result_fields, optimization_type='min'): + """ + 优化饲料配方,计算最低/最高目标值的原料比例 + :param requirements: 猪营养需求字典,如 {'蛋白_下限': 16, '能量_下限': 12, '纤维_上限': 8} + :param ingredients: 原料数据字典,如 {'构树叶': {'蛋白': 18, '能量': 10, '纤维': 5, '价格': 500}} + :param result_fields: 结果字段列表,如 ['比例', '成本'] + :param optimization_type: 'min'(最小化,如成本)或 'max'(最大化,如消化率) + :return: 字典,含结果字段和值,如 {'构树叶': {'比例': 0.2, '成本': 100}, '总成本': 1475} + """ + # 创建模型 + sense = pulp.LpMinimize if optimization_type == 'min' else pulp.LpMaximize + model = pulp.LpProblem("Feed_Optimization", sense) + + # 决策变量:原料比例(0-1) + x = pulp.LpVariable.dicts("比例", ingredients, lowBound=0, upBound=1) + + # 目标函数:优化指定字段(如价格) + target_field = next(f for f in ingredients[list(ingredients.keys())[0]] if '价格' in f or '成本' in f) # 默认优化价格 + model += pulp.lpSum([x[i] * ingredients[i][target_field] for i in ingredients]), "目标" + + # 约束1:总比例=100% + model += pulp.lpSum([x[i] for i in ingredients]) == 1, "总比例" + + # 约束2:营养需求(下限/上限) + for req, value in requirements.items(): + if '下限' in req: + nutrient = req.replace('_下限', '') + model += pulp.lpSum([x[i] * ingredients[i][nutrient] for i in ingredients]) >= value, req + elif '上限' in req: + nutrient = req.replace('_上限', '') + model += pulp.lpSum([x[i] * ingredients[i][nutrient] for i in ingredients]) <= value, req + + # 求解 + model.solve() + + # 构建结果 + if pulp.LpStatus[model.status] != 'Optimal': + return {"错误": "无解!检查原料数据或需求(比如纤维全超上限?)"} + + result = {} + total_target = pulp.value(model.objective) + result['总' + target_field] = round(total_target, 2) + for i in ingredients: + result[i] = {field: round(pulp.value(x[i]) * 100, 2) if field == '比例' else round( + pulp.value(x[i]) * ingredients[i][target_field], 2) for field in result_fields} + return result +