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

Update app.py

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