Если вы решите пойти по пути программирования ограничений, я думаю, что это будет лучшая идея, но вы можете сделать это с помощью грубой силы и коллекций .Net. Это будет не очень хорошо, но это должно сработать.
- Во-первых, вам нужен способ перечисления всех возможных разделов ваших добавок в комплекты с мощностью, равной количеству ингредиентов. [a, b, c], разделенные на три набора, могут дать
[abc,,]
, затем [ab,c,]
, затем [a,bc,]
и т. д.
- Перечисляя все эти множества, вам нужно будет перечислить все перестановки этих множеств
- Наконец, для каждой перестановки набора вам нужно будет проверить, удовлетворяются ли все правила текущим совпадением между ингредиентом и набором добавок
у меня не было времени, чтобы создать метод для разделения, но я имел удлинительный перестановку доступны, поэтому я создал второй этап программы
private static IList<string> Ingredients { get; set; }
private static IList<string> Additives { get; set; }
private static IList<Func<string, string, bool>> Rules { get; set; }
private static void Main(string[] args)
{
Ingredients = new List<string>() { "Apple", "Orange", "Peach" };
Additives = new List<string>() { "Vodka", "Rum", "Whiskey" };
Rules = new List<Func<string, string, bool>>() { (ingredient1, ingredient2) => { return (ingredient1 != "Peach" && ingredient2 != "Whiskey"); } };
var additivesOrganisationMatchingAllTheRules = FindMatch();
}
private static IList<string> FindMatch()
{
// here we should enumerate all sets and then enumerate permutation of all the sets
// instead for the example we just enumerate the permutations
foreach (var additivesPermutation in Additives.GetCombinations())
{
for (int i = 0; i < additivesPermutation.Count; i++)
{
var thisSituationIsOk = Rules.All(r => r(Ingredients[i], Additives[i]));
if (thisSituationIsOk) return additivesPermutation;
}
}
return null;
}
Он использует расширение метода перестановки; из того, что я помню, это расширение не сохраняет исходный список. Не использовать без тестов
public static class CombinatorialExtension
{
public static IEnumerable<IList<TSource>> GetCombinations<TSource>(
this IList<TSource> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
return GetCombinationsImpl<TSource>(source);
}
private static IEnumerable<IList<TSource>> GetCombinationsImpl<TSource>(
this IList<TSource> list)
{
return Permutations(list, list.Count);
}
private static void ShiftRight<TSource>(IList<TSource> list, int cardinality)
{
var lastElement = list[cardinality - 1];
list.RemoveAt(cardinality - 1);
list.Insert(0, lastElement);
}
private static IEnumerable<IList<TSource>> Permutations<TSource>(IList<TSource> list, int cardinality)
{
if (cardinality == 1)
{
yield return list;
}
else
{
for (int i = 0; i < cardinality; i++)
{
foreach (var perm in Permutations(list, cardinality - 1))
yield return perm;
ShiftRight(list, cardinality);
}
}
}
}
Этот алгоритм позволил бы найти решение, которое соответствует ингредиенты и правила, которые вы имеете, но это не очень красиво, а на самом деле действительно не эффективно: много комбинаций было бы вычислено несколько раз. Вы также должны настроить правила, чтобы получить результаты, которые были бы приятным (то есть минимальное и максимальное количество добавок и т.д.)
EDIT
Это даже возможно, чтобы избежать необходимости создавать все наборы, вы можете просто добавить столько добавок в ваши добавки, как у вас есть ингредиенты минус 1. Затем просто выполните перестановку и разделите свои добавки в соответствии с разделителями на списки добавок. Тогда ваши правила могут принять ингредиент и список добавок в качестве основы для проверки того, соблюдается ли оно. Вот пример кода
private static IList<string> Ingredients { get; set; }
private static IList<string> Additives { get; set; }
private static IList<Func<string, IList<string>, bool>> Rules { get; set; }
private static void Main(string[] args)
{
Ingredients = new List<string>() { "Apple", "Orange", "Peach" };
Additives = new List<string>() { "Vodka", "Rum", "Whiskey" };
Additives.Add("Separator");
Additives.Add("Separator"); // add as many separators as the number of ingredients - 1
Rules = new List<Func<string, IList<string>, bool>>() {
(ingredient1, ingredient2) => { return (ingredient1 != "Peach" && ingredient2.All(s => s != "Whiskey")); }
,
(ingredient1, ingredient2) => { return ingredient2.Count > 0; }
};
var additivesOrganisationMatchingAllTheRules = FindMatch();
}
private static IList<IList<string>> FindMatch()
{
// separators will create the sets
foreach (var additivesPermutation in Additives.GetCombinations())
{
var Sets = Split(additivesPermutation);
var thisSituationIsOk = true;
for (int i = 0; i < Sets.Count && thisSituationIsOk ; i++)
{
thisSituationIsOk = thisSituationIsOk && Rules.All(r => r(Ingredients[i], Sets[i]));
}
if (thisSituationIsOk) return Sets;
}
return null;
}
private static IList<IList<string>> Split(IList<string> values)
{
var splitValues = new List<IList<String>>();
var currentList = new List<string>();
foreach (var value in values)
{
if (value == "Separator")
{
splitValues.Add(currentList);
currentList = new List<string>();
}
else
{
currentList.Add(value);
}
}
splitValues.Add(currentList);
return splitValues;
}
Это работа для программирования ограничений! –
Так как это не звучит, например. «Яблоко» можно использовать только один раз, и у вас, вероятно, нет мести по каждому из них, я бы просто сделал что-то вроде: for (int fruit = 0; fruit
@presiuslitelsnoflek Я не знаком с программированием ограничений. Я быстро посмотрел на Google, и это кажется интересным, но я нашел очень мало практических примеров. Как бы вы решили решить небольшую проблему выше, используя программирование ограничений? – Sylverdrag