Решение задачи Эйнштейна на платформе 1с

12.08.20

Разработка - Математика и алгоритмы

Недавно мне попалась интересная задача по созданию обработки, которая будет решать "задачу Эйнштейна". Изначально кажется, что можно просто прописать все явные и неявные условия через "Если", но это не верно. При таком подходе задачу решает ваш мозг, а решить задачу должна сама обработка основываясь только на условиях явно прописанных в тексте. Разработчик не должен делать никаких выводов и прописывать косвенные условия вытекающие из условия задачи. Условия задачи в коде должны переставляться в любом сочетании и это не должно влиять на решение.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Внешняя обработка для решение задачи Эйнштейна
.epf 12,58Kb
4
4 Скачать (1 SM) Купить за 1 850 руб.

Текст задачи:

На улице стоят пять домов. Англичанин живет в красном доме. У испанца есть собака. В зеленом доме пьют кофе. Украинец пьет чай. Зеленый дом стоит сразу справа от белого дома. Тот, кто курит Old Gold, разводит улиток. В желтом доме курят Kool. В центральном доме пьют молоко. Норвежец живет в первом доме. Сосед того, кто курит Chesterfield, держит лису. В доме по соседству с тем, в котором держат лошадь, курят Kool. Тот, кто курит Lucky Strike, пьет апельсиновый сок. Японец курит Parliament. Норвежец живет рядом с синим домом. Каждый из домов покрашен в отдельный цвет, в каждом доме живет представитель отдельной национальности, у каждого — свой питомец, своя любимая марка сигарет и напиток

Требуется:

Реализовать на 1с алгоритм выводящий возможный вариант расположения домов, цветов, национальностей, питомцев, напитков и марок сигарет.

 

Решение:

Предлагаемое решение основано рекурсивном просчитывании каждого варианта вглубь.
Т.е. предполагаем что в доме например живет Англичанин. Это рождает новую ветку решения. В этой ветке последовательно заполняем все пустые значения возможными вариантами и проверяем на условия. Каждый вариант рождает новую ветку решения.
Когда перебраны все варианты для ветки, проверяем правильность решения (заполненность всей таблицы и соответствие условиям). Если решение не найдено переходим к следующей ветке.

 

Сначала переведем текст задачи в набор условий. Набором будет ТаблицаЗначений. Прописываем только условия, явно указанные в тексте задачи. При этом результат условия должен быть уникален для всей таблицы. 

Функция ПолучитьТаблицуУсловий()
	//тип|значение ЕСЛИ ТипДом|ТипУсловия = ЗначениеУсловия
	//Тип и Значение должны быть уникальны

	ТаблицаУсловий = Новый ТаблицаЗначений;
	ТаблицаУсловий.Колонки.Добавить("Тип");
	ТаблицаУсловий.Колонки.Добавить("Значение");
	ТаблицаУсловий.Колонки.Добавить("ТипДома");
	ТаблицаУсловий.Колонки.Добавить("ТипУсловия");
	ТаблицаУсловий.Колонки.Добавить("ЗначениеУсловия");
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Национальность";
	НовоеУсловие.Значение        = "Англичанин";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Цвет";
	НовоеУсловие.ЗначениеУсловия = "Красный";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Национальность";
	НовоеУсловие.Значение        = "Испанец";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Питомец";
	НовоеУсловие.ЗначениеУсловия = "Собака";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Цвет";
	НовоеУсловие.Значение        = "Зеленый";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Напиток";
	НовоеУсловие.ЗначениеУсловия = "Кофе";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Национальность";
	НовоеУсловие.Значение        = "Украинец";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Напиток";
	НовоеУсловие.ЗначениеУсловия = "Чай";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Цвет";
	НовоеУсловие.Значение        = "Белый";
	НовоеУсловие.ТипДома         = "справа";
	НовоеУсловие.ТипУсловия      = "Цвет";
	НовоеУсловие.ЗначениеУсловия = "Зеленый";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "МаркаСигарет";
	НовоеУсловие.Значение        = "OldGold";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Питомец";
	НовоеУсловие.ЗначениеУсловия = "Улитки";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "МаркаСигарет";
	НовоеУсловие.Значение        = "Kool";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Цвет";
	НовоеУсловие.ЗначениеУсловия = "Желтый";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Питомец";
	НовоеУсловие.Значение        = "Лошадь";
	НовоеУсловие.ТипДома         = "соседний";
	НовоеУсловие.ТипУсловия      = "МаркаСигарет";
	НовоеУсловие.ЗначениеУсловия = "Kool";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "Питомец";
	НовоеУсловие.Значение        = "Лиса";
	НовоеУсловие.ТипДома         = "соседний";
	НовоеУсловие.ТипУсловия      = "МаркаСигарет";
	НовоеУсловие.ЗначениеУсловия = "Chesterfield";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "МаркаСигарет";
	НовоеУсловие.Значение        = "LuckyStrike";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Напиток";
	НовоеУсловие.ЗначениеУсловия = "Сок";
	
	НовоеУсловие                 = ТаблицаУсловий.Добавить();
	НовоеУсловие.Тип             = "МаркаСигарет";
	НовоеУсловие.Значение        = "Parlament";
	НовоеУсловие.ТипДома         = "текущий";
	НовоеУсловие.ТипУсловия      = "Национальность";
	НовоеУсловие.ЗначениеУсловия = "Японец";
	
	возврат ТаблицаУсловий;

КонецФункции

 

Теперь создадим функцию, которая для строки таблицы условий ставит конкретное значение. Проверяем как прямое так и обратное условие. Например прямое: если дом = желтый, то сигареты = kool и обратное: если сигареты = kool, то дом = желтый.

Функция ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом)
	Если ДомДляПроверкиУсловия = неопределено Тогда
		возврат ложь;
	КонецЕсли;
	
	Если ДомДляПроверкиУсловия[условие.ТипУсловия] = условие.ЗначениеУсловия И ПустаяСтрока(Дом[условие.Тип]) Тогда
		возврат УстановитьЗначение(Дом, условие.Тип, условие.Значение);
		//и обратное	
	ИначеЕсли ДомДляПроверкиУсловия[условие.Тип] = условие.Значение И ПустаяСтрока(Дом[условие.ТипУсловия]) Тогда
		возврат УстановитьЗначение(Дом, условие.ТипУсловия, условие.ЗначениеУсловия);
	КонецЕсли;
	
	возврат ложь;
КонецФункции

 

Далее создаем процедуру которая для текущего состояния решения определит все значения по таблице условий. После выполнения каждого условия опять вызываем рекурсивно процедуру определения значений. Например мы знаем, что "В доме по соседству с тем, в котором держат лошадь, курят Kool" и "В желтом доме курят Kool". Если мы в текущей итерации поставили сигареты = Kool, т.к. в соседнем доме лощадь, то мы сразу же должны и установить цвет в желтый.

//Проставляет зависимые значения по всем домам
Процедура ОпределитьЗначения(ТаблицаУсловий, ТекущаяТаблица)
	Для каждого Дом из ТекущаяТаблица Цикл
		ОпределитьЗначенияДома(ТаблицаУсловий,ТекущаяТаблица, Дом);
	КонецЦикла;
КонецПроцедуры

//Проставляет зависимые значения для конкретного дома
Процедура ОпределитьЗначенияДома(ТаблицаУсловий, ТекущаяТаблица, Дом)
	//после изменения любого реквизита заново пересчитываем всю таблицу
	Для каждого условие из ТаблицаУсловий Цикл		
		Если условие.ТипДома = "текущий" Тогда
			ДомДляПроверкиУсловия = Дом;	
			Если ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом) Тогда
				ОпределитьЗначения(ТаблицаУсловий,ТекущаяТаблица);
			КонецЕсли;
			
		ИначеЕсли условие.ТипДома = "справа" Тогда
			ДомДляПроверкиУсловия = СоседСправа(Дом);		
			Если ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом) Тогда
				ОпределитьЗначения(ТаблицаУсловий,ТекущаяТаблица);
			КонецЕсли;
					
		ИначеЕсли условие.ТипДома = "слева" Тогда
			ДомДляПроверкиУсловия = СоседСлева(Дом);
			Если ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом) Тогда
				ОпределитьЗначения(ТаблицаУсловий,ТекущаяТаблица);
			КонецЕсли;
			
		ИначеЕсли условие.ТипДома = "соседний" Тогда
			ДомДляПроверкиУсловия = СоседСправа(Дом);		
			Если НЕ ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом) Тогда
				ДомДляПроверкиУсловия = СоседСлева(Дом);
				Если ВыполнитьУсловие(условие,ДомДляПроверкиУсловия,Дом) Тогда
					ОпределитьЗначения(ТаблицаУсловий,ТекущаяТаблица);
				КонецЕсли;
			Иначе
				ОпределитьЗначения(ТаблицаУсловий,ТекущаяТаблица);
			КонецЕсли;
			
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Переходим непосредственно к перебору вариантов решения.

Алгоритм такой:

  1. Ставим очередное возможное значение для типа
  2. Проверяем по всей таблице решения не нарушились ли условия. Если условия не выполняются, то переходим к п.1.
  3. Определяем все зависимые значения во всей таблице решения.
  4. Переходим к следующему типу значений (Цвет, Животное и т.д.) и повторяем с п.1
  5. Когда перебрали все типы проверяем вся ли таблица решения заполнена и все ли условия выполнены. Если решение не найдено, то повторяем с п.1 опять для каждого типа
Процедура РешитьЗадачуНаСервере()
	УстановитьНачальныеЗначения();
	ТаблицаУсловий = ПолучитьТаблицуУсловий();
	Если НЕ ТаблицаУсловийВерна(ТаблицаУсловий) Тогда
		возврат;
	КонецЕсли;
	
	ТекущаяТаблица = Объект.Дома.Выгрузить();
	Решение = НайтиРешение(ТаблицаУсловий,ТекущаяТаблица); 
	Если Решение = неопределено Тогда
		Сообщить("Решения нет",СтатусСообщения.ОченьВажное);
	Иначе
		Объект.Дома.Загрузить(Решение);
		Сообщить("Задача решена!");		
	КонецЕсли;
		
КонецПроцедуры

Функция НайтиРешение(ТаблицаУсловий,ТекущаяТаблица)
    //каждое решение - отдельная таблица производная от текущей
	Решение = ТекущаяТаблица.Скопировать();
	ОпределитьЗначения(ТаблицаУсловий,Решение);
	
	СтруктураВозможныхЗначений = ПолучитьСтруктуруВозможныхЗначений();
	
	Для каждого Дом из Решение Цикл
		//порядок вызова проверок может быть любой, на результат не влияет
		ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, "Цвет", СтруктураВозможныхЗначений.Цвета);
		ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, "Напиток", СтруктураВозможныхЗначений.Напитки);
		ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, "Национальность", СтруктураВозможныхЗначений.Национальность);		
		ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, "Питомец", СтруктураВозможныхЗначений.Питомцы);
		ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, "МаркаСигарет", СтруктураВозможныхЗначений.Сигареты);
		
	КонецЦикла;
		
	Если ПроверитьРешение(ТаблицаУсловий,Решение) Тогда
		возврат Решение;
	КонецЕсли;
	
	возврат неопределено;
КонецФункции

//Перебирает возможные варианты значения и проверяет есть ли решение при данном значении
Процедура ПроверитьВозможныеЗначения(ТаблицаУсловий, Дом, Тип, МассивВозможныхЗначений)
	Если НЕ ПустаяСтрока(Дом[Тип]) Тогда
		возврат;
	КонецЕсли;
	
	Для каждого возможноеЗначение из МассивВозможныхЗначений Цикл
		//если подошло, дальше не подставляем
		Если ПроверитьГипотезу(ТаблицаУсловий, Дом, Тип, возможноеЗначение) Тогда
			прервать;
		КонецЕсли;
	Конеццикла;
		
	ОпределитьЗначения(ТаблицаУсловий, Дом.Владелец());
КонецПроцедуры

Код проверки гипотезы. Ставит значение и проверяет не нарушаются ли при этом условия задачи.

//Проверяет, будут ли истины уловия если мы установим значение
Функция ПроверитьГипотезу(ТаблицаУсловий, Дом, Тип, Значение)
	//что будет если мы поставим такой тип
	Если УстановитьЗначение(Дом, Тип, Значение) Тогда
		
		Если НЕ ПроверитьВыполнениеУсловий(ТаблицаУсловий, Дом.Владелец()) Тогда
			Дом[Тип] = "";
			возврат Ложь;
		КонецЕсли;
		
		возврат Истина;
	КонецЕсли;
	
	возврат Ложь;
	
КонецФункции

Функция ПроверитьВыполнениеУсловий(ТаблицаУсловий, ТекущаяТаблица)
	Для каждого Дом из ТекущаяТаблица Цикл	
		Если не ПроверитьДом(ТаблицаУсловий, Дом) Тогда
			возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	возврат Истина;
КонецФункции

Функция ПроверитьДом(ТаблицаУсловий, Дом)
	Для каждого условие из ТаблицаУсловий Цикл		
		Если условие.ТипДома = "текущий" Тогда
			ДомДляПроверкиУсловия = Дом;	
			Если не ПроверитьУсловие(условие,ДомДляПроверкиУсловия, Дом) Тогда
				возврат ложь;
			КонецЕсли;
			
		ИначеЕсли условие.ТипДома = "справа" Тогда
			ДомДляПроверкиУсловия = СоседСправа(Дом);		
			Если не ПроверитьУсловие(условие,ДомДляПроверкиУсловия, Дом) Тогда
				возврат ложь;
			КонецЕсли;
					
		ИначеЕсли условие.ТипДома = "слева" Тогда
			ДомДляПроверкиУсловия = СоседСлева(Дом);
			Если не ПроверитьУсловие(условие,ДомДляПроверкиУсловия, Дом) Тогда
				возврат ложь;
			КонецЕсли;
			
		ИначеЕсли условие.ТипДома = "сосоедний" Тогда
			ДомДляПроверкиУсловия = СоседСправа(Дом);		
			Если НЕ ПроверитьУсловие(условие,ДомДляПроверкиУсловия, Дом) Тогда
				ДомДляПроверкиУсловия = СоседСлева(Дом);
				Если не ПроверитьУсловие(условие,ДомДляПроверкиУсловия, Дом) Тогда
					возврат ложь;
				КонецЕсли;
			КонецЕсли;
			
		КонецЕсли;
	КонецЦикла;
	
	возврат Истина;
КонецФункции

Функция ПроверитьУсловие(условие, ДомДляПроверкиУсловия, Дом)
	Если ДомДляПроверкиУсловия = неопределено 
		ИЛИ ПустаяСтрока(ДомДляПроверкиУсловия[условие.ТипУсловия]) 
		ИЛИ ПустаяСтрока(Дом[условие.Тип]) Тогда
		возврат Истина; //пустые значения пропускаем
	КонецЕсли;
	
	Если Дом[условие.Тип] = условие.Значение Тогда //если стоит проверяемое значение то проверим его условие
		Если ДомДляПроверкиУсловия[условие.ТипУсловия] = условие.ЗначениеУсловия Тогда
			возврат Истина;		
		КонецЕсли;
		
		//обратная проверка
	ИначеЕсли Дом[условие.ТипУсловия] = условие.ЗначениеУсловия Тогда //если стоит проверяемое значение то проверим его условие
		Если ДомДляПроверкиУсловия[условие.Тип] = условие.Значение Тогда
			возврат Истина;		
		КонецЕсли;
	Иначе
		возврат Истина;
	КонецЕсли;
	
	возврат Ложь;
КонецФункции

И наконец проверка самого решения.

Функция ПроверитьРешение(ТаблицаУсловий, ТекущаяТаблица)
	//1. Все ячейки таблицы должны быть заполнены
	//2. Должны быть выполнены условия
	Для каждого дом из ТекущаяТаблица Цикл
		Для каждого колонка из ТекущаяТаблица.Колонки Цикл
			Если ПустаяСтрока(Дом[колонка.Имя]) Тогда
				возврат ложь;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	возврат ПроверитьВыполнениеУсловий(ТаблицаУсловий, ТекущаяТаблица);
КонецФункции

 

задача Эйнштейна алгоритм

См. также

Математика и алгоритмы Программист Платформа 1C v8.2 Конфигурации 1cv8 Россия Абонемент ($m)

На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.

1 стартмани

30.01.2024    3459    stopa85    12    

38

Математика и алгоритмы Бесплатно (free)

Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.

19.10.2023    7954    user1959478    52    

36

Математика и алгоритмы Разное Платформа 1С v8.3 Конфигурации 1cv8 Россия Абонемент ($m)

Расширение (+ обработка) представляют собою математический тренажер. Ваш ребенок сможет проверить свои знание на математические вычисление до 100.

2 стартмани

29.09.2023    3397    maksa2005    8    

26

Математика и алгоритмы Инструментарий разработчика Программист Платформа 1С v8.3 Мобильная платформа Россия Абонемент ($m)

Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.

1 стартмани

09.06.2023    11151    8    SpaceOfMyHead    19    

61

Математика и алгоритмы Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.

03.04.2023    4652    RustIG    9    

25

Механизмы платформы 1С Математика и алгоритмы Программист Платформа 1С v8.3 Россия Бесплатно (free)

В статье анализируются средства платформы для решения системы линейных уравнений в 1С. Приводятся доводы в пользу некорректной работы встроенных алгоритмов, а значит потенциально некорректного расчета себестоимости в типовых конфигурациях.

23.11.2022    3809    gzharkoj    14    

25

Математика и алгоритмы Программист Платформа 1С v8.3 Россия Абонемент ($m)

Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса

1 стартмани

21.03.2022    9093    7    kalyaka    11    

44
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SerVer1C 816 12.08.20 14:48 Сейчас в теме
В молодые годы делал такое на шарпе с полным перебором ~ 6 млн комбинаций )
2. Cyberhawk 135 13.08.20 09:05 Сейчас в теме
Какой оклад предложили в оффере в итоге? :)
Оставьте свое сообщение