Проверьте два массива на наличие циклических перестановок в C#

#c#

#c#

Вопрос:

Я хотел бы знать, как эффективно проверить, является ли массив int циклической перестановкой другого (или равен) в C #.

Например, следующие массивы являются циклическими перестановками: {1,2,3} , {2,3,1} и {3,1,2} , тогда как {2,1,3} это не так. Есть идеи?

Комментарии:

1. Могут ли числа дублироваться? То есть, что, если у нас есть {1, 1, 2} и {1, 2, 1} , совпадает ли это?

2. Я отмечаю, что обычно это называется циклической перестановкой , а не циклической перестановкой .

3. Вы запрашиваете эффективное решение, не говоря, что вы подразумеваете под «эффективным». Асимптотически эффективен? Возникает нехватка памяти? Быстро ли отбрасываются очевидные плохие совпадения? Каков ваш показатель эффективности?

4. Вы говорите, что у вас есть метод грубой силы, но вы не говорите, что это такое. Помогите нам помочь вам . Нам нет смысла повторять работу, которую вы уже выполнили и отвергли. Задайте более конкретный вопрос .

5. Существует две версии этой проблемы. Общая проблема заключается в том, что у вас есть множество пар массивов (a, b), (c, d), (e, f) и так далее, и вы хотите увидеть, какие пары обладают свойством, что (x, y) имеет x циклическую перестановку y или нет. Более конкретная проблема заключается в следующем: у вас есть определенный массив «a», и у вас есть целая куча массивов b, c, d, e, f, g, … и вы хотите знать, какие из b, c, d, e, f, g являются циклическими перестановками «a». Это не та же проблема! Могут существовать методы предварительной обработки «a», чтобы решение было более эффективным для каждого возможного случая.

Ответ №1:

Следующий метод должен обеспечить желаемое поведение:

 bool IsCyclicPermutation(int[] a, int[] b) {
    if (a == null || b == null || a.Length != b.Length) return false;

    for (int i = 0; i < a.Length; i  )
    {
        if (a[i] == b[0]) // Potential starting point of cycle found
        {
            bool isCyclic = true;
            for (int j = 1; j < b.Length amp;amp; isCyclic; j  )
            {
                if (a[(j   i) % a.Length] != b[j]) isCyclic = false;
            }
            if (isCyclic) return true; // Cycle found
        }
    }

    return false; // No cycles found
}
  

Редактировать
Если это тот случай, когда повторяющиеся числа отсутствуют, вы можете использовать модифицированный код, как показано ниже, для немного лучшей производительности:

 bool IsCyclicPermutation(int[] a, int[] b) {
    if (a == null || b == null || a.Length != b.Length) return false;

    for (int i = 0; i < a.Length; i  )
    {
        if (a[i] == b[0]) // Starting point of potential cycle found
        {
            bool isCyclic = true;
            for (int j = 1; j < b.Length amp;amp; isCyclic; j  )
            {
                if (a[(j   i) % a.Length] != b[j]) isCyclic = false;
            }
            return isCyclic;
        }
    }

    return false; // No cycles found
}
  

Были выполнены следующие тесты:

 var arr1 = new int[] {1, 2, 3};
var arr2 = new int[] {2, 3, 1};
var arr3 = new int[] {3, 1, 2};
var arr4 = new int[] {2, 1, 3};

IsCyclicPermutation(arr1, arr1); // True
IsCyclicPermutation(arr1, arr2); // True
IsCyclicPermutation(arr1, arr3); // True
IsCyclicPermutation(arr2, arr1); // True
IsCyclicPermutation(arr2, arr2); // True
IsCyclicPermutation(arr2, arr3); // True
IsCyclicPermutation(arr3, arr1); // True
IsCyclicPermutation(arr3, arr2); // True
IsCyclicPermutation(arr3, arr3); // True
IsCyclicPermutation(arr4, arr1); // False
IsCyclicPermutation(arr4, arr2); // False
IsCyclicPermutation(arr4, arr3); // False
  

Что касается производительности, трудно сказать, не имея с чем ее сравнить, хотя я верю, что это O (n), где n — размер входных массивов.

Комментарии:

1. Спасибо, это было именно то, что мне было нужно

2. @Couroux взгляните на мою правку, чтобы найти улучшенную функцию, которая будет использоваться, когда массивы не содержат повторяющихся значений.

Ответ №2:

 int[] array1 = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
int[] array2 = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
        int tmp = array1[0];
        int index = Array.IndexOf(array2, tmp);
        int n = array1.Length;

        for (int i = 1; i < n; i  )
        {
            if (index   i < array1.Length)
            {
                if (array1[i] != array2[index i])
                {
                    Console.WriteLine("Arrays are not equal");
                    break;

                }
            }
            else
            {
                if (array1[i] != array2[index   i - n])
                {
                    Console.WriteLine("Arrays are not equal");
                    break;
                }
            }

        }
  

Я предполагаю, что длина массивов и их элементов одинакова. Приведенный выше код выполняет работу в O (n). Дважды проверьте индекс!

Ответ №3:

Конечно:

Рабочий пример DotNetFiddle

 using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var test1 = new [] {1, 2, 3};
        var test2 = new [] {2, 3, 1};
        var test3 = new [] {3, 1, 2};
        var test4 = new [] {2, 1, 3};

        test1.IsCiruclarPermutation(test2);
        test1.IsCiruclarPermutation(test3);
        test2.IsCiruclarPermutation(test1);
        test2.IsCiruclarPermutation(test3);
        test3.IsCiruclarPermutation(test1);
        test3.IsCiruclarPermutation(test2);

        test4.IsCiruclarPermutation(test1);
        test4.IsCiruclarPermutation(test2);
        test4.IsCiruclarPermutation(test3);
    }
}

public static class ArrayExtensions
{
    public static bool IsCiruclarPermutation(this int[] source, int[] dest)
    {
        Console.Write(string.Join(",", source)   " is a Ciruclar Permutation of ");
        Console.Write(string.Join(",", dest));

        var left = source.ToList();
        var right = dest.ToList();
        right.AddRange(dest);

        var result = false;

        for (int idx = 0; idx < left.Count; idx  )      
        {
            var compareTo = right.Skip(idx).Take(left.Count);
            result = Enumerable.SequenceEqual(left, compareTo);
            if (result) break;
        }

        Console.WriteLine("  "   result.ToString());
        return resu<
    }
}
  

Вывод:

верно ли, что 1,2,3 — это циклическая перестановка 2,3,1

верно ли 1,2,3 — это циклическая перестановка 3,1,2

2,3,1 — верна ли циклическая перестановка 1,2,3

2,3,1 — верна ли циклическая перестановка 3,1,2

3,1,2 — верна ли циклическая перестановка 1,2,3

3,1,2 — верна ли циклическая перестановка 2,3,1

2,1,3 — это циклическая перестановка 1,2,3 False

2,1,3 — это циклическая перестановка 2,3,1 False

2,1,3 — это циклическая перестановка 3,1,2 False

Комментарии:

1. Вы говорите, что предстоит проделать работу по сравнению массивов разной длины. Можете ли вы привести пример двух массивов разной длины, где один является перестановкой другого?

2. Я отмечаю, что ни в одном из ваших тестовых примеров test1 не рассматривается как перестановка test1.

Ответ №4:

Если у вас сложная проблема, преобразуйте ее в simple problem, в следующем коде используйте linkedlist для решения проблемы, это не лучший вариант для производительности, но простой для понимания.

 public class SameTest<T>
{
    public bool TestCircularArray(IEnumerable<T> array1, IEnumerable<T> array2)
    {
        if (array1 == null)
            throw new ArgumentNullException("array1");
        if (array2 == null)
            throw new ArgumentNullException("array2");

        // if they are the same, the length must be the same
        if (array1.Count() != array2.Count())
            return false;

        // two empty array assume to same
        if (!array1.Any())
            return true;

        // convert array2 to linkedlist
        var linkedList = new LinkedList<T>(array2);

        LinkedListNode<T> node = null;

        foreach (T item1 in array1)
        {
            // first find the element in link
            if (node == null)
            {
                node = linkedList.Find(item1);
                if (node == null)
                    return false;
                continue;
            }

            node = node.Next;
            // reach tail move to head
            if (node == null)
                node = linkedList.First;

            if (!item1.Equals(node.Value))
                return false;
        }


        return true;
    }
}

[TestMethod]
public void TestMethod1()
{
    int[] array1 = {2, 3, 1};
    int[] array2 = {1, 2, 3};
    int[] array3 = {2, 1, 3};

    var tester = new SameTest<int>();

    var result = tester.TestCircularArray(array1, array2);

    Assert.AreEqual(true, result);

    result = tester.TestCircularArray(array2, array3);

    Assert.AreEqual(false, result);
}