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+bnơi kđếm ngược 0,1,...,399và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)==0chúng ta cần b=par(k), tức là bit cuối cùng nlà 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 klê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)^kcó 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 0và tất cả 1sang bên phải của nó, tạo ra một vị trí dẫn đầu mới 0nế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 1sự thay đổi thành a 0và truyền một bit carry 1, nó tiếp tục truyền sang trái cho đến khi nó chạm 0và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 0và 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)^knó 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ế 3giữa 1và 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 bnhư 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 kvà bit chẵn lẻ bcả hai 0, sau đó liên tục in n=2*k+bvà cập nhật b=b^((k+1)^k)%3và 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+1trong ((k+1)^k)%3vì 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+bvà thực hiện các cập nhật trực tiếp trên nó. Làm k+=1tương ứng với n+=2. Và, cập nhật b^=(k+1^k)%3tương ứng với n^=(k+1^k)%3. Ở đây, k=n/2trướ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ì /2loạ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, /2tương *2đương với -, lưu ý rằng n+2^nthậ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+=2vào n=(n+2)^c, cmột chút. Điều này hoạt động vì ^cchỉ hoạt động trên bit cuối cùng và +2khô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!