Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
cpython
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
cpython
Commits
9a8c8e27
Kaydet (Commit)
9a8c8e27
authored
Tem 13, 2001
tarafından
Tim Peters
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Having fun on my own time: quicker flat_conjoin; Knights Tour solver
simplified and generalized to rectangular boards.
üst
7eea2714
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
110 additions
and
100 deletions
+110
-100
test_generators.py
Lib/test/test_generators.py
+110
-100
No files found.
Lib/test/test_generators.py
Dosyayı görüntüle @
9a8c8e27
...
@@ -903,32 +903,41 @@ def conjoin(gs):
...
@@ -903,32 +903,41 @@ def conjoin(gs):
# conjoin too.
# conjoin too.
# NOTE WELL: This allows large problems to be solved with only trivial
# NOTE WELL: This allows large problems to be solved with only trivial
# demands on stack space. Without explicitly resumable generators, this is
# demands on stack space. Without explicitly resumable generators, this is
# much harder to achieve.
# much harder to achieve. OTOH, this is much slower (up to a factor of 2)
# than the fancy unrolled recursive conjoin.
def
flat_conjoin
(
gs
):
# rename to conjoin to run tests with this instead
def
flat_conjoin
(
gs
):
# rename to conjoin to run tests with this instead
n
=
len
(
gs
)
n
=
len
(
gs
)
values
=
[
None
]
*
n
values
=
[
None
]
*
n
iters
=
[
None
]
*
n
iters
=
[
None
]
*
n
_StopIteration
=
StopIteration
# make local because caught a *lot*
i
=
0
i
=
0
while
i
>=
0
:
while
1
:
# Need a fresh iterator.
# Descend.
if
i
>=
n
:
try
:
yield
values
while
i
<
n
:
# Backtrack.
it
=
iters
[
i
]
=
gs
[
i
]()
.
next
i
-=
1
values
[
i
]
=
it
()
i
+=
1
except
_StopIteration
:
pass
else
:
else
:
iters
[
i
]
=
gs
[
i
]()
.
next
assert
i
==
n
yield
values
# Need next value from current iterator.
# Backtrack until an older iterator can be resumed.
i
-=
1
while
i
>=
0
:
while
i
>=
0
:
try
:
try
:
values
[
i
]
=
iters
[
i
]()
values
[
i
]
=
iters
[
i
]()
except
StopIteration
:
# Success! Start fresh at next level.
# Backtrack.
i
+=
1
break
except
_StopIteration
:
# Continue backtracking.
i
-=
1
i
-=
1
else
:
else
:
# Start fresh at next level.
assert
i
<
0
i
+=
1
break
break
# A conjoin-based N-Queens solver.
# A conjoin-based N-Queens solver.
...
@@ -986,31 +995,21 @@ class Queens:
...
@@ -986,31 +995,21 @@ class Queens:
# A conjoin-based Knight's Tour solver. This is pretty sophisticated
# A conjoin-based Knight's Tour solver. This is pretty sophisticated
# (e.g., when used with flat_conjoin above, and passing hard=1 to the
# (e.g., when used with flat_conjoin above, and passing hard=1 to the
# constructor, a 200x200 Knight's Tour was found quickly -- note that we're
# constructor, a 200x200 Knight's Tour was found quickly -- note that we're
# creating 10s of thousands of generators then!),
so goes on at some length
# creating 10s of thousands of generators then!),
and is lengthy.
class
Knights
:
class
Knights
:
def
__init__
(
self
,
n
,
hard
=
0
):
def
__init__
(
self
,
m
,
n
,
hard
=
0
):
self
.
n
=
n
self
.
m
,
self
.
n
=
m
,
n
def
coords2index
(
i
,
j
):
# solve() will set up succs[i] to be a list of square #i's
return
i
*
n
+
j
# successors.
succs
=
self
.
succs
=
[]
offsets
=
[(
1
,
2
),
(
2
,
1
),
(
2
,
-
1
),
(
1
,
-
2
),
(
-
1
,
-
2
),
(
-
2
,
-
1
),
(
-
2
,
1
),
(
-
1
,
2
)]
succs
=
[]
for
i
in
range
(
n
):
for
j
in
range
(
n
):
s
=
[
coords2index
(
i
+
io
,
j
+
jo
)
for
io
,
jo
in
offsets
if
0
<=
i
+
io
<
n
and
0
<=
j
+
jo
<
n
]
succs
.
append
(
s
)
del
s
del
offsets
free
=
[
0
]
*
n
**
2
# 0 if occupied, 1 if visited
# Remove i0 from each of its successor's successor lists, i.e.
nexits
=
free
[:]
# number of free successors
# successors can't go back to i0 again. Return 0 if we can
# detect this makes a solution impossible, else return 1.
def
decrexits
(
i0
):
def
remove_from_successors
(
i0
,
len
=
len
):
# If we remove all exits from a free square, we're dead:
# If we remove all exits from a free square, we're dead:
# even if we move to it next, we can't leave it again.
# even if we move to it next, we can't leave it again.
# If we create a square with one exit, we must visit it next;
# If we create a square with one exit, we must visit it next;
...
@@ -1021,77 +1020,67 @@ class Knights:
...
@@ -1021,77 +1020,67 @@ class Knights:
# the other one a dead end.
# the other one a dead end.
ne0
=
ne1
=
0
ne0
=
ne1
=
0
for
i
in
succs
[
i0
]:
for
i
in
succs
[
i0
]:
if
free
[
i
]:
s
=
succs
[
i
]
e
=
nexits
[
i
]
-
1
s
.
remove
(
i0
)
nexits
[
i
]
=
e
e
=
len
(
s
)
if
e
==
0
:
if
e
==
0
:
ne0
+=
1
ne0
+=
1
elif
e
==
1
:
elif
e
==
1
:
ne1
+=
1
ne1
+=
1
return
ne0
==
0
and
ne1
<
2
return
ne0
==
0
and
ne1
<
2
def
increxits
(
i0
):
# Put i0 back in each of its successor's successor lists.
def
add_to_successors
(
i0
):
for
i
in
succs
[
i0
]:
for
i
in
succs
[
i0
]:
if
free
[
i
]:
succs
[
i
]
.
append
(
i0
)
nexits
[
i
]
+=
1
# Generate the first move.
# Generate the first move.
def
first
():
def
first
():
if
n
<
1
:
if
m
<
1
or
n
<
1
:
return
return
# Initialize board structures.
for
i
in
xrange
(
n
**
2
):
free
[
i
]
=
1
nexits
[
i
]
=
len
(
succs
[
i
])
# Since we're looking for a cycle, it doesn't matter where we
# Since we're looking for a cycle, it doesn't matter where we
# start. Starting in a corner makes the 2nd move easy.
# start. Starting in a corner makes the 2nd move easy.
corner
=
coords2index
(
0
,
0
)
corner
=
self
.
coords2index
(
0
,
0
)
free
[
corner
]
=
0
remove_from_successors
(
corner
)
decrexits
(
corner
)
self
.
lastij
=
corner
self
.
lastij
=
corner
yield
corner
yield
corner
increxits
(
corner
)
add_to_successors
(
corner
)
free
[
corner
]
=
1
# Generate the second moves.
# Generate the second moves.
def
second
():
def
second
():
corner
=
coords2index
(
0
,
0
)
corner
=
self
.
coords2index
(
0
,
0
)
assert
self
.
lastij
==
corner
# i.e., we started in the corner
assert
self
.
lastij
==
corner
# i.e., we started in the corner
if
n
<
3
:
if
m
<
3
or
n
<
3
:
return
return
assert
nexits
[
corner
]
==
len
(
succs
[
corner
])
==
2
assert
len
(
succs
[
corner
])
==
2
assert
coords2index
(
1
,
2
)
in
succs
[
corner
]
assert
self
.
coords2index
(
1
,
2
)
in
succs
[
corner
]
assert
coords2index
(
2
,
1
)
in
succs
[
corner
]
assert
self
.
coords2index
(
2
,
1
)
in
succs
[
corner
]
# Only two choices. Whichever we pick, the other must be the
# Only two choices. Whichever we pick, the other must be the
# square picked on move
n**2
, as it's the only way to get back
# square picked on move
m*n
, as it's the only way to get back
# to (0, 0). Save its index in self.final so that moves before
# to (0, 0). Save its index in self.final so that moves before
# the last know it must be kept free.
# the last know it must be kept free.
for
i
,
j
in
(
1
,
2
),
(
2
,
1
):
for
i
,
j
in
(
1
,
2
),
(
2
,
1
):
this
,
final
=
coords2index
(
i
,
j
),
coords2index
(
3
-
i
,
3
-
j
)
this
=
self
.
coords2index
(
i
,
j
)
assert
free
[
this
]
and
free
[
final
]
final
=
self
.
coords2index
(
3
-
i
,
3
-
j
)
self
.
final
=
final
self
.
final
=
final
nexits
[
final
]
+=
1
# it has an exit back to 0,0
free
[
this
]
=
0
remove_from_successors
(
this
)
decrexits
(
this
)
succs
[
final
]
.
append
(
corner
)
self
.
lastij
=
this
self
.
lastij
=
this
yield
this
yield
this
increxits
(
this
)
succs
[
final
]
.
remove
(
corner
)
free
[
this
]
=
1
add_to_successors
(
this
)
nexits
[
final
]
-=
1
# Generate moves 3 thru
n**2
-1.
# Generate moves 3 thru
m*n
-1.
def
advance
():
def
advance
(
len
=
len
):
# If some successor has only one exit, must take it.
# If some successor has only one exit, must take it.
# Else favor successors with fewer exits.
# Else favor successors with fewer exits.
candidates
=
[]
candidates
=
[]
for
i
in
succs
[
self
.
lastij
]:
for
i
in
succs
[
self
.
lastij
]:
if
free
[
i
]:
e
=
len
(
succs
[
i
])
e
=
nexits
[
i
]
assert
e
>
0
,
"else remove_from_successors() pruning flawed"
assert
e
>
0
,
"else decrexits() pruning flawed"
if
e
==
1
:
if
e
==
1
:
candidates
=
[(
e
,
i
)]
candidates
=
[(
e
,
i
)]
break
break
...
@@ -1101,79 +1090,100 @@ class Knights:
...
@@ -1101,79 +1090,100 @@ class Knights:
for
e
,
i
in
candidates
:
for
e
,
i
in
candidates
:
if
i
!=
self
.
final
:
if
i
!=
self
.
final
:
if
decrexits
(
i
):
if
remove_from_successors
(
i
):
free
[
i
]
=
0
self
.
lastij
=
i
self
.
lastij
=
i
yield
i
yield
i
free
[
i
]
=
1
add_to_successors
(
i
)
increxits
(
i
)
# Generate moves 3 thru
n**2
-1. Alternative version using a
# Generate moves 3 thru
m*n
-1. Alternative version using a
# stronger (but more expensive) heuristic to order successors.
# stronger (but more expensive) heuristic to order successors.
# Since the # of backtracking levels is
n**2
, a poor move early on
# Since the # of backtracking levels is
m*n
, a poor move early on
# can take eons to undo. Smallest
n for which this matters a lot
is
# can take eons to undo. Smallest
square board for which th
is
#
n==
52.
#
matters a lot is 52x
52.
def
advance_hard
(
midpoint
=
(
n
-
1
)
/
2.0
):
def
advance_hard
(
vmid
=
(
m
-
1
)
/
2.0
,
hmid
=
(
n
-
1
)
/
2.0
,
len
=
len
):
# If some successor has only one exit, must take it.
# If some successor has only one exit, must take it.
# Else favor successors with fewer exits.
# Else favor successors with fewer exits.
# Break ties via max distance from board centerpoint (favor
# Break ties via max distance from board centerpoint (favor
# corners and edges whenever possible).
# corners and edges whenever possible).
candidates
=
[]
candidates
=
[]
for
i
in
succs
[
self
.
lastij
]:
for
i
in
succs
[
self
.
lastij
]:
if
free
[
i
]:
e
=
len
(
succs
[
i
])
e
=
nexits
[
i
]
assert
e
>
0
,
"else remove_from_successors() pruning flawed"
assert
e
>
0
,
"else decrexits() pruning flawed"
if
e
==
1
:
if
e
==
1
:
candidates
=
[(
e
,
0
,
i
)]
candidates
=
[(
e
,
0
,
i
)]
break
break
i1
,
j1
=
divmod
(
i
,
n
)
i1
,
j1
=
self
.
index2coords
(
i
)
d
=
(
i1
-
midpoint
)
**
2
+
(
j1
-
midpoint
)
**
2
d
=
(
i1
-
vmid
)
**
2
+
(
j1
-
hmid
)
**
2
candidates
.
append
((
e
,
-
d
,
i
))
candidates
.
append
((
e
,
-
d
,
i
))
else
:
else
:
candidates
.
sort
()
candidates
.
sort
()
for
e
,
d
,
i
in
candidates
:
for
e
,
d
,
i
in
candidates
:
if
i
!=
self
.
final
:
if
i
!=
self
.
final
:
if
decrexits
(
i
):
if
remove_from_successors
(
i
):
free
[
i
]
=
0
self
.
lastij
=
i
self
.
lastij
=
i
yield
i
yield
i
free
[
i
]
=
1
add_to_successors
(
i
)
increxits
(
i
)
# Generate the last move.
# Generate the last move.
def
last
():
def
last
():
assert
self
.
final
in
succs
[
self
.
lastij
]
assert
self
.
final
in
succs
[
self
.
lastij
]
yield
self
.
final
yield
self
.
final
if
n
<=
1
:
if
m
*
n
<
4
:
self
.
row
generators
=
[
first
]
self
.
square
generators
=
[
first
]
else
:
else
:
self
.
row
generators
=
[
first
,
second
]
+
\
self
.
square
generators
=
[
first
,
second
]
+
\
[
hard
and
advance_hard
or
advance
]
*
(
n
**
2
-
3
)
+
\
[
hard
and
advance_hard
or
advance
]
*
(
m
*
n
-
3
)
+
\
[
last
]
[
last
]
def
coords2index
(
self
,
i
,
j
):
assert
0
<=
i
<
self
.
m
assert
0
<=
j
<
self
.
n
return
i
*
self
.
n
+
j
def
index2coords
(
self
,
index
):
assert
0
<=
index
<
self
.
m
*
self
.
n
return
divmod
(
index
,
self
.
n
)
def
_init_board
(
self
):
succs
=
self
.
succs
del
succs
[:]
m
,
n
=
self
.
m
,
self
.
n
c2i
=
self
.
coords2index
offsets
=
[(
1
,
2
),
(
2
,
1
),
(
2
,
-
1
),
(
1
,
-
2
),
(
-
1
,
-
2
),
(
-
2
,
-
1
),
(
-
2
,
1
),
(
-
1
,
2
)]
rangen
=
range
(
n
)
for
i
in
range
(
m
):
for
j
in
rangen
:
s
=
[
c2i
(
i
+
io
,
j
+
jo
)
for
io
,
jo
in
offsets
if
0
<=
i
+
io
<
m
and
0
<=
j
+
jo
<
n
]
succs
.
append
(
s
)
# Generate solutions.
# Generate solutions.
def
solve
(
self
):
def
solve
(
self
):
for
x
in
conjoin
(
self
.
rowgenerators
):
self
.
_init_board
()
for
x
in
conjoin
(
self
.
squaregenerators
):
yield
x
yield
x
def
printsolution
(
self
,
x
):
def
printsolution
(
self
,
x
):
n
=
self
.
n
m
,
n
=
self
.
m
,
self
.
n
assert
len
(
x
)
==
n
**
2
assert
len
(
x
)
==
m
*
n
w
=
len
(
str
(
n
**
2
+
1
))
w
=
len
(
str
(
m
*
n
))
format
=
"
%
"
+
str
(
w
)
+
"d"
format
=
"
%
"
+
str
(
w
)
+
"d"
squares
=
[[
None
]
*
n
for
i
in
range
(
n
)]
squares
=
[[
None
]
*
n
for
i
in
range
(
m
)]
k
=
1
k
=
1
for
i
in
x
:
for
i
in
x
:
i1
,
j1
=
divmod
(
i
,
n
)
i1
,
j1
=
self
.
index2coords
(
i
)
squares
[
i1
][
j1
]
=
format
%
k
squares
[
i1
][
j1
]
=
format
%
k
k
+=
1
k
+=
1
sep
=
"+"
+
(
"-"
*
w
+
"+"
)
*
n
sep
=
"+"
+
(
"-"
*
w
+
"+"
)
*
n
print
sep
print
sep
for
i
in
range
(
n
):
for
i
in
range
(
m
):
row
=
squares
[
i
]
row
=
squares
[
i
]
print
"|"
+
"|"
.
join
(
row
)
+
"|"
print
"|"
+
"|"
.
join
(
row
)
+
"|"
print
sep
print
sep
...
@@ -1269,7 +1279,7 @@ Solution 2
...
@@ -1269,7 +1279,7 @@ Solution 2
And run a Knight's Tour on a 10x10 board. Note that there are about
And run a Knight's Tour on a 10x10 board. Note that there are about
20,000 solutions even on a 6x6 board, so don't dare run this to exhaustion.
20,000 solutions even on a 6x6 board, so don't dare run this to exhaustion.
>>> k = Knights(10)
>>> k = Knights(10
, 10
)
>>> LIMIT = 2
>>> LIMIT = 2
>>> count = 0
>>> count = 0
>>> for x in k.solve():
>>> for x in k.solve():
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment