@@ -909,6 +909,42 @@ def _gen3(i, values=values):
909909 for x in gen (0 ):
910910 yield x
911911
912+ # And one more approach: For backtracking apps like the Knight's Tour
913+ # solver below, the number of backtracking levels can be enormous (one
914+ # level per square, for the Knight's Tour, so that e.g. a 100x100 board
915+ # needs 10,000 levels). In such cases Python is likely to run out of
916+ # stack space due to recursion. So here's a recursion-free version of
917+ # conjoin too.
918+ # NOTE WELL: This allows large problems to be solved with only trivial
919+ # demands on stack space. Without explicitly resumable generators, this is
920+ # much harder to achieve.
921+
922+ def flat_conjoin (gs ): # rename to conjoin to run tests with this instead
923+ n = len (gs )
924+ values = [None ] * n
925+ iters = [None ] * n
926+ i = 0
927+ while i >= 0 :
928+ # Need a fresh iterator.
929+ if i >= n :
930+ yield values
931+ # Backtrack.
932+ i -= 1
933+ else :
934+ iters [i ] = gs [i ]().next
935+
936+ # Need next value from current iterator.
937+ while i >= 0 :
938+ try :
939+ values [i ] = iters [i ]()
940+ except StopIteration :
941+ # Backtrack.
942+ i -= 1
943+ else :
944+ # Start fresh at next level.
945+ i += 1
946+ break
947+
912948# A conjoin-based N-Queens solver.
913949
914950class Queens :
@@ -961,12 +997,207 @@ def printsolution(self, row2col):
961997 print "|" + "|" .join (squares ) + "|"
962998 print sep
963999
1000+ # A conjoin-based Knight's Tour solver. This is pretty sophisticated
1001+ # (e.g., when used with flat_conjoin above, and passing hard=1 to the
1002+ # constructor, a 200x200 Knight's Tour was found quickly -- note that we're
1003+ # creating 10s of thousands of generators then!), so goes on at some length
1004+
1005+ class Knights :
1006+ def __init__ (self , n , hard = 0 ):
1007+ self .n = n
1008+
1009+ def coords2index (i , j ):
1010+ return i * n + j
1011+
1012+ offsets = [( 1 , 2 ), ( 2 , 1 ), ( 2 , - 1 ), ( 1 , - 2 ),
1013+ (- 1 , - 2 ), (- 2 , - 1 ), (- 2 , 1 ), (- 1 , 2 )]
1014+ succs = []
1015+ for i in range (n ):
1016+ for j in range (n ):
1017+ s = [coords2index (i + io , j + jo ) for io , jo in offsets
1018+ if 0 <= i + io < n and
1019+ 0 <= j + jo < n ]
1020+ succs .append (s )
1021+ del s
1022+ del offsets
1023+
1024+ free = [0 ] * n ** 2 # 0 if occupied, 1 if visited
1025+ nexits = free [:] # number of free successors
1026+
1027+ def decrexits (i0 ):
1028+ # If we remove all exits from a free square, we're dead:
1029+ # even if we move to it next, we can't leave it again.
1030+ # If we create a square with one exit, we must visit it next;
1031+ # else somebody else will have to visit it, and since there's
1032+ # only one adjacent, there won't be a way to leave it again.
1033+ # Finelly, if we create more than one free square with a
1034+ # single exit, we can only move to one of them next, leaving
1035+ # the other one a dead end.
1036+ ne0 = ne1 = 0
1037+ for i in succs [i0 ]:
1038+ if free [i ]:
1039+ e = nexits [i ] - 1
1040+ nexits [i ] = e
1041+ if e == 0 :
1042+ ne0 += 1
1043+ elif e == 1 :
1044+ ne1 += 1
1045+ return ne0 == 0 and ne1 < 2
1046+
1047+ def increxits (i0 ):
1048+ for i in succs [i0 ]:
1049+ if free [i ]:
1050+ nexits [i ] += 1
1051+
1052+ # Generate the first move.
1053+ def first ():
1054+ if n < 1 :
1055+ return
1056+
1057+ # Initialize board structures.
1058+ for i in xrange (n ** 2 ):
1059+ free [i ] = 1
1060+ nexits [i ] = len (succs [i ])
1061+
1062+ # Since we're looking for a cycle, it doesn't matter where we
1063+ # start. Starting in a corner makes the 2nd move easy.
1064+ corner = coords2index (0 , 0 )
1065+ free [corner ] = 0
1066+ decrexits (corner )
1067+ self .lastij = corner
1068+ yield corner
1069+ increxits (corner )
1070+ free [corner ] = 1
1071+
1072+ # Generate the second moves.
1073+ def second ():
1074+ corner = coords2index (0 , 0 )
1075+ assert self .lastij == corner # i.e., we started in the corner
1076+ if n < 3 :
1077+ return
1078+ assert nexits [corner ] == len (succs [corner ]) == 2
1079+ assert coords2index (1 , 2 ) in succs [corner ]
1080+ assert coords2index (2 , 1 ) in succs [corner ]
1081+ # Only two choices. Whichever we pick, the other must be the
1082+ # square picked on move n**2, as it's the only way to get back
1083+ # to (0, 0). Save its index in self.final so that moves before
1084+ # the last know it must be kept free.
1085+ for i , j in (1 , 2 ), (2 , 1 ):
1086+ this , final = coords2index (i , j ), coords2index (3 - i , 3 - j )
1087+ assert free [this ] and free [final ]
1088+ self .final = final
1089+ nexits [final ] += 1 # it has an exit back to 0,0
1090+
1091+ free [this ] = 0
1092+ decrexits (this )
1093+ self .lastij = this
1094+ yield this
1095+ increxits (this )
1096+ free [this ] = 1
1097+
1098+ nexits [final ] -= 1
1099+
1100+ # Generate moves 3 thru n**2-1.
1101+ def advance ():
1102+ # If some successor has only one exit, must take it.
1103+ # Else favor successors with fewer exits.
1104+ candidates = []
1105+ for i in succs [self .lastij ]:
1106+ if free [i ]:
1107+ e = nexits [i ]
1108+ assert e > 0 , "else decrexits() pruning flawed"
1109+ if e == 1 :
1110+ candidates = [(e , i )]
1111+ break
1112+ candidates .append ((e , i ))
1113+ else :
1114+ candidates .sort ()
1115+
1116+ for e , i in candidates :
1117+ if i != self .final :
1118+ if decrexits (i ):
1119+ free [i ] = 0
1120+ self .lastij = i
1121+ yield i
1122+ free [i ] = 1
1123+ increxits (i )
1124+
1125+ # Generate moves 3 thru n**2-1. Alternative version using a
1126+ # stronger (but more expensive) heuristic to order successors.
1127+ # Since the # of backtracking levels is n**2, a poor move early on
1128+ # can take eons to undo. Smallest n for which this matters a lot is
1129+ # n==52.
1130+ def advance_hard (midpoint = (n - 1 )/ 2.0 ):
1131+ # If some successor has only one exit, must take it.
1132+ # Else favor successors with fewer exits.
1133+ # Break ties via max distance from board centerpoint (favor
1134+ # corners and edges whenever possible).
1135+ candidates = []
1136+ for i in succs [self .lastij ]:
1137+ if free [i ]:
1138+ e = nexits [i ]
1139+ assert e > 0 , "else decrexits() pruning flawed"
1140+ if e == 1 :
1141+ candidates = [(e , 0 , i )]
1142+ break
1143+ i1 , j1 = divmod (i , n )
1144+ d = (i1 - midpoint )** 2 + (j1 - midpoint )** 2
1145+ candidates .append ((e , - d , i ))
1146+ else :
1147+ candidates .sort ()
1148+
1149+ for e , d , i in candidates :
1150+ if i != self .final :
1151+ if decrexits (i ):
1152+ free [i ] = 0
1153+ self .lastij = i
1154+ yield i
1155+ free [i ] = 1
1156+ increxits (i )
1157+
1158+ # Generate the last move.
1159+ def last ():
1160+ assert self .final in succs [self .lastij ]
1161+ yield self .final
1162+
1163+ if n <= 1 :
1164+ self .rowgenerators = [first ]
1165+ else :
1166+ self .rowgenerators = [first , second ] + \
1167+ [hard and advance_hard or advance ] * (n ** 2 - 3 ) + \
1168+ [last ]
1169+
1170+ # Generate solutions.
1171+ def solve (self ):
1172+ for x in conjoin (self .rowgenerators ):
1173+ yield x
1174+
1175+ def printsolution (self , x ):
1176+ n = self .n
1177+ assert len (x ) == n ** 2
1178+ w = len (str (n ** 2 + 1 ))
1179+ format = "%" + str (w ) + "d"
1180+
1181+ squares = [[None ] * n for i in range (n )]
1182+ k = 1
1183+ for i in x :
1184+ i1 , j1 = divmod (i , n )
1185+ squares [i1 ][j1 ] = format % k
1186+ k += 1
1187+
1188+ sep = "+" + ("-" * w + "+" ) * n
1189+ print sep
1190+ for i in range (n ):
1191+ row = squares [i ]
1192+ print "|" + "|" .join (row ) + "|"
1193+ print sep
1194+
9641195conjoin_tests = """
9651196
9661197Generate the 3-bit binary numbers in order. This illustrates dumbest-
9671198possible use of conjoin, just to generate the full cross-product.
9681199
969- >>> for c in conjoin([lambda: ( 0, 1)] * 3):
1200+ >>> for c in conjoin([lambda: iter(( 0, 1) )] * 3):
9701201... print c
9711202[0, 0, 0]
9721203[0, 0, 1]
@@ -986,7 +1217,7 @@ def printsolution(self, row2col):
9861217... yield x[:]
9871218
9881219>>> for n in range(10):
989- ... all = list(gencopy(conjoin([lambda: ( 0, 1)] * n)))
1220+ ... all = list(gencopy(conjoin([lambda: iter(( 0, 1) )] * n)))
9901221... print n, len(all), all[0] == [0] * n, all[-1] == [1] * n
99112220 1 1 1
99212231 2 1 1
@@ -1048,6 +1279,64 @@ def printsolution(self, row2col):
10481279
10491280>>> print count, "solutions in all."
1050128192 solutions in all.
1282+
1283+ And run a Knight's Tour on a 10x10 board. Note that there are about
1284+ 20,000 solutions even on a 6x6 board, so don't dare run this to exhaustion.
1285+
1286+ >>> k = Knights(10)
1287+ >>> LIMIT = 2
1288+ >>> count = 0
1289+ >>> for x in k.solve():
1290+ ... count += 1
1291+ ... if count <= LIMIT:
1292+ ... print "Solution", count
1293+ ... k.printsolution(x)
1294+ ... else:
1295+ ... break
1296+ Solution 1
1297+ +---+---+---+---+---+---+---+---+---+---+
1298+ | 1| 58| 27| 34| 3| 40| 29| 10| 5| 8|
1299+ +---+---+---+---+---+---+---+---+---+---+
1300+ | 26| 35| 2| 57| 28| 33| 4| 7| 30| 11|
1301+ +---+---+---+---+---+---+---+---+---+---+
1302+ | 59|100| 73| 36| 41| 56| 39| 32| 9| 6|
1303+ +---+---+---+---+---+---+---+---+---+---+
1304+ | 74| 25| 60| 55| 72| 37| 42| 49| 12| 31|
1305+ +---+---+---+---+---+---+---+---+---+---+
1306+ | 61| 86| 99| 76| 63| 52| 47| 38| 43| 50|
1307+ +---+---+---+---+---+---+---+---+---+---+
1308+ | 24| 75| 62| 85| 54| 71| 64| 51| 48| 13|
1309+ +---+---+---+---+---+---+---+---+---+---+
1310+ | 87| 98| 91| 80| 77| 84| 53| 46| 65| 44|
1311+ +---+---+---+---+---+---+---+---+---+---+
1312+ | 90| 23| 88| 95| 70| 79| 68| 83| 14| 17|
1313+ +---+---+---+---+---+---+---+---+---+---+
1314+ | 97| 92| 21| 78| 81| 94| 19| 16| 45| 66|
1315+ +---+---+---+---+---+---+---+---+---+---+
1316+ | 22| 89| 96| 93| 20| 69| 82| 67| 18| 15|
1317+ +---+---+---+---+---+---+---+---+---+---+
1318+ Solution 2
1319+ +---+---+---+---+---+---+---+---+---+---+
1320+ | 1| 58| 27| 34| 3| 40| 29| 10| 5| 8|
1321+ +---+---+---+---+---+---+---+---+---+---+
1322+ | 26| 35| 2| 57| 28| 33| 4| 7| 30| 11|
1323+ +---+---+---+---+---+---+---+---+---+---+
1324+ | 59|100| 73| 36| 41| 56| 39| 32| 9| 6|
1325+ +---+---+---+---+---+---+---+---+---+---+
1326+ | 74| 25| 60| 55| 72| 37| 42| 49| 12| 31|
1327+ +---+---+---+---+---+---+---+---+---+---+
1328+ | 61| 86| 99| 76| 63| 52| 47| 38| 43| 50|
1329+ +---+---+---+---+---+---+---+---+---+---+
1330+ | 24| 75| 62| 85| 54| 71| 64| 51| 48| 13|
1331+ +---+---+---+---+---+---+---+---+---+---+
1332+ | 87| 98| 89| 80| 77| 84| 53| 46| 65| 44|
1333+ +---+---+---+---+---+---+---+---+---+---+
1334+ | 90| 23| 92| 95| 70| 79| 68| 83| 14| 17|
1335+ +---+---+---+---+---+---+---+---+---+---+
1336+ | 97| 88| 21| 78| 81| 94| 19| 16| 45| 66|
1337+ +---+---+---+---+---+---+---+---+---+---+
1338+ | 22| 91| 96| 93| 20| 69| 82| 67| 18| 15|
1339+ +---+---+---+---+---+---+---+---+---+---+
10511340"""
10521341
10531342__test__ = {"tut" : tutorial_tests ,
0 commit comments