Nhận 39 byte
Đây là một lời giải thích về cách tôi có một giải pháp 39 byte, mà Dennis và JonathanFrech cũng tìm thấy riêng. Hay nói đúng hơn, nó giải thích làm thế nào người ta có thể đi đến câu trả lời trong nhận thức muộn màng, theo cách mà nó đẹp hơn nhiều so với con đường thực tế của tôi đến nó, nơi đầy lý luận lầy lội và ngõ cụt.
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Viết cái này ít chơi gôn hơn và có nhiều parens hơn, nó trông giống như:
n=0
for _ in range(400):
print n
n=(n+2)^(-((n+2)^n))%3
Chẵn lẻ bit
Chúng tôi bắt đầu với một ý tưởng từ giải pháp 47 byte của tôi để xuất ra tất cả các số của biểu mẫu n=2*k+b
nơi k
đếm ngược 0,1,...,399
vàb
là một bit chẵn lẻ làm cho tổng số 1 chẵn.
Chúng ta hãy viết par(x)
cho tính chẵn lẻ của bitx
, đó là xor ( ^
) tất cả các bit trong x
. Đây là 0 nếu có số bit 1 chẵn (số là xấu) và 1 nếu có số lẻ 1 bit. Vì n=2*k+b
, chúng ta có par(n) = par(k)^b
, vì vậy để đạt được cái ác par(n)==0
chúng ta cần b=par(k)
, tức là bit cuối cùng n
là bit chẵn lẻ của các bit trước.
Những nỗ lực đầu tiên của tôi khi chơi golf là thể hiện par(k)
, lúc đầu trực tiếp với bin(k).count('1')%2
, và sau đó với thao tác bit .
Cập nhật chẵn lẻ
Tuy nhiên, dường như không có một biểu hiện ngắn. Thay vào đó, nó giúp nhận ra rằng có nhiều thông tin hơn để làm việc. Thay vì chỉ tính toán tính chẵn lẻ của số hiện tại,
k ----> par(k)
chúng ta có thể cập nhật tính chẵn lẻ bit khi chúng ta tăng k
lên k+1
.
k ----> par(k)
|
v
k+1 ----> par(k+1)
Đó là, vì chúng tôi đang đếm k=0,1,2,...
, chúng tôi chỉ cần duy trì tính chẵn lẻ bit hiện tại thay vì tính toán từ đầu mỗi lần. Bản cập nhật bit chẵn lẻ par(k+1)^par(k)
là chẵn lẻ của số bit lộn trong đi từ k
đến k+1
, đó là par((k+1)^k)
.
par(k+1) ^ par(k) = par((k+1)^k)
par(k+1) = par(k) ^ par((k+1)^k)
Hình thức (k+1)^k
Bây giờ chúng ta cần tính toán par((k+1)^k)
. Có vẻ như chúng ta chẳng đi đến đâu vì tính toán tương đương bit chính xác là vấn đề chúng ta đang cố gắng giải quyết. Nhưng, các số được biểu thị là (k+1)^k
có dạng 1,3,7,15,..
, đó là một dưới mức lũy thừa 2, một thực tế thường được sử dụng trong các bản hack bit . Hãy xem tại sao lại như vậy.
Khi chúng ta tăng lên k
, hiệu ứng của mang nhị phân là đảo ngược cái cuối cùng 0
và tất cả 1
sang bên phải của nó, tạo ra một vị trí dẫn đầu mới 0
nếu không có. Ví dụ: lấyk=43=0b101011
**
101011 (43)
+ 1
------
= 101100 (44)
101011 (43)
^101100 (44)
------
= 000111 (77)
Các cột gây ra mang được đánh dấu bằng *
. Những cái này có một 1
sự thay đổi thành a 0
và truyền một bit carry 1
, nó tiếp tục truyền sang trái cho đến khi nó chạm 0
vào k
, nó thay đổi thành 1
. Bất kỳ bit nào bên trái không bị ảnh hưởng. Vì vậy, khi k^(k+1)
kiểm tra mà cắn vị trí thay đổi k
để k+1
, nó tìm thấy vị trí của các đầu mút bên phải 0
và 1
's bên phải của nó. Nghĩa là, các bit thay đổi tạo thành một hậu tố, do đó, kết quả là 0 được theo sau bởi một hoặc nhiều 1. Không có các số 0 đứng đầu, có các số nhị phân 1, 11, 111, 1111, ...
nằm dưới một lũy thừa 2.
Máy tính par((k+1)^k)
Bây giờ chúng tôi hiểu rằng (k+1)^k
nó bị giới hạn 1,3,7,15,...
, chúng ta hãy tìm cách tính tính chẵn lẻ của các số đó. Ở đây, một thực tế hữu ích là 1,2,4,8,16,...
modulo thay thế 3
giữa 1
và 2
, kể từ đó 2==-1 mod 3
. Vì vậy, lấy 1,3,7,15,31,63...
modulo 3
đưa ra 1,0,1,0,1,0...
, chính xác là tương đương bit của họ. Hoàn hảo!
Vì vậy, chúng tôi có thể thực hiện cập nhật par(k+1) = par(k) ^ par((k+1)^k)
như
par(k+1) = par(k) ^ ((k+1)^k)%3
Sử dụng b
như biến chúng ta lưu trữ chẵn lẻ trong, điều này trông giống như
b^=((k+1)^k)%3
Viết mã
Đặt mã này cùng nhau trong mã, chúng tôi bắt đầu k
và bit chẵn lẻ b
cả hai 0
, sau đó liên tục in n=2*k+b
và cập nhật b=b^((k+1)^k)%3
và k=k+1
.
46 byte
k=b=0
exec"print 2*k+b;b^=(k+1^k)%3;k+=1;"*400
Hãy thử trực tuyến!
Chúng tôi loại bỏ dấu ngoặc xung quanh k+1
trong ((k+1)^k)%3
vì Python được ưu tiên thực hiện việc bổ sung đầu tiên dù sao, lạ vì nó trông.
Cải tiến mã
Chúng ta có thể làm tốt hơn bằng cách làm việc trực tiếp với một biến duy nhất n=2*k+b
và thực hiện các cập nhật trực tiếp trên nó. Làm k+=1
tương ứng với n+=2
. Và, cập nhật b^=(k+1^k)%3
tương ứng với n^=(k+1^k)%3
. Ở đây, k=n/2
trước khi cập nhật n
.
44 byte
n=0
exec"print n;n^=(n/2+1^n/2)%3;n+=2;"*400
Hãy thử trực tuyến!
Chúng ta có thể rút ngắn n/2+1^n/2
(hãy nhớ điều này (n/2+1)^n/2
) bằng cách viết lại
n/2+1 ^ n/2
(n+2)/2 ^ n/2
(n+2 ^ n)/2
Vì /2
loại bỏ bit cuối cùng, không có vấn đề gì nếu chúng ta làm điều đó trước hoặc sau khi xor-ing. Vì vậy, chúng tôi có n^=(n+2^n)/2%3
. Chúng ta có thể lưu một byte khác bằng cách lưu ý rằng modulo 3
, /2
tương *2
đương với -
, lưu ý rằng n+2^n
thậm chí là phân chia thực sự giảm một nửa mà không cần sàn. Điều này mang lạin^=-(n+2^n)%3
41 byte
n=0
exec"print n;n^=-(n+2^n)%3;n+=2;"*400
Hãy thử trực tuyến!
Cuối cùng, chúng ta có thể kết hợp các hoạt động n^=c;n+=2
vào n=(n+2)^c
, c
một chút. Điều này hoạt động vì ^c
chỉ hoạt động trên bit cuối cùng và +2
không quan tâm đến bit cuối cùng, vì vậy các hoạt động đi lại. Một lần nữa, ưu tiên cho phép chúng tôi bỏ qua parens và viết n=n+2^c
.
39 byte
n=0
exec"print n;n=n+2^-(n+2^n)%3;"*400
Hãy thử trực tuyến!