Это было бы верно в стандартной системе на 32 бита, но конечно, нет никаких гарантий, и Вы могли найти большую архитектуру, где это не верно. Например, распространенное заблуждение - то, что sizeof (интервал) на x86_64 был бы 8 (так как это - система на 64 бита, я предполагаю), который это не. На x86_64, sizeof (интервал) все еще 4, но sizeof (пусто*) равняется 8.
Пока это работает. Если и только если существует заказ, удовлетворяющий условиям, он его найдет. Пока не пробовал оптимизировать. Он работает в обратном порядке, отслеживая, какие элементы не могут быть добавлены предыдущей операцией.
Изменить: Я не могу отметить это как ответ, потому что я уже сильно пострадал от этого и у меня всего 17 операций ( ITransform
s). На сортировку сейчас уходит 15 секунд lol / fail.
Добавлено 23 августа: Посмотрите в следующем разделе кода, как я улучшил сортировку до чего-то, что действительно работает, еще раз.
Редактировать 8/25: Ситуация снова стала неприятной, когда я скрестил 25 элементов, но оказалось, что у меня была небольшая проблема с предварительной сортировкой, которая теперь исправлена.
private static ITransform[] OrderTransforms(ITransform[] source)
{
return OrderTransforms(
new List<ITransform>(source),
new Stack<ITransform>(),
new HashSet<OpCode>(source.SelectMany(transform => transform.RemovedOpCodes)),
source.Aggregate(InstructionSemantics.None, (preventedSemantics, transform) => preventedSemantics | transform.RemovedSemantics)
);
}
private static ITransform[] OrderTransforms(List<ITransform> source, Stack<ITransform> selected, HashSet<OpCode> preventAdd, InstructionSemantics preventSemantics)
{
if (source.Count == 0 && preventAdd.Count == 0)
return selected.ToArray();
for (int i = source.Count - 1; i >= 0; i--)
{
var transform = source[i];
if ((preventSemantics & transform.InsertedSemantics) != 0)
continue;
if (preventAdd.Intersect(transform.InsertedOpCodes).Any())
continue;
selected.Push(transform);
source.RemoveAt(i);
#if true
var result = OrderTransforms(source, selected, new HashSet<OpCode>(preventAdd.Except(transform.RemovedOpCodes).Union(transform.PreventOpCodes)), (preventSemantics & ~transform.RemovedSemantics) | transform.PreventSemantics);
#else // this is even slower:
OpCode[] toggle = preventAdd.Intersect(transform.RemovedOpCodes).Union(transform.PreventOpCodes.Except(preventAdd)).ToArray();
preventAdd.SymmetricExceptWith(toggle);
var result = OrderTransforms(source, selected, preventAdd, (preventSemantics & ~transform.RemovedSemantics) | transform.PreventSemantics);
preventAdd.SymmetricExceptWith(toggle);
#endif
if (result != null)
return result;
source.Insert(i, transform);
selected.Pop();
}
return null;
}
В попытке вернуть свою награду, Я сократил время сортировки с 15380 с до 173 мс, эвристически предварительно подготовив массив, за которым сразу следует маршрутизация сортировки, описанная выше.
private static ITransform[] PreSortTransforms(ITransform[] source)
{
// maps an opcode to the set of transforms that remove it
ILookup<OpCode, ITransform> removals =
source
.SelectMany(transform => transform.RemovedOpCodes.Select(opcode => new
{
OpCode = opcode,
Transform = transform
}))
.ToLookup(item => item.OpCode, item => item.Transform);
// maps an opcode to the set of transforms that add it
ILookup<OpCode, ITransform> additions =
source
.SelectMany(transform => transform.InsertedOpCodes.Select(opcode => new
{
OpCode = opcode,
Transform = transform
}))
.ToLookup(item => item.OpCode, item => item.Transform);
// maps a set of items (A) to a set of items (B), where ALL elements of B must come before SOME element of A
ILookup<IEnumerable<ITransform>, ITransform> weakForwardDependencies =
removals
.SelectMany(item => additions[item.Key].Select(dependency => new
{
Transform = item,
Dependency = dependency
}))
.ToLookup(item => item.Transform.AsEnumerable(), item => item.Dependency);
/* For items in the previous map where set A only had one element, "somewhat" order the
* elements of set B before it. The order doesn't [necessarily] hold when a key from one
* relationship is a member of the values of another relationship.
*/
var ordered =
weakForwardDependencies
.Where(dependencyMap => dependencyMap.Key.SingleOrDefault() != null)
.SelectMany(dependencyMap => dependencyMap.AsEnumerable());
// Add the remaining transforms from the original array before the semi-sorted ones.
ITransform[] semiSorted = source.Except(ordered).Union(ordered).ToArray();
return semiSorted;
}
Поскольку то, может ли элемент X появиться следующим в списке, зависит не только от последнего элемента в списке, но и от предыдущих элементов, вы правы, говоря, что топологическая сортировка слишком сильный. Это более общая проблема поиска, поэтому я бы попробовал более общее решение: либо поиск с возвратом, либо динамическое программирование. Первое всегда можно сделать, но иногда это происходит невероятно медленно;
Я думаю, вы здесь говорите об алгоритме упорядочения, а не об алгоритме сортировки. То есть вы хотите найти упорядочение, имеющее перечисленные свойства.
Я был бы удивлен, если бы уже существовал конкретный алгоритм упорядочивания, который удовлетворял бы этим свойствам.
Имейте в виду, что вы не сможете найти упорядочение для заданного набора операций. На самом деле может даже не быть частичного упорядочивания / решетки. Тривиальный пример:
op1(adds(1),removes(2))
op2(adds(2),removes(1))
Звуки например топологическая сортировка , представьте каждую операцию как узел в ориентированном графе с ребрами, являющимися упомянутыми вами ограничениями.
Изменить: @ 280Z28 прокомментировал этот ответ:
Сейчас я использую топологическую сортировку, но технически она слишком сильна. Мне нужен какой-то способ иметь "слабые" группы ребер (одно или несколько ребер группы сохраняются в окончательном порядке)
I ' Я не уверен, что следую тому, что вы, ребята, относительно слабых групп ребер, если это относится к разрывным циклам, то топологическая сортировка может это сделать, как я сделал это, чтобы поддерживать в счетчике который показал, сколько непосещенных узлов указывают на этот узел. Затем для каждой итерации вы работаете с (одним из) узлов с минимальным в счетчике , если в счетчике не равен нулю, это означает, что существует цикл, и вы произвольно прерываете цикл в чтобы завершить сортировку.