如何在Python中检查两个列表是否循环相同

如何在Python中检查两个列表是否循环相同,python,algorithm,Python,Algorithm,例如,我有以下列表: a[0] = [1, 1, 1, 0, 0] a[1] = [1, 1, 0, 0, 1] a[2] = [0, 1, 1, 1, 0] # and so on 它们似乎不同,但如果假设起点和终点是相连的,那么它们在循环中是相同的 问题是,我的每个列表的长度都是55,其中只有3个1和52个0。如果没有循环条件,则有26235(55选择3)个列表。但是,如果条件“循环”存在,则存在大量循环相同的列表 目前,我通过以下方式循环检查身份: def is_dup(a, b):

例如,我有以下列表:

a[0] = [1, 1, 1, 0, 0]
a[1] = [1, 1, 0, 0, 1]
a[2] = [0, 1, 1, 1, 0]
# and so on
它们似乎不同,但如果假设起点和终点是相连的,那么它们在循环中是相同的

问题是,我的每个列表的长度都是55,其中只有3个1和52个0。如果没有循环条件,则有26235(55选择3)个列表。但是,如果条件“循环”存在,则存在大量循环相同的列表

目前,我通过以下方式循环检查身份:

def is_dup(a, b):
    for i in range(len(a)):
        if a == list(numpy.roll(b, i)): # shift b circularly by i
            return True
    return False
在最坏的情况下,该功能需要55次循环换档操作。有26235个列表需要相互比较。简而言之,我需要55*26235*(26235-1)/2=18926847225计算。大约20千兆安


有什么好方法可以用更少的计算量来完成吗?或者任何支持循环的数据类型?

首先,根据列表的长度,这可以在
O(n)
中完成 您可以注意到,如果您将列表复制两次(
[1,2,3]
)将
[1,2,3,1,2,3]
,那么您的新列表肯定会包含所有可能的循环列表

因此,您只需检查您正在搜索的列表是否位于起始列表的2倍以内。在python中,可以通过以下方式实现这一点(假设长度相同)

关于我的oneliner的一些解释:
list*2
将把一个列表与自身结合起来,
map(str[1,2])
将所有数字转换为字符串和
'.join()
将数组
['1','2','111']
转换为字符串
'12 111'

正如一些人在评论中指出的那样,oneliner可能会给出一些误报,从而涵盖所有可能的边缘情况:

def isCircular(arr1, arr2):
    if len(arr1) != len(arr2):
        return False

    str1 = ' '.join(map(str, arr1))
    str2 = ' '.join(map(str, arr2))
    if len(str1) != len(str2):
        return False

    return str1 in str2 + ' ' + str2
p.S.1在谈到时间复杂性时,值得注意的是,如果在
O(n)
时间中可以找到子字符串,则将实现
O(n)
。这并不总是如此,这取决于您的语言的实现(例如time KMP)

p.S.2对于害怕字符串操作的人,由于这一事实,他们认为答案不好。重要的是复杂性和速度。该算法可能在
O(n)
时间和
O(n)
空间中运行,这使得它比
O(n^2)
域中的任何算法都要好。要亲自查看这一点,您可以运行一个小基准测试(创建一个随机列表,弹出第一个元素并将其附加到末尾,从而创建一个循环列表。您可以自由地进行自己的操作)


在我的机器上0.3秒。不太长。现在尝试将其与
O(n^2)
解决方案进行比较。在比较时,您可以从美国旅行到澳大利亚(最有可能乘坐游轮)

跟进萨尔瓦多·达利非常聪明的解决方案,处理它的最佳方法是确保所有元素的长度相同,以及两个列表的长度相同

def is_circular_equal(lst1, lst2):
    if len(lst1) != len(lst2):
        return False
    lst1, lst2 = map(str, lst1), map(str, lst2)
    len_longest_element = max(map(len, lst1))
    template = "{{:{}}}".format(len_longest_element)
    circ_lst = " ".join([template.format(el) for el in lst1]) * 2
    return " ".join([template.format(el) for el in lst2]) in circ_lst
不知道这比萨尔瓦多·达利的答案中AshwiniChaudhary推荐的正则表达式解决方案快还是慢,答案如下:

import re

def is_circular_equal(lst1, lst2):
    if len(lst2) != len(lst2):
        return False
    return bool(re.search(r"\b{}\b".format(' '.join(map(str, lst2))),
                          ' '.join(map(str, lst1)) * 2))

借助@Salvadodali关于在b+b中的任意a长度切片中查找a的匹配项的观察,这里有一个仅使用列表操作的解决方案

def rollmatch(a,b):
    bb=b*2
    return any(not any(ax^bbx for ax,bbx in zip(a,bb[i:])) for i in range(len(a)))

l1 = [1,0,0,1]
l2 = [1,1,0,0]
l3 = [1,0,1,0]

rollmatch(l1,l2)  # True
rollmatch(l1,l3)  # False

第二种方法:[已删除]

对Python了解不够,无法用您要求的语言回答这个问题,但在C/C++中,根据您问题的参数,我会将0和1转换为位,并将它们推到uint64\t的最低有效位上。这将允许您一下子比较所有55位-1个时钟

速度非常快,整个过程都可以放入芯片缓存(209880字节)。硬件支持将所有55个列表成员同时右移,仅在CPU的寄存器中可用。同时比较所有55个成员国也是如此。这允许将问题一对一映射到软件解决方案。(并使用SIMD/SSE 256位寄存器,如果需要,最多256个成员)因此,代码对读者来说是显而易见的

您可能能够在Python中实现这一点,我只是不太了解它,不知道这是否可能,或者性能可能如何

睡了一觉之后,一些事情变得显而易见,一切都好起来了

1.)使用位旋转循环链表非常容易,因此不需要Dali非常聪明的技巧。在64位寄存器内,标准位移位将非常简单地完成旋转,并试图通过使用算术而不是位运算,使其更加友好

2.)使用除以2可轻松完成位移位

3.)通过模2可以轻松地检查列表末尾的0或1

4.)将0从尾部“移动”到列表的头部可以通过除以2来完成。这是因为如果真的移动了零,它会使第55位为假,因为它已经完全不做任何事情了

5.)将1从尾部“移动”到列表的头部可以通过除以2并加上18014398509481984来完成-这是通过将第55位标记为真而将其余所有标记为假而创建的值

6.)如果在任何给定旋转后,锚定和组合uint64_t的比较为真,则中断并返回真

我会将整个列表数组转换为一个uint64数组,以避免重复转换

在花了几个小时试图优化代码、学习汇编语言之后,我能够在运行时节省20%。我应该补充的是,O/S和MSVC编译器昨天中午也得到了更新。无论出于何种原因,C编译器生成的代码质量在更新(2014年11月15日)后有了显著提高。运行时间现在为~70个时钟,17纳秒,用于合成并比较锚环与测试环的所有55圈,以及所有环的NxN与所有环的NxN
import re

def is_circular_equal(lst1, lst2):
    if len(lst2) != len(lst2):
        return False
    return bool(re.search(r"\b{}\b".format(' '.join(map(str, lst2))),
                          ' '.join(map(str, lst1)) * 2))
def rollmatch(a,b):
    bb=b*2
    return any(not any(ax^bbx for ax,bbx in zip(a,bb[i:])) for i in range(len(a)))

l1 = [1,0,0,1]
l2 = [1,1,0,0]
l3 = [1,0,1,0]

rollmatch(l1,l2)  # True
rollmatch(l1,l3)  # False
def makereps():
    reps = []
    for i in range(1, 55 - 1):
        for j in range(i + 1, 55):
            if (i, j) <= min((j - i, 55 - i), (55 - j, 55 + i - j)):
                reps.append('1' + '0' * (i - 1) + '1' + '0' * (j - i - 1) + '1' + '0' * (55 - j - 1))
    return reps
def normalise(lst):
    # Pick the 'maximum' out of all cyclic options
    return max([lst[i:]+lst[:i] for i in range(len(lst))])

a_normalised = map(normalise,a)
a_tuples = map(tuple,a_normalised)
a_unique = set(a_tuples)
list1, list2 = [0,1,1,1,0,0,1,0], [1,0,0,1,0,0,1,1]

str_list1="".join(map(str,list1))
str_list2="".join(map(str,list2))

def rotate(string_to_rotate, result=[]):
    result.append(string_to_rotate)
    for i in xrange(1,len(string_to_rotate)):
        result.append(result[-1][1:]+result[-1][0])
    return result

for x in rotate(str_list1):
    if cmp(x,str_list2)==0:
        print "lists are rotationally identical"
        break
List 1   1 1 1 0 1 0

List 2   1 0 1 1 1 0  1st permutation
         1 1 1 0 1 0  2nd permutation, final permutation, match, done
    public class KmpModified
    {
        public int[] CalculatePhi(int[] pattern)
        {
            var phi = new int[pattern.Length + 1];
            phi[0] = -1;
            phi[1] = 0;

            int pos = 1, cnd = 0;
            while (pos < pattern.Length)
                if (pattern[pos] == pattern[cnd])
                {
                    cnd++;
                    phi[pos + 1] = cnd;
                    pos++;
                }
                else if (cnd > 0)
                    cnd = phi[cnd];
                else
                {
                    phi[pos + 1] = 0;
                    pos++;
                }

            return phi;
        }

        public IEnumerable<int> Search(int[] pattern, int[] list)
        {
            var phi = CalculatePhi(pattern);

            int m = 0, i = 0;
            while (m < list.Length)
                if (pattern[i] == list[m])
                {
                    i++;
                    if (i == pattern.Length)
                    {
                        yield return m - i + 1;
                        i = phi[i];
                    }
                    m++;
                }
                else if (i > 0)
                {
                    i = phi[i];
                }
                else
                {
                    i = 0;
                    m++;
                }
        }

        [Fact]
        public void BasicTest()
        {
            var pattern = new[] { 1, 1, 10 };
            var list = new[] {2, 4, 1, 1, 1, 10, 1, 5, 1, 1, 10, 9};
            var matches = Search(pattern, list).ToList();

            Assert.Equal(new[] {3, 8}, matches);
        }

        [Fact]
        public void SolveProblem()
        {
            var random = new Random();
            var list = new int[10];
            for (var k = 0; k < list.Length; k++)
                list[k]= random.Next();

            var rotation = new int[list.Length];
            for (var k = 1; k < list.Length; k++)
                rotation[k - 1] = list[k];
            rotation[rotation.Length - 1] = list[0];

            Assert.True(Search(list, rotation.Concat(rotation).ToArray()).Any());
        }
    }
from sets import Set
a = Set ([1, 1, 1, 0, 0])
b = Set ([0, 1, 1, 1, 0]) 
c = Set ([1, 0, 0, 1, 1])
a==b
True
a==b==c
True
A = [ 1, 1, 1, 0, 0, 1, 1, 0 ]
B = [ 1, 1, 0, 1, 1, 1, 0, 0 ]
~
A = [ +3, -2, +2, -1 ]
B = [ +2, -1, +3, -2 ]
FUNCTION IS_DUPLICATE (LIST L1, LIST L2) : BOOLEAN

    LIST A = MAP_LIST(L1)
    LIST B = MAP_LIST(L2)

    LIST ALPHA = LOOKUP_INDEX(B, A[0])

    IF A.SIZE != B.SIZE
       OR COUNT_CHAR(A, 0) != COUNT_CHAR(B, ALPHA[0]) THEN

        RETURN FALSE

    END IF

    FOR EACH INDEX IN ALPHA

        IF ALPHA_NGRAM(A, B, INDEX, 1) THEN

            IF IS_DUPLICATE(A, B, INDEX) THEN

                RETURN TRUE

            END IF

        END IF

    END FOR

    RETURN FALSE

END FUNCTION
FUNCTION IS_DUPLICATE (LIST L1, LIST L2, INTEGER INDEX) : BOOLEAN

    INTEGER I = 0

    WHILE I < L1.SIZE DO

        IF L1[I] != L2[(INDEX+I)%L2.SIZE] THEN

            RETURN FALSE

        END IF

        I = I + 1

    END WHILE

    RETURN TRUE

END FUNCTION
public class Main {

    public static void main(String[] args){
        int[] a = {0,1,1,1,0};
        int[] b = {1,1,0,0,1};

        System.out.println(isCircularIdentical(a, b));
    }

    public static boolean isCircularIdentical(int[] a, int[]b){
        if(a.length != b.length){
            return false;
        }

        //The outer loop is for the increase of the index of the second list
        outer:
        for(int i = 0; i < a.length; i++){
            //Loop trough the list and compare each value to the according value of the second list
            for(int k = 0; k < a.length; k++){
                // I use modulo length to wrap around the index
                if(a[k] != b[(k + i) % a.length]){
                    //If the values do not match I continue and shift the index one further
                    continue outer;
                }
            }
            return true;
        }
        return false;
    }
}
def normalize_rotation_index(ls, v_min_other=None):
    """ Return the index or -1 (when the minimum is above `v_min_other`) """

    if len(ls) <= 1:
        return 0

    def compare_rotations(i_a, i_b):
        """ Return True when i_a is smaller.
            Note: unless there are large duplicate sections of identical values,
            this loop will exit early on.
        """
        for offset in range(1, len(ls)):
            v_a = ls[(i_a + offset) % len(ls)]
            v_b = ls[(i_b + offset) % len(ls)]
            if v_a < v_b:
                return True
            elif v_a > v_b:
                return False
        return False

    v_min = ls[0]
    i_best_first = 0
    i_best_last = 0
    i_best_total = 1
    for i in range(1, len(ls)):
        v = ls[i]
        if v_min > v:
            v_min = v
            i_best_first = i
            i_best_last = i
            i_best_total = 1
        elif v_min == v:
            i_best_last = i
            i_best_total += 1

    # all values match
    if i_best_total == len(ls):
        return 0

    # exit early if we're not matching another lists minimum
    if v_min_other is not None:
        if v_min != v_min_other:
            return -1
    # simple case, only one minimum
    if i_best_first == i_best_last:
        return i_best_first

    # otherwise find the minimum with the lowest values compared to all others.
    # start looking after the first we've found
    i_best = i_best_first
    for i in range(i_best_first + 1, i_best_last + 1):
        if (ls[i] == v_min) and (ls[i - 1] != v_min):
            if compare_rotations(i, i_best):
                i_best = i

    return i_best


def compare_circular_lists(ls_a, ls_b):
    # sanity checks
    if len(ls_a) != len(ls_b):
        return False
    if len(ls_a) <= 1:
        return (ls_a == ls_b)

    index_a = normalize_rotation_index(ls_a)
    index_b = normalize_rotation_index(ls_b, ls_a[index_a])

    if index_b == -1:
        return False

    if index_a == index_b:
        return (ls_a == ls_b)

    # cancel out 'index_a'
    index_b = (index_b - index_a)
    if index_b < 0:
        index_b += len(ls_a)
    index_a = 0  # ignore it

    # compare rotated lists
    for i in range(len(ls_a)):
        if ls_a[i] != ls_b[(index_b + i) % len(ls_b)]:
            return False
    return True


assert(compare_circular_lists([0, 9, -1, 2, -1], [-1, 2, -1, 0, 9]) == True)
assert(compare_circular_lists([2, 9, -1, 0, -1], [-1, 2, -1, 0, 9]) == False)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["World", "Hello" "Circular"]) == True)
assert(compare_circular_lists(["Hello" "Circular", "World"], ["Circular", "Hello" "World"]) == False)
Hb=0;
for (i=0; i<N ; i++)
{
    Hb = Hb*P + B[i];
}
Hb=0;
for (i=0; i<N ; i++)
{
    Hb += B[i] * P^(N-1-i);  //^ is exponentiation, not XOR
}
Hb1 = Hb*P + B[0]*(1-(P^N))
Hb2 = Hb1*P + B[1]*(1-(P^N))