是否有更快的算法来关闭所有灯?

时间:2017-10-31 13:37:42

标签: python algorithm numpy sage

我发现了一个编程问题网站https://www.ohjelmointiputka.net/postit/tehtava.php?tunnus=muslam,并且存在一个我无法解决的问题。对于普通笔记本电脑而言,什么样的算法能够以最小的移动速度解决以下问题:

我有一个圆形的n个灯,并由L_1,...,L_n枚举。其中一些已打开,其中一些已关闭。我也得到了一个正整数m。每转一圈,我选择一盏灯L_i然后灯L_ {i-m},...,L_ {i + m}将改变它们的状态,我的意思是如果灯L_j被关闭然后现在它被打开,反之亦然。索引是模n,因此灯L_ {n-1},L_ {n},L_1,L_2是连续的。

如果灯的初始状态(从L_1到L_n)如下表所示,那么关闭所有灯的最小转数是多少以及必须按哪些开关(列数是房间名称,数字)对于灯具来说,开关变化到一个方向的最近的灯数是多少,所以总共2n + 1个灯具开关有效,并且灯的初始状态为:

================================================================================
B       6               1               101101
--------------------------------------------------------------------------------
C       10              2               1011010110
--------------------------------------------------------------------------------
D       20              1               11111011101010111111
--------------------------------------------------------------------------------
E       30              7               011100001010011011100001010011
--------------------------------------------------------------------------------
F       39              6               110100111111101000011000100110111100010
--------------------------------------------------------------------------------
G       53              9               0101100101111100100011100111101001001010
                                        0010000010110
--------------------------------------------------------------------------------
H       120             7               1010110110000100000101011001011111010111
                                        1010011101001100000010001010011010110000
                                        0000100110010100010010110111000000010110
--------------------------------------------------------------------------------
I       220             27              1110111111101000100110011001100110100000
                                        0010100011000111101100111111000001010000
                                        1010110110011100100010011011010111100011
                                        0101101000010000100110111101001001011010
                                        1101001001110110001100011010111101001100
                                        11010111110101010100
--------------------------------------------------------------------------------
J       500             87              1010001101101001110001101001000101010100
                                        0001111111001101011000000011001111111011
                                        1001110011010111111011010100010011011001
                                        1001101110011011100001000111110101011111
                                        1100111100001100110011101110101100001111
                                        1100010010011010001111000000101110101101
                                        1010100001100011111000111001000101101000
                                        1011111111101111000000011111010001000000
                                        1110011110111101010010011000000100010100
                                        0011101011010011010110011110111000010010
                                        0111100100011010010110001000011100101001
                                        1110111010001001011001111011111011010110
                                        10101101111011101110
--------------------------------------------------------------------------------
K       1002            83              0010100100100101000000110101111111101011
                                        1101000101111110001110000110110110010101
                                        1110110011011101100110111001110110010011
                                        1101111010110011110101100001101010100011
                                        1110001100011111110100011110100111111100
                                        0011001011100110101100001101000001110010
                                        0110100000100100100000011010000010111100
                                        1110001110011110101001100111101101010000
                                        0101010000011010011110101001001001000000
                                        0011000100011011011001111010001101111000
                                        0100001011010011001010111001111100110001
                                        0011111110101101001100111101110000000000
                                        1101100100000011000010010100010101001000
                                        1100001000101001100110010100001000001101
                                        1101000100001010011000101001101000100010
                                        0011010001011101010100011101001101101100
                                        0111110100110011001111000000001001001001
                                        1001111001011111000010110000110010101000
                                        1011001100111101000101000110000111010100
                                        0010011011010111001101011001111000001011
                                        1110101010101101111011111110100001100110
                                        1000101100110011010000110000011011110011
                                        0010000010000000111101101000001111101111
                                        0100111110010101100011101001111101010000
                                        1111100010011001110111111000101000000101
                                        01
--------------------------------------------------------------------------------
L       2107            108             0111110100011000011111101110010101100011
                                        1001111011101001001110111110001100011001
                                        1001010100101011101101001000010111111111
                                        1001101010111011110100100101000101100011
                                        1110100010010010101110100000111100101000
                                        0111101011111100010010110000100110100100
                                        0100110101110010110011110010101101100111
                                        1110010011000110110111010110010100101110
                                        0111111101110000111001111100100010010001
                                        1010110011000101100111111001011110101110
                                        0111010110111110110101000101100100011000
                                        1011000011011110001111100110100010100101
                                        1101111100110011001110010010001010101111
                                        1000001001000110011110010011011101110100
                                        1011111100110010011000010110010110101010
                                        0110101000011011110001010000010001000110
                                        1001110101001001110110111111010011010111
                                        1111011001000110111001000011101101110001
                                        0000011111101000010101011111011011000011
                                        1111000000011100010011011001011000110101
                                        1101011111100001100010110010110011000000
                                        0001001111100101110100100011011010011100
                                        0000001111010101000111011000110110100001
                                        1010110011100110111010111110110000010000
                                        1000101001111001000110000101010000010111
                                        1011100001000110001100010000001011101110
                                        1001111110100010010000011000100101010101
                                        1001001001110110101000001001001100001011
                                        0011011100011111100111001110101101110001
                                        0111010000010011110110011011000011101001
                                        1111011010010000101111000010000001100110
                                        1001011101001000010101001001011111111011
                                        1000111000100001101100101110100011111100
                                        1011001111101111110110101111101111011111
                                        1001111100110101110101111110010010101101
                                        1111111111000100100111100011101110110100
                                        0100011011001010110100101101000000110010
                                        0010010001001110110100011111100011111101
                                        0100110111101101010101010100110110011011
                                        0001111111000100000111011010101011000010
                                        0011011110110110110100011001101111001000
                                        1000000011110011100111100000001010010011
                                        1000011101111100000101010101010010100101
                                        1010001011010100011011001110110010100000
                                        1000111101111000010111111101010110110111
                                        0110001111100011001110000100100101001111
                                        0000111111100010011001010000010110111000
                                        1000110110001000001100110000001011000010
                                        1000101101110000101100100010101111100011
                                        1000010010111101000010000110011010000001
                                        0010001100001000001100110111110100100111
                                        1001100110001000100101011111001011001111
                                        110001011111001101010101001
================================================================================

其中1表示相应的灯在开头打开,0表示相应的灯在开始时关闭。

我修改了https://ask.sagemath.org/question/39335/how-to-find-a-minimum-number-of-switch-presses-to-shut-down-all-lamps/中的代码。它根据场Z / 2Z上的问题做了矩阵方程。它使用了一些Sage自己的线性代数函数,如nonzero_positionshttp://doc.sagemath.org/html/en/reference/matrices/sage/matrix/matrix0.htmlright_kernelhttp://doc.sagemath.org/html/en/prep/Quickstarts/Linear-Algebra.htmlsolve_righthttp://doc.sagemath.org/html/en/tutorial/tour_linalg.html。 我能够解决一些案例,比如H:

input = '101011011000010000010101100101111101011110100111010011000000100010100110101100000000100110010100010010110111000000010110'
m = 7
d = len(input)
F = GF(2)

L_init = vector(F,input)
M = matrix(F,d,d)
for i in range(d):
 for j in range(-m,m+1):
  M[i,(i+j) % d] = 1
I = M.solve_right(L_init)
K = M.right_kernel();
m = min([len((I+k).nonzero_positions()) for k in K])
print (m)

一段时间后输出46但有些情况需要花费太多时间,比如

input ='1110111111101000100110011001100110100000001010001100011110110011111100000101000010101101100111001000100110110101111000110101101000010000100110111101001001011010110100100111011000110001101011110100110011010111110101010100'
m = 27
d = len(input)
F = GF(2)
L_init = vector(F,input)
M = matrix(F,d,d)
for i in range(d):
 for j in range(-m,m+1):
  M[i,(i+j) % d] = 1
I = M.solve_right(L_init)
K = M.right_kernel();
m = min([len((I+k).nonzero_positions()) for k in K])
print (m)

那么如何才能找到一种更快的算法来以最少的开关次数关闭所有灯?

我试着编写自己的Gauss-Jordan实现:

def swap(m,i,j):
    for k in range(8):
        temp = m[i][k]
        m[i][k]=m[j][k]
        m[j][k]=temp
    return m

def addrow(m,i,j):
    for k in range(8):
        m[i][k] += m[j][k]
        m[i][k] %= 2
    return m

w = 8;
m = [[0 for x in range(w)] for y in range(w)] 

for i in range(w):
    for j in range(w):
        if max(i,j)-min(i,j) < 3:
            m[i][j] = 1
        else:
            m[i][j] = 0
print(m)
m = swap(m,0,1)
print(m)
m = addrow(m,2,3)
print(m)
for i in range(0,8):
    if m[i][i] == 0:
        j = i+1
    while j<8:
        if m[j][i] == 1:
            m = swap(m,j,i)
        if m[j][i] == 1:
            m = addrow(m,i,j)
        j += 1
print(m)

但看起来矩阵方程总是不会给出最佳的压力集https://www.quora.com/What-is-the-algorithm-for-solving-lights-out-puzzle-in-minimum-number-of-moves-in-Java。这可以通过将不同的灯位置视为图形节点并将开关按下作为顶点来解决,然后使用Thorup的算法(https://dl.acm.org/citation.cfm?doid=316542.316548)来找到最小的印刷机组吗?但是,我认为图形太大而无法安装到计算机内存中,因此我认为该方法需要某种方法将图形拆分为较小的部分,并多次使用该算法来找到最佳解决方案。

https://ask.sagemath.org/question/39335/how-to-find-a-minimum-number-of-switch-presses-to-shut-down-all-lamps/中有一个算法,但该算法无法在我的计算机上运行。

F = GF(2)

CASES = (
    ( 6, 1, '101101' ),
    ( 10, 2, '1011010110' ),
    ( 20, 1, '11111011101010111111' ),
    ( 30, 7, '011100001010011011100001010011' ),
    ( 39, 6, '110100111111101000011000100110111100010' ),
    ( 53, 9,
      '''01011001011111001000111001111010010010100010000010110''' ),
    ( 120, 7,
      '''10101101100001000001010110010111110101111010011101001100000010001010011010110000
      0000100110010100010010110111000000010110''' ),
    ( 220, 27,
      '''11101111111010001001100110011001101000000010100011000111101100111111000001010000
      10101101100111001000100110110101111000110101101000010000100110111101001001011010
      110100100111011000110001101011110100110011010111110101010100''' ),
    ( 500, 87,
      '''10100011011010011100011010010001010101000001111111001101011000000011001111111011
      10011100110101111110110101000100110110011001101110011011100001000111110101011111
      11001111000011001100111011101011000011111100010010011010001111000000101110101101
      10101000011000111110001110010001011010001011111111101111000000011111010001000000
      11100111101111010100100110000001000101000011101011010011010110011110111000010010
      01111001000110100101100010000111001010011110111010001001011001111011111011010110
      10101101111011101110''' ),
    ( 1002, 83,
      '''00101001001001010000001101011111111010111101000101111110001110000110110110010101
      11101100110111011001101110011101100100111101111010110011110101100001101010100011
      11100011000111111101000111101001111111000011001011100110101100001101000001110010
      01101000001001001000000110100000101111001110001110011110101001100111101101010000
      01010100000110100111101010010010010000000011000100011011011001111010001101111000
      01000010110100110010101110011111001100010011111110101101001100111101110000000000
      11011001000000110000100101000101010010001100001000101001100110010100001000001101
      11010001000010100110001010011010001000100011010001011101010100011101001101101100
      01111101001100110011110000000010010010011001111001011111000010110000110010101000
      10110011001111010001010001100001110101000010011011010111001101011001111000001011
      11101010101011011110111111101000011001101000101100110011010000110000011011110011
      00100000100000001111011010000011111011110100111110010101100011101001111101010000
      111110001001100111011111100010100000010101''' ),
    ( 2107, 108,
      """01111101000110000111111011100101011000111001111011101001001110111110001100011001
      10010101001010111011010010000101111111111001101010111011110100100101000101100011
      11101000100100101011101000001111001010000111101011111100010010110000100110100100
      01001101011100101100111100101011011001111110010011000110110111010110010100101110
      01111111011100001110011111001000100100011010110011000101100111111001011110101110
      01110101101111101101010001011001000110001011000011011110001111100110100010100101
      11011111001100110011100100100010101011111000001001000110011110010011011101110100
      10111111001100100110000101100101101010100110101000011011110001010000010001000110
      10011101010010011101101111110100110101111111011001000110111001000011101101110001
      00000111111010000101010111110110110000111111000000011100010011011001011000110101
      11010111111000011000101100101100110000000001001111100101110100100011011010011100
      00000011110101010001110110001101101000011010110011100110111010111110110000010000
      10001010011110010001100001010100000101111011100001000110001100010000001011101110
      10011111101000100100000110001001010101011001001001110110101000001001001100001011
      00110111000111111001110011101011011100010111010000010011110110011011000011101001
      11110110100100001011110000100000011001101001011101001000010101001001011111111011
      10001110001000011011001011101000111111001011001111101111110110101111101111011111
      10011111001101011101011111100100101011011111111111000100100111100011101110110100
      01000110110010101101001011010000001100100010010001001110110100011111100011111101
      01001101111011010101010101001101100110110001111111000100000111011010101011000010
      00110111101101101101000110011011110010001000000011110011100111100000001010010011
      10000111011111000001010101010100101001011010001011010100011011001110110010100000
      10001111011110000101111111010101101101110110001111100011001110000100100101001111
      00001111111000100110010100000101101110001000110110001000001100110000001011000010
      10001011011100001011001000101011111000111000010010111101000010000110011010000001
      00100011000010000011001101111101001001111001100110001000100101011111001011001111
      110001011111001101010101001""" )
)

def getA( N, M, delay=0 ):
    R = range(N)
    lists_for_M = [ [ (j+k+delay) % N for k in range(M) ]
                    for j in R ]
    return matrix( F, N, N, [ [ F(1) if k in lists_for_M[j]    else F(0)
                                for k in R ]
                              for j in R ] ).transpose()

def showVector( vec, separator='' ):
    return separator.join( [ str( entry ) for entry in vec ] )

def exchange( wblocks, wpattern, b, k1, k2 ):
    blocks, pattern = wblocks[:], wpattern[:]    # copy the information

    for block in blocks:
        block[k1] = F(1) - block[k1]
        block[k2] = F(1) - block[k2]

    pattern[k1] = b - pattern[k1]
    pattern[k2] = b - pattern[k2]

    return blocks, pattern

def blocksInformation( blocks ):
    print "Blocks:"
    for block in blocks:
        print showVector( block )
    print

# main search for all cases...
def searchLampSwitches( N, m, lamps
                        , showBlocks=False
                        , returnSolution=False ):
    """N, m, lamps is an entry in CASES..."""
    print N, m, lamps

    M = 2*m+1
    d = gcd( N, M )
    R = range( d )           # we will often loop using R
    b = ZZ( N/d )            # number of blocks
    bound = (b/2).floor()    # critical bound for the entries in pattern

    print "N=%s M=%s d=gcd(N,M)=%s b=N/d=%s bound=%s" % ( N, M, d, b, bound )

    A = getA( N, M, delay=-m )
    v = matrix( F, N, 1, [ F(int(s)) for s in lamps if s in '01' ] )
    w = vector( A.solve_right( v ) )

    if d == 1:
        # then w is already the (unique, thus optimal) solution
        print "UNIQUE SOLUTION"
        print sum( [ 1 for entry in w if entry ] )
        print showVector( w )

        return w if returnSolution else None

    # else we build blocks
    wblocks = [ [ w[ j + d*k ] for j in R ]
               for k in range(N/d) ]

    wpattern = [ sum( [ 1 for wblock in wblocks if wblock[k] ] )
                 for k in R ]
    print "Initial pattern:",
    print showVector( wpattern, separator = '' if b<10 else ',' )

    if showBlocks:
        blocksInformation( wblocks )

    wplaces = [ k for k in R if wpattern[k] > bound ]    # suboptimal places in wpattern
    wplaces . sort( lambda k1, k2:    cmp( -wpattern[k1], -wpattern[k2] ) )
    print "Suboptimal places:", wplaces

    while len( wplaces ) > 1:
        k1, k2  = wplaces[:2]
        wplaces = wplaces[2:]
        wblocks, wpattern = exchange( wblocks, wpattern, b, k1, k2 )
        print "New pattern:",
        print showVector( wpattern, separator = '' if b<10 else ',' )
        if showBlocks:
            blocksInformation( wblocks )

    # we count solutions, last changes can be done - but the   in case...
    if wplaces:
        k, = wplaces
        # one last move is possible, if there are places with entries > then opposite of k'th place
        # examples:
        # b = 8 or 9, bound = 4, and
        # ---> wpattern is 00011122233349 - then exchange 49
        # ---> wpattern is 00011122233339 - then exchange 39
        # ---> wpattern is 00011122222225 - no good exchange, 5 becomes 3 or 4, but the 2 (best choice) becomes > 5 

        goodoppositeplaces = [ kk for kk in R if kk != k and wpattern[kk] > b - wpattern[k] ]
        if goodoppositeplaces:
            goodoppositeplaces.sort( lambda k1, k2:    -cmp( wpattern[k1], wpattern[k2] ) )
            # we can still get an optimization...
            kk = goodoppositeplaces[0]
            print "Last exchange possible, place %s can be moved (using place %s)" % ( k, kk )
            wblocks, wpattern = exchange( wblocks, wpattern, b, k, kk )
            wplaces = [ kk, ] if kk > bound else []
            print "New pattern:",
            print showVector( wpattern, separator = '' if b<10 else ',' )
            if showBlocks:
                blocksInformation( wblocks )

    if wplaces:
        k, = wplaces
        oppositeplaces = [ kk for kk in R if wpattern[kk] + wpattern[k] == b ]
        if not oppositeplaces:
            print "UNIQUE SOLUTION, place %s cannot be moved" % k
        else:
            L = len( oppositeplaces )
            print ( "%s = 2^%s SOLUTIONS, obtained by interchanging even number of positions in %s"
                    % ( 2**L, L, str( [k, ] + oppositeplaces ) ) )
    else:
        if b == 2*bound+1:
            print "UNIQUE SOLUTION, all suboptimal places could be paired"
        else:
            midplaces = [ kk for kk in R if wpattern[kk] == bound ]
            L = len( midplaces )
            if L == 0 :
                print "UNIQUE SOLUTION, all suboptimal places could be paired, none equal %s remained" % bound
            elif L == 1:
                print "UNIQUE SOLUTION, one middle place remained"
            else:
                print ( "%s = 2^%s SOLUTIONS, obtained by interchanging even number of middle positions in %s"
                        % ( 2**(L-1), L-1, str(midplaces) ) )

    print "OPTIMAL NUMBER OF SWITCHES: %s" % sum(wpattern)
    if not returnSolution:
        return

    sol = []
    for wblock in wblocks:
        sol += wblock
    return sol


for N, m, lamps in CASES:
    searchLampSwitches( N, m, lamps )
    print 60*'.'

输出为TypeError: unable to find a common ring for all elements

此外,https://math.stackexchange.com/questions/2569003/how-to-find-an-algorithm-to-shut-down-all-lamps-with-minimum-number-of-moves中给出的算法似乎很难实现。

0 个答案:

没有答案