Find round objects in the collection (intransitive objects)

Problem:

I have a simple List<T>one and I'm trying to sort it. But the items in the list are not all transitive in terms of comparability, that is, for, for example, my List<T>looks like this:

A
B
C
D
E

where A> B and B> C, but C> A. It is also possible to have circular greatness similar to A> B, B> C, C> D, but D> A, i.e. not necessarily always a group of 3 . I want to find all groups of circular quantities in a givenList<T> . For example, if A> B> C> A and A> B> C> D> A are two circular groups in this case, my output should look like this:

List<List<T>> circulars = [[A, B, C, A], [A, B, C, D, A]]

or

List<List<T>> circulars = [[A, B, C], [A, B, C, D]]
// but in this case I do not want duplicates in the output. 
// For e.g., the output shouldn't have both [B, C, A] and [A, B, C]
// since both the groups refer to the same set of circular items A, B & C
// as B > C > A > B is also true. 
// But [B, A, C] is a different group (though nothing circular about it)

. (linquish) , , . , - .


:

, / , , , , . , , , . , " " , , . , , , , .

:

class Player : IComparable<Player>
{
    // logic
}

, :

  • 3. [ABC], [A, C, B]...., [A, B, C, D], [ A, B, D, C].... .. ( )

  • . , , A > B > C > D ( , )

  • , , , [A, B, C] [B, C, A] ..

:

var players = [.....]; //all the players in the collection

// first generate all the permutations possible in the list from size 3 
// to players.Count
var circulars = Enumerable.Range(3, players.Count - 3 + 1)
               .Select(x => players.Permutations(x))
               .SelectMany(x => x)
               .Select(x => x.ToList())

// then check in the each sublists if a pattern like A > B > C > A is 
// generated                                                                          vv    this is the player comparison
               .Where(l => l.Zip(l.Skip(1), (p1, p2) => new { p1, p2 }).All(x => x.p1 > x.p2) && l.First() < l.Last())

// then remove the duplicate lists using special comparer
               .Distinct(new CircularComparer<Player>())
               .ToList();

public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> list, int length)
{
    if (length == 1) 
        return list.Select(t => new[] { t });

    return Permutations(list, length - 1)  
          .SelectMany(t => list.Where(e => !t.Contains(e)), (t1, t2) => t1.Concat(new[] { t2 }));
}

class CircularComparer<T> : IEqualityComparer<ICollection<T>>
{
    public bool Equals(ICollection<T> x, ICollection<T> y)
    {
        if (x.Count != y.Count)
            return false;

        return Enumerable.Range(1, x.Count)
              .Any(i => x.SequenceEqual(y.Skip(i).Concat(y.Take(i))));
    }

    public int GetHashCode(ICollection<T> obj)
    {
        return 0;
    }
}

, . 10 , , ( 1 ). , ? . ? .

+4
3

, . , , , . , ( ), . .

:

  • ICyclic<T>, Player.

  • ( Prepare).

  • (.. ) (.. ), , , , . Prepare .

  • , , . . . , .

  • .

  • HashSet<T>, . .

:

public interface ICyclic<T> : IComparable<T>
{
    ISet<T> Worse { get; set; }
    ISet<T> Better { get; set; }
}

public static ISet<IList<T>> Cycles<T>(this ISet<T> input) where T : ICyclic<T>
{
    input = input.ToHashSet();
    Prepare(input);

    var output = new HashSet<IList<T>>(new CircleEqualityComparer<T>());
    foreach (var item in input)
    {
        bool detected;
        Visit(item, new List<T> { item }, item.Worse, output, out detected);
    }

    return output;
}

static void Prepare<T>(ISet<T> input) where T : ICyclic<T>
{
    foreach (var item in input)
    {
        item.Worse = input.Where(t => t.CompareTo(item) < 0).ToHashSet();
        item.Better = input.Where(t => t.CompareTo(item) > 0).ToHashSet();
    }

    Action<Func<T, ISet<T>>> exceptionsRemover = x =>
    {
        var exceptions = new HashSet<T>();
        foreach (var item in input.OrderBy(t => x(t).Count))
        {
            x(item).ExceptWith(exceptions);
            if (!x(item).Any())
                exceptions.Add(item);
        }

        input.ExceptWith(exceptions);
    };
    exceptionsRemover(t => t.Worse);
    exceptionsRemover(t => t.Better);
}

static void Visit<T>(T item, List<T> visited, ISet<T> worse, ISet<IList<T>> output, 
                     out bool detected) where T : ICyclic<T>
{
    detected = false;

    foreach (var bad in worse)
    {
        Func<T, T, bool> comparer = (t1, t2) => t1.CompareTo(t2) > 0;

        if (comparer(visited.Last(), visited.First()))
        {
            detected = true;
            var cycle = visited.ToList();
            output.Add(cycle);
        }

        if (visited.Contains(bad))
        {
            var cycle = visited.SkipWhile(x => !x.Equals(bad)).ToList();
            if (cycle.Count >= 3)
            {
                detected = true;
                output.Add(cycle);
            }
            continue;
        }

        if (bad.Equals(item) || comparer(bad, visited.Last()))
            continue;

        visited.Add(bad);

        Visit(item, visited, bad.Worse, output, out detected);
        if (detected)
            visited.Remove(bad);
    }
}

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

public class CircleEqualityComparer<T> : IEqualityComparer<ICollection<T>>
{
    public bool Equals(ICollection<T> x, ICollection<T> y)
    {
        if (x.Count != y.Count)
            return false;

        return Enumerable.Range(1, x.Count)
              .Any(i => x.SequenceEqual(y.Skip(i).Concat(y.Take(i))));
    }

    public int GetHashCode(ICollection<T> obj)
    {
        return unchecked(obj.Aggregate(0, (x, y) => x + y.GetHashCode()));
    }
}

( OP)

. , , ICyclic<T>, IComparable<T> . , .

public static IEnumerable<ICollection<T>> Cycles<T>(this ISet<T> input) where T : IComparable<T>
{
    if (input.Count < 3)
        return Enumerable.Empty<ICollection<T>>();

    Func<T, T, bool> comparer = (t1, t2) => t1.CompareTo(t2) > 0;

    return Enumerable.Range(3, input.Count - 3 + 1)
          .Select(x => input.Permutations(x))
          .SelectMany(x => x)
          .Select(x => x.ToList())
          .Where(l => l.Zip(l.Skip(1), (t1, t2) => new { t1, t2 }).All(x => comparer(x.t1, x.t2))
                   && comparer(l.Last(), l.First()))
          .Distinct(new CircleEqualityComparer<T>());
}

public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> list, int length)
{
    if (length == 1)
        return list.Select(t => new[] { t });

    return Permutations(list, length - 1)
          .SelectMany(t => list.Where(e => !t.Contains(e)), (t1, t2) => t1.Concat(new[] { t2 }));
}

public class CircleEqualityComparer<T> : IEqualityComparer<ICollection<T>>
{
    public bool Equals(ICollection<T> x, ICollection<T> y)
    {
        if (x.Count != y.Count)
            return false;

        return Enumerable.Range(1, x.Count)
              .Any(i => x.SequenceEqual(y.Skip(i).Concat(y.Take(i))));
    }

    public int GetHashCode(ICollection<T> obj)
    {
        return unchecked(obj.Aggregate(0, (x, y) => x + y.GetHashCode()));
    }
}

:

  • ISet<T> HashSet<T> List<T> s, , , . .

  • .NET (.. ), List<T>. , , , set list .

  • 100 .

  • , Prepare. , .. . .

  • , . , . , .. , . .

  • ( ) 3, .. , A > B > C > A. , A > B, B > A . , 3 , . .

0

...

[A, B, C, D, E]

A > B, B > C, C > D, C > A, D > A

... , , A -> B A > B:

Graph

, " ?"

, , Tarjan. .

+2

N , . , CUDOFY Traveling Salesman:

    /// <summary>Amended algorithm after SpaceRat (see Remarks): 
    /// Don't <b>Divide</b> when you can <b>Multiply</b>!</summary>
    /// <seealso cref="http://www.daniweb.com/software-development/cpp/code/274075/all-permutations-non-recursive"/> 
    /// <remarks>Final loop iteration unneeded, as element [0] only swaps with itself.</remarks>
  [Cudafy]
  public static float PathFromRoutePermutation(GThread thread, 
            long  permutation, int[,] path) {
     for (int city = 0; city < _cities; city++) { path[city, thread.threadIdx.x] = city; }

     var divisor = 1L;
     for (int city = _cities; city > 1L; /* decrement in loop body */) {
        var dest    = (int)((permutation / divisor) % city);
        divisor     *= city;

        city--;

        var swap                        = path[dest, thread.threadIdx.x];
        path[dest, thread.threadIdx.x]  = path[city, thread.threadIdx.x];
        path[city, thread.threadIdx.x]  = swap;
     }
     return 0;
    }
    #endregion
}

From this point on, it is easy to identify permutations with circular magnitude in parallel. First, you can use several cores on the processor to improve performance, and then available on the GPU. After reconfiguring the problem with Traveling Salesman in this way, I improved performance for the 11-city case from more than 14 seconds (using the processor only) to 0.25 seconds using my GPU; 50 times improvement.

Of course, your mileage will depend on other aspects of the problem, as well as your equipment.

+1
source

All Articles