DmitrMakeev commited on
Commit
8a9ca09
·
verified ·
1 Parent(s): f4bce88

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -95
app.py CHANGED
@@ -735,132 +735,116 @@ class NutrientCalculator:
735
  self.target_profile = BASE_PROFILE.copy()
736
  self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
737
  self.total_ec = 0.0
738
- self.best_solution = None
739
- self.min_difference = float('inf')
740
- self.max_recursion_depth = 1000 # Ограничение глубины рекурсии
741
- self.current_depth = 0
742
 
743
  # Расчёт азота
744
  total_parts = NO3_RATIO + NH4_RATIO
745
  self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
746
  self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
747
 
 
 
 
 
 
 
 
 
 
748
  def calculate(self):
749
  try:
 
750
  self.actual_profile = {k: 0.0 for k in self.target_profile}
751
  self.results = {}
752
- self.current_depth = 0
753
-
754
  if self._backtrack_search():
755
- print("Оптимальная комбинация найдена!")
756
- return self.best_solution
757
  else:
758
- return self.best_solution or {"error": "Не удалось найти идеальную комбинацию"}
759
 
760
  except Exception as e:
761
  print(f"Ошибка при расчёте: {str(e)}")
762
  raise
763
 
764
- def _backtrack_search(self, fertilizer_index=0, step=1.0):
765
- self.current_depth += 1
766
- if self.current_depth > self.max_recursion_depth:
767
- return False
768
-
769
- # Проверяем текущее решение
770
- current_diff = self._calculate_difference()
771
- if current_diff < self.min_difference:
772
- self.min_difference = current_diff
773
- self.best_solution = {
774
- "results": self._copy_results(),
775
- "actual_profile": self.actual_profile.copy(),
776
- "total_ec": self.total_ec,
777
- "difference": current_diff
778
- }
779
-
780
- if current_diff < 0.1: # Допустимая погрешность
781
- return True
782
 
783
- # Пробуем добавлять удобрения с текущего индекса
784
  for i in range(fertilizer_index, len(self.fertilizer_list)):
785
  fert_name = self.fertilizer_list[i]
786
-
787
- # Пробуем добавить удобрение с текущим шагом
788
- self._apply_fertilizer(fert_name, step)
789
-
790
- # Рекурсивно продолжаем поиск
791
- if self._backtrack_search(i, step):
792
- return True
793
-
794
- # Если не получилось - откатываемся
795
- self._remove_fertilizer(fert_name, step)
796
-
797
- # Пробуем уменьшить шаг для более точного поиска
798
- if step > 0.1:
799
- if self._backtrack_search(i, step/2):
800
  return True
801
 
 
 
 
 
802
  return False
803
 
804
- def _apply_fertilizer(self, fert_name, amount):
805
- """Добавляет указанное количество удобрения"""
 
 
 
 
 
 
 
 
806
  if fert_name not in self.results:
807
- self._init_fertilizer_record(fert_name)
808
-
809
- self.results[fert_name]['граммы'] += amount
810
- self.results[fert_name]['миллиграммы'] += int(amount * 1000)
811
-
812
- added_ec = 0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
  for element, content in self.fertilizers[fert_name].items():
814
- added_ppm = (amount * content * 1000) / self.volume
815
  self.results[fert_name][f'внесет {element}'] += added_ppm
816
  self.actual_profile[element] += added_ppm
817
- added_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
818
-
819
- self.results[fert_name]['вклад в EC'] += added_ec
820
- self.total_ec += added_ec
821
 
822
- def _remove_fertilizer(self, fert_name, amount):
823
- """Удаляет указанное количество удобрения"""
824
  if fert_name in self.results:
825
- self.results[fert_name]['граммы'] -= amount
826
- self.results[fert_name]['миллиграммы'] -= int(amount * 1000)
827
-
828
- removed_ec = 0.0
829
  for element, content in self.fertilizers[fert_name].items():
830
- removed_ppm = (amount * content * 1000) / self.volume
831
- self.results[fert_name][f'внесет {element}'] -= removed_ppm
832
- self.actual_profile[element] -= removed_ppm
833
- removed_ec += removed_ppm * EC_COEFFICIENTS.get(element, 0.0015)
834
-
835
- self.results[fert_name]['вклад в EC'] -= removed_ec
836
- self.total_ec -= removed_ec
837
-
838
- if self.results[fert_name]['граммы'] <= 0.001:
839
- del self.results[fert_name]
840
-
841
- def _calculate_difference(self):
842
- """Вычисляет общее отклонение от целевого профиля"""
843
- return sum(
844
- abs(self.actual_profile[e] - self.target_profile[e])
845
- for e in self.target_profile
846
- )
847
-
848
- def _copy_results(self):
849
- """Создаёт глубокую копию результатов"""
850
- return {
851
- fert: data.copy()
852
- for fert, data in self.results.items()
853
- }
854
-
855
- def _init_fertilizer_record(self, fert_name):
856
- """Инициализирует запись для нового удобрения"""
857
- self.results[fert_name] = {
858
- 'граммы': 0.0,
859
- 'миллиграммы': 0,
860
- 'вклад в EC': 0.0
861
- }
862
- for elem in self.fertilizers[fert_name]:
863
- self.results[fert_name][f'внесет {elem}'] = 0.0
864
 
865
  def calculate_ec(self):
866
  return round(self.total_ec, 2)
 
735
  self.target_profile = BASE_PROFILE.copy()
736
  self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
737
  self.total_ec = 0.0
 
 
 
 
738
 
739
  # Расчёт азота
740
  total_parts = NO3_RATIO + NH4_RATIO
741
  self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
742
  self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
743
 
744
+ # Сохраняем исходный профиль азота
745
+ self.initial_n_profile = {
746
+ "NO3-": self.target_profile['N (NO3-)'],
747
+ "NH4+": self.target_profile['N (NH4+)']
748
+ }
749
+
750
+ # Список всех удобрений
751
+ self.fertilizer_list = list(self.fertilizers.keys())
752
+
753
  def calculate(self):
754
  try:
755
+ # Инициализация фактического профиля и результата
756
  self.actual_profile = {k: 0.0 for k in self.target_profile}
757
  self.results = {}
758
+
759
+ # Начинаем рекурсивный поиск
760
  if self._backtrack_search():
761
+ print("Успешная комбинация найдена!")
762
+ return self.results
763
  else:
764
+ raise ValueError("Не удалось найти подходящую комбинацию удобрений.")
765
 
766
  except Exception as e:
767
  print(f"Ошибка при расчёте: {str(e)}")
768
  raise
769
 
770
+ def _backtrack_search(self, fertilizer_index=0):
771
+ """Рекурсивный поиск с возвратом"""
772
+ # Проверяем, достигнуты ли целевые значения
773
+ if self.validate_results():
774
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
775
 
776
+ # Пробуем добавить удобрения по очереди
777
  for i in range(fertilizer_index, len(self.fertilizer_list)):
778
  fert_name = self.fertilizer_list[i]
779
+
780
+ # Применяем удобрение
781
+ if self._can_apply_fertilizer(fert_name):
782
+ self._apply_fertilizer(fert_name)
783
+ print(f"Добавлено удобрение: {fert_name}")
784
+
785
+ # Рекурсивно пробуем следующее удобрение
786
+ if self._backtrack_search(i + 1):
 
 
 
 
 
 
787
  return True
788
 
789
+ # Откатываемся назад (удаляем последнее удобрение)
790
+ self._remove_fertilizer(fert_name)
791
+ print(f"Откат: удалено удобрение {fert_name}")
792
+
793
  return False
794
 
795
+ def _can_apply_fertilizer(self, fert_name):
796
+ """Проверка, можно ли применить удобрение без перебора"""
797
+ for element, content in self.fertilizers[fert_name].items():
798
+ added_ppm = (1 * content * 1000) / self.volume # Добавляем 1 грамм для проверки
799
+ if self.actual_profile[element] + added_ppm > self.target_profile[element] + 0.1:
800
+ return False
801
+ return True
802
+
803
+ def _apply_fertilizer(self, fert_name):
804
+ """Применение удобрения"""
805
  if fert_name not in self.results:
806
+ self.results[fert_name] = {
807
+ 'граммы': 0.0,
808
+ 'миллиграммы': 0,
809
+ 'вклад в EC': 0.0
810
+ }
811
+ for elem in self.fertilizers[fert_name]:
812
+ self.results[fert_name][f'внесет {elem}'] = 0.0
813
+
814
+ # Находим минимальный недостаток среди всех элементов удобрения
815
+ min_required_ppm = float('inf')
816
+ for element, content in self.fertilizers[fert_name].items():
817
+ required_ppm = self.target_profile[element] - self.actual_profile[element]
818
+ if required_ppm > 0.1:
819
+ min_required_ppm = min(min_required_ppm, required_ppm / content)
820
+
821
+ # Добавляем удобрение в количестве, необходимом для покрытия минимального недостатка
822
+ grams = min_required_ppm * 1000
823
+ self.results[fert_name]['граммы'] += grams
824
+ self.results[fert_name]['миллиграммы'] += int(grams * 1000)
825
+
826
  for element, content in self.fertilizers[fert_name].items():
827
+ added_ppm = (grams * content * 1000) / self.volume
828
  self.results[fert_name][f'внесет {element}'] += added_ppm
829
  self.actual_profile[element] += added_ppm
 
 
 
 
830
 
831
+ def _remove_fertilizer(self, fert_name):
832
+ """Удаление удобрения"""
833
  if fert_name in self.results:
834
+ grams = self.results[fert_name]['граммы']
 
 
 
835
  for element, content in self.fertilizers[fert_name].items():
836
+ added_ppm = (grams * content * 1000) / self.volume
837
+ self.actual_profile[element] -= added_ppm
838
+ del self.results[fert_name]
839
+
840
+ def validate_results(self):
841
+ """Проверка соответствия целевым значениям"""
842
+ for element, target_value in self.target_profile.items():
843
+ actual_value = self.actual_profile[element]
844
+ if abs(actual_value - target_value) > 0.1: # Допустимая погрешность
845
+ print(f"Несоответствие: {element} (цель: {target_value:.2f}, фактически: {actual_value:.2f})")
846
+ return False
847
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
848
 
849
  def calculate_ec(self):
850
  return round(self.total_ec, 2)