Con trăn 2
Bảng lên đến n = 64
, được xác minh tối ưu với lực lượng vũ phu lên đến n = 32
:
4 4 0001
8 4 00010001
12 6 000001010011
16 8 0000010011101011
20 10 00010001011110011010
24 12 000101001000111110110111
28 14 0001011000010011101011111011
32 14 00001101000111011101101011110010
36 18 001000101001000111110011010110111000
40 20 0010101110001101101111110100011100100100
44 18 00010000011100100011110110110101011101101111
48 24 001011011001010111111001110000100110101000000110
52 26 0011010111000100111011011111001010001110100001001000
56 28 00100111111101010110001100001101100000001010100111001011
60 30 000001101101100011100101011101111110010010111100011010100010
64 32 0001100011110101111111010010011011100111000010101000001011011001
nơi 0
đại diện -1
. Nếu n
không chia hết cho 4 thì m = 1
là tối ưu. Được tạo bằng mã này (hoặc các biến thể nhỏ của nó) nhưng với nhiều thử nghiệm hơn cho cao hơn n
:
from random import *
seed(10)
trials=10000
def calcm(x,n):
m=1
y=x
while 1:
y=((y&1)<<(n-1))|(y>>1)
if bin(x^y).count('1')!=n/2:
return m
m+=1
def hillclimb(x,n,ns):
bestm=calcm(x,n)
while 1:
cands=[]
for pos in ns:
xx=x^(1<<pos)
m=calcm(xx,n)
if m>bestm:
bestm=m
cands=[xx]
elif cands and m==bestm:
cands+=[xx]
if not cands:
break
x=choice(cands)
return x,bestm
def approx(n):
if n<10: return brute(n)
bestm=1
bestx=0
for trial in xrange(1,trials+1):
if not trial&16383:
print bestm,bin((1<<n)|bestx)[3:]
if not trial&1:
x=randint(0,(1<<(n/2-2))-1)
x=(x<<(n/2)) | (((1<<(n/2))-1)^x)
ns=range(n/2-2)
if not trial&7:
adj=randint(1,5)
x^=((1<<adj)-1)<<randint(0,n/2-adj)
else:
x=randint(0,(1<<(n-2))-1)
ns=range(n-2)
x,m=hillclimb(x,n,ns)
if m>bestm:
bestm=m
bestx=x
return bestm,bestx
def brute(n):
bestm=1
bestx=0
for x in xrange(1<<(n-2)):
m=calcm(x,n)
if m>bestm:
bestm=m
bestx=x
return bestm,bestx
for n in xrange(4,101,4):
m,x=approx(n)
print n,m,bin((1<<n)|x)[3:]
Cách tiếp cận là tìm kiếm ngẫu nhiên đơn giản với leo đồi, tận dụng một mô hình được chú ý cho nhỏ n
. Mô hình là để tối ưu m
, nửa sau của hàng đầu tiên thường có khoảng cách chỉnh sửa nhỏ so với phủ định (bitwise) của nửa đầu. Các kết quả cho mã trên là tốt cho nhỏ n
nhưng bắt đầu xấu đi không lâu sau khi lực lượng vũ phu trở nên không khả thi; Tôi sẽ rất vui khi thấy một cách tiếp cận tốt hơn.
Một số quan sát:
- Khi
n
là số lẻ, m = 1
là tối ưu vì số lẻ và số âm không thể cộng thành 0. (Trực giao có nghĩa là sản phẩm chấm bằng không.)
- Khi nào
n = 4k + 2
, m = 1
là tối ưu bởi vì để m >= 2
chúng ta cần có chính xác các n/2
đảo ngược ký hiệu {(a1,a2), (a2,a3), ... (a{n-1},an), (an,a1)}
và một số lượng đảo ngược ký hiệu lẻ sẽ ngụ ý a1 = -a1
.
- Sản phẩm chấm của hai hàng
i
và j
trong một ma trận tuần hoàn được xác định bởi abs(i-j)
. Ví dụ, nếu row1 . row2 = 0
sau đó row4 . row5 = 0
. Điều này là do các cặp phần tử cho sản phẩm chấm giống nhau, chỉ được xoay.
- Do đó, để kiểm tra tính trực giao lẫn nhau, chúng ta chỉ cần kiểm tra các hàng liên tiếp so với hàng đầu tiên.
- Nếu chúng ta biểu diễn một hàng dưới dạng một chuỗi nhị phân
0
thay cho -1
, chúng ta có thể kiểm tra tính trực giao của hai hàng bằng cách lấy bitwise xor và so sánh số đếm với n/2
.
- Chúng ta có thể tùy ý sửa hai phần tử đầu tiên của hàng đầu tiên, bởi vì (1) Việc phủ định ma trận không ảnh hưởng đến việc các sản phẩm chấm có bằng 0 hay không và (2) Chúng ta biết rằng phải có ít nhất hai phần tử liền kề có cùng dấu và hai các phần tử liền kề với dấu hiệu khác nhau, vì vậy chúng ta có thể xoay để đặt cặp mong muốn ở đầu.
- Một giải pháp
(n0, m0)
sẽ tự động đưa ra các giải pháp (k * n0, m0)
cho tùy ý k > 1
, bằng cách (lặp đi lặp lại) nối hàng đầu tiên với chính nó. Một hậu quả là chúng ta có thể dễ dàng đạt được m = 4
bất kỳ số n
chia nào cho 4.
Đó là điều tự nhiên khi phỏng đoán n/2
là giới hạn trên chặt chẽ m
khi nào n > 4
, nhưng tôi không biết điều đó sẽ được chứng minh như thế nào.