Ini adalah pertanyaan kiat untuk bermain golf di Python tentang pertanyaan Nomor Jahat di Golf Anarki .
Angka jahat jika ekspansi binernya memiliki angka genap 1. Tantangannya adalah untuk mencetak 400 angka kejahatan pertama 0,3,5,...,795,797,798
, satu per baris.
Pengiriman Python 2 dipimpin oleh llhuii dengan solusi 42 byte. Yang terbaik berikutnya adalah 46 byte oleh mitch, diikuti oleh lima pengiriman 47 byte. Tampaknya llhuii telah menemukan sesuatu yang benar-benar ajaib yang telah menghindari banyak pegolf Python yang kuat selama lebih dari 2 tahun. Menyimpan 4 atau 5 byte sangat besar untuk golf singkat.
Saya masih di 47 byte. Saya berharap kami dapat memecahkan teka-teki ini sebagai sebuah komunitas. Jika kami mendapat jawaban bersama, saya akan mengirimkannya di bawah nama semua orang yang berkontribusi. Jawaban untuk pertanyaan ini bisa berupa kode atau ide baru atau analisis. Jika Anda llhuii, tolong jangan merusaknya untuk kami.
Meskipun pengiriman tidak diungkapkan karena masalah ini tidak ada habisnya, kami diberi beberapa petunjuk. Pengajuan pemenang membutuhkan waktu 0,1699 detik untuk berjalan, lebih lama dari yang lainnya, menunjukkan metode yang tidak efisien. Dari statistik byte, dari 42 karakter, 23 adalah alfanumerik [0-9A-Za-z]
dan 19 adalah simbol ASCII. Ini berarti tidak ada spasi putih dalam solusi llhuii.
Anda dapat menguji kode Anda pada halaman masalah , memilih Python dari dropdown bahasa atau mengunggah .py
file. Perhatikan bahwa:
- Python 2.7 digunakan
- Kode Anda harus berupa program lengkap yang dicetak
- Tidak ada input untuk masalah ini, seperti kolmogorov-kompleksitas
- Program Anda hanya perlu mencetak 400 nilai seperti yang diberikan, bahkan jika itu akan merusak nilai yang lebih besar
- Program memiliki 2 detik untuk dijalankan
- Program dapat berakhir dengan kesalahan
- Anda dapat menggunakan
exec
; "exec ditolak" mengacu pada shell exec
Jawaban:
Ini bukan solusi yang sama dengan llhuii, tetapi juga panjangnya 42 byte.
Cobalah online!
Terima kasih kepada @JonathanFrech, kami sekarang berada di 40 byte.
Cobalah online!
Ada byte lain yang harus disimpan, dengan total 39.
Cobalah online!
sumber
print+n
, solusi mereka harus berbeda dengan milikku.-
tanda dengan menggeser denganprint~n
danprint-n
dan menggunakan&
atau~
, meskipun saya belum mendapatkan apa pun untuk bekerja. Juga,n=0;exec"print n;d=n^n+2;n^=d^-d%3;"*400
cantik tapi 40 byte.print-n
tampaknya tidak mungkin karena tidak ada hubungan yang mudah antara set bitn
dan-n
.print~n
kedengarannya lebih menjanjikan secara teori, tapi saya tidak bisa mendapatkan di bawah 40 byte dengan pendekatan ini.Mendapatkan 39 byte
Ini adalah penjelasan tentang bagaimana saya mendapatkan solusi 39-byte, yang ditemukan Dennis dan JonathanFrech secara terpisah. Atau, lebih tepatnya, itu menjelaskan bagaimana seseorang bisa sampai pada jawaban di belakang, dengan cara yang jauh lebih baik daripada jalan saya yang sebenarnya untuk itu, yang penuh dengan alasan berlumpur dan jalan buntu.
Menulis ini sedikit kurang golf dan dengan lebih banyak parens, ini seperti:
Paritas bit
Kita mulai dengan sebuah ide dari solusi 47-byte saya untuk menampilkan semua angka dari formulir di
n=2*k+b
manak
dihitung0,1,...,399
danb
merupakan bit paritas yang membuat angka keseluruhan 1 genap.Mari kita menulis
par(x)
untuk bit paritas darix
, itu adalah xor (^
) semua bit dalamx
. Ini adalah 0 jika ada angka genap 1-bit (angka itu jahat), dan 1 jika ada angka ganjil 1-bit. Karenan=2*k+b
, kita haruspar(n) = par(k)^b
, untuk mencapai kejahatan yangpar(n)==0
kita butuhkanb=par(k)
, yaitu bit terakhirn
untuk menjadi bit paritas bit sebelumnya.Upaya pertama saya dalam bermain golf adalah mengekspresikan
par(k)
, pertama langsung denganbin(k).count('1')%2
, dan kemudian dengan sedikit manipulasi .Pembaruan paritas
Tetap saja, sepertinya tidak ada ekspresi pendek. Alih-alih, itu membantu untuk menyadari bahwa ada lebih banyak info untuk dikerjakan. Daripada hanya menghitung paritas bit dari angka saat ini,
kita dapat memperbarui paritas bit seperti yang kita kenaikan
k
untukk+1
.Yaitu, karena kita menghitung
k=0,1,2,...
, kita hanya perlu mempertahankan paritas bit saat ini daripada menghitungnya dari awal setiap kali. Update bit paritaspar(k+1)^par(k)
adalah paritas dari jumlah bit membalik untuk pergi darik
kek+1
, yangpar((k+1)^k)
.Bentuk dari
(k+1)^k
Sekarang kita perlu menghitung
par((k+1)^k)
. Sepertinya kita tidak mendapatkan apa-apa karena menghitung bit parity adalah masalah yang ingin kita pecahkan. Tapi, angka-angka yang dinyatakan(k+1)^k
memiliki bentuk1,3,7,15,..
, yaitu satu di bawah kekuatan 2, fakta yang sering digunakan dalam peretasan bit . Mari kita lihat mengapa begitu.Ketika kita bertambah
k
, efek dari binary carry adalah membalikkan yang terakhir0
dan semuanya1
ke kanan, menciptakan pemimpin baru0
jika tidak ada. Sebagai contoh, ambilk=43=0b101011
Kolom yang menyebabkan carry ditandai dengan
*
. Ini memiliki1
perubahan ke0
dan meneruskan sedikit carry1
, yang terus menyebar ke kiri sampai menyentuh0
dalamk
, yang berubah menjadi1
. Setiap bit lebih jauh ke kiri tidak terpengaruh. Jadi, ketikak^(k+1)
cek yang sedikit posisi berubahk
untukk+1
, ia menemukan posisi paling kanan0
dan1
's untuk yang benar. Yaitu, bit yang diubah membentuk akhiran, jadi hasilnya 0's diikuti oleh satu atau lebih 1's. Tanpa nol terkemuka, ada angka biner1, 11, 111, 1111, ...
yang satu di bawah kekuatan 2.Komputasi
par((k+1)^k)
Sekarang kita mengerti bahwa
(k+1)^k
terbatas1,3,7,15,...
, mari kita cari cara untuk menghitung bit paritas dari angka-angka tersebut. Di sini, fakta yang berguna adalah1,2,4,8,16,...
modulo alternatif itu3
antara1
dan2
, sejak2==-1 mod 3
. Jadi, mengambil memberi1,3,7,15,31,63...
modulo , yang persis paritas bit mereka. Sempurna!3
1,0,1,0,1,0...
Jadi, kita bisa melakukan pembaruan
par(k+1) = par(k) ^ par((k+1)^k)
sebagaiMenggunakan
b
sebagai variabel tempat kita menyimpan paritas, ini terlihat sepertiMenulis kodenya
Menyatukan ini dalam kode, kita mulai
k
dan paritasb
keduanya0
, kemudian berulang kali mencetakn=2*k+b
dan memperbaruib=b^((k+1)^k)%3
dank=k+1
.46 byte
Cobalah online!
Kami menghapus parens
k+1
di sekitar((k+1)^k)%3
karena prioritas Python melakukan penambahan pertama, aneh seperti yang terlihat.Perbaikan kode
Kita bisa melakukan yang lebih baik dengan bekerja secara langsung dengan satu variabel
n=2*k+b
dan melakukan pembaruan langsung di atasnya. Melakukank+=1
sesuai dengann+=2
. Dan, memperbaruib^=(k+1^k)%3
berkorespondensi dengann^=(k+1^k)%3
. Di sini,k=n/2
sebelum memperbaruin
.44 byte
Cobalah online!
Kita dapat mempersingkat
n/2+1^n/2
(ingat ini(n/2+1)^n/2
) dengan menulis ulangSejak
/2
menghapus bit terakhir, tidak masalah jika kita melakukannya sebelum atau sesudah xor-ing. Jadi, sudahn^=(n+2^n)/2%3
. Kita dapat menyimpan byte lain dengan mencatat modulo itu3
,/2
sama dengan*2
ekivalen dengan-
, mencatat bahwan+2^n
meskipun demikian pembagiannya adalah separuh sebenarnya tanpa lantai. Ini memberin^=-(n+2^n)%3
41 byte
Cobalah online!
Akhirnya, kita bisa menggabungkan operasi
n^=c;n+=2
menjadin=(n+2)^c
, di manac
ada sedikit. Ini berfungsi karena^c
hanya bertindak pada bit terakhir dan+2
tidak peduli dengan bit terakhir, jadi operasi pun jadi. Sekali lagi, diutamakan mari kita hilangkan orangtua dan menulisn=n+2^c
.39 byte
Cobalah online!
sumber
Ini memberikan solusi 47-byte (xnor) dan pemikiran yang mengarahkan saya ke sana. Jangan baca ini jika Anda ingin mencari tahu sendiri.
Gagasan pertama yang alami adalah beralih melalui angka 0 hingga 799, hanya mencetak angka-angka genap 1 dalam biner.
52 byte
Cobalah online!
Di sini,
~
dibutuhkan bit komplemen untuk beraliheven<->odd
dalam penghitungan dan memberikan nilai yang benar hanya pada penghitungan genap.Kami dapat meningkatkan metode ini dengan menghasilkan semua nilai alih-alih memfilter. Perhatikan bahwa nilai-nilai output adalah angka 0 hingga 399, masing-masing dengan sedikit ditambahkan untuk membuat jumlah 1 bit genap.
Jadi, angka
n
ke-2 adalah2*n+b
dengan salah satub=0
ataub=1
. Bitb
dapat ditemukan dengan menghitung1
dalam bitn
dan mengambil modulo 2 hitungan.49 byte
Cobalah online!
Kita dapat memotong 2 byte
2*
dengan mengulangi0,2,4,...
, yang tidak menghitung hitungan1
. Kita bisa melakukan ini dengan menggunakan satuexec
loop yang berjalan 400 kali dan bertambahn
2 setiap loop.47 byte
Cobalah online!
Dan, itu solusi 47-byte saya. Saya menduga sebagian besar jika tidak semua solusi 47-byte lainnya adalah sama.
sumber
exec
pendekatan Anda sepanjang 47 byte diizinkan?exec
tetapi baris perintahexec
.Pengajuan Python 3 llhuii
Berikut ini adalah pengiriman Python 3 untuk Nomor Jahat pada saat penulisan:
llhuii mungkin porting trik mereka ke Python 3, dan datang dengan solusi itu
Porting 47n xnor ke Python 3 secara harfiah, kami mendapatkan 50B ini:
Saya mengirimkannya sebagai
ppcg(xnor)
. (Ia menambahkan tanda kurung keexec
danprint
, yang sekarang berfungsi.) Ia memiliki statistik kode yang berbeda dari jawaban Python 3 lainnya, yang semuanya memiliki sejumlah spasi putih di dalamnya. Menarik!Namun ada cara yang lebih singkat untuk menulis ulang (
exec
cenderung kehilangan keunggulan kompetitifnya di Python 3):Ini 49 byte. Saya mengirimkannya sebagai
ppcg(xnor,alternative)
. Ini memiliki dua byte spasi, seperti jawaban llhui! Ini membuat saya percaya bahwa jawaban Python 3 llhuii terlihat seperti ini (baris baru, kemudianwhile
loop). Jadi llhuii mungkin digunakanexec
dalam Python 2 danwhile
Python 3, sama seperti kita; ini menjelaskan perbedaan spasi putih.47B kami menjadi 49B dalam Python 3. Yang menarik, sekarang, adalah bahwa 42B llhuii tidak menjadi 44B, itu menjadi 45B! Sesuatu tentang solusi llhuii membutuhkan satu byte tambahan dalam Python 3. Ini bisa berarti berbagai hal.
Hal pertama yang terlintas dalam pikiran adalah pembagian : mungkin llhuii menggunakan
/
Python 2, yang menjadi//
Python 3. (Jika mereka menghitung dalam dua-dua seperti kita, makan/2
dapat digunakan untuk beralihn
kembali ke kanan dengan satu bit?)Hal lain yang terlintas dalam pikiran adalah operator unary setelah mencetak . Kami
print blah
menjadiprint(blah)
(1 byte tambahan), tetapi jika llhuii menulis sesuatu sepertiprint~-blah
di Python 2, itu akan menjadiprint(~-blah)
di Python 3.Mungkin ada ide lain. Tolong beritahu saya.
Statistik kode untuk semua solusi Py3, termasuk milik saya sekarang:
sumber
Pendekatan lain
1) Menggunakan rumus untuk A001969
Daripada mengkonversi ke biner, dimungkinkan untuk mengambil keuntungan dari rumus berikut (dari OEIS ):
Saya sangat buruk bermain golf di Python, jadi saya bahkan tidak akan mencoba. Tapi di sini ada upaya cepat di JS.
NB: Saya tidak berpikir itu akan menjadi pengiriman JS yang valid karena hanya mengisi array tanpa menampilkannya. Dan meskipun demikian, ini 5 byte lebih lama dari solusi JS terbaik saat ini (yaitu 45 byte). Tapi toh bukan itu intinya di sini.
Tampilkan cuplikan kode
Semoga ini bisa memberi inspirasi.
Menggunakan array mungkin bukan ide yang baik karena perlu diinisialisasi dan diperbarui. Mungkin lebih efisien (ukuran kode bijaksana) untuk menggunakan fungsi rekursif sebagai gantinya, yang akan menjelaskan mengapa solusi yang menang mengambil lebih banyak waktu daripada yang lain.
2) Membangun urutan Thue-Morse dengan penggantian
Secara teori, kode ini harus berfungsi:
Cobalah online! (versi runnable terbatas hingga 20 istilah)
Itu menghitung urutan Thue-Morse dengan pergantian berturut-turut dan mencari posisi 1 (Nomor Jahat) dalam loop yang sama.
Tapi:
3) Membangun urutan Thue-Morse dengan operasi bitwise
Mulai dari Definisi Langsung Wikipedia tentang urutan Thue-Morse , saya telah sampai pada algoritma ini (beralih kembali ke JS ... maaf):
di mana kita melacak kejahatan saat ini dari urutan dalam e dan menggunakan 170 sebagai bitmask bit aneh dalam satu byte.
sumber
f=lambda n:_
for n in range(400):print f(n)
sudah butuh 43 byte. Mungkin ada cara untuk mensimulasikan rekursi dengan membangun array yang mereferensikan dirinya sendiri, atau array yang menambahkan elemen masa depan ke akhir.def
,for
,while
,lambda
(dengan parameter setidaknya), dllwhile~0:print~1
tidak membutuhkan spasi.((x=n++^n)^x/2)
tampaknya agak bertele-tele hanya untuk menemukan bit set terendah. Seluruh kekacauan itu bisa digantikan oleh++n&-n
. Cobalah online!Pendekatan penghitung bersarang
Saya punya ide untuk pendekatan yang berbeda, tapi saya tidak cukup berpengalaman dalam golf python, jadi saya akan meninggalkannya di sini untuk kalian pertimbangkan sebagai titik awal lain yang mungkin untuk bermain golf.
Gagasan yang tidak diserang:
Cobalah online!
Sembilan tingkat bersarang kedalaman, semua loop adalah sama, jadi dalam pikiranku mereka harus dibangun oleh
exec"something"*9+"deepest stuff"
. Dalam praktiknya saya tidak tahu apakah mungkin untuk melakukan hal seperti ini dengan siklus for.Hal-hal yang perlu dipertimbangkan untuk bermain golf:
mungkin ada beberapa kemungkinan lain untuk siklus dua kali selain untuk loop (saya mencoba pendekatan seperti quine dengan string yang akan dieksekusi dilewatkan ke dirinya sendiri sebagai argumen format dua kali, tapi kepala saya meledak).
mungkin juga ada alternatif yang lebih baik
if n<800:
, yang diperlukan di sini karena jika tidak kita akan terus mencetak angka jahat hingga 2 ^ 10sumber
print
adalah pernyataan, bukan fungsi, dan dengan demikian tidak dapat muncul di dalam pemahaman.print '\n'.join([[[[[[[[[foo]foo]foo]foo]foo]foo]foo]foo]foo])
str.join
hanya berfungsi pada daftar yang berisi string dan karakter daftar tambahan tidak boleh dicetak. Memformat sendiri akan membutuhkan banyak byte.Ide: Paritas bit lebih pendek
Dibutuhkan banyak karakter untuk
bin(n).count('1')%2
menghitung paritas bit-count. Mungkin cara aritmatika lebih pendek, terutama mengingat panjang bit terbatas.Cara lucu yang sama panjangnya adalah
int(bin(n)[2:],3)%2
, menafsirkan nilai biner sebagai basis3
(atau basis ganjil). Sayangnya, 4 byte dihabiskan untuk menghapus0b
awalan. Ini juga berfungsi untuk dilakukanint(bin(n)[2:])%9%2
.Gagasan lain datang dari menggabungkan bit menggunakan xor. Jika
n
memiliki representasi binerabcdefghi
, makaJadi,
r=n/16^n%16
adalah kejahatan jika dan hanya jika itun
adalah kejahatan. Kita kemudian dapat mengulangi bahwa sebagais=r/4^r%4
, nilais
di0,1,2,3
, yang1
dan2
tidak jahat, checkable dengan0<s<3
.52 byte
Cobalah online!
Ini ternyata jauh lebih lama. Ada banyak tombol untuk membalikkan bagaimana cara membagi angka, bagaimana memeriksa angka akhir (mungkin tabel pencarian berbasis bit). Saya menduga ini hanya bisa sejauh ini.
sumber
to_bytes
fungsi bilangan bulat? Saya ragu tapi ada sesuatu yang perlu dipertimbangkan :)0b
:int(bin(n),13)%2
! : Dn=0;exec"print~int(bin(n),13)%2+n;n+=2;"*400
Dengan konstruksi,
n+n^n
selalu jahat, tetapi kemampuan Python saya yang buruk hanya bisa menghasilkan solusi 61-byte:Terima kasih kepada @Peilonrayz untuk menghemat 5 byte dan @ Mr.Xcoder untuk menghemat 1 byte:
sumber
for n in sorted(n^n*2for n in range(512))[:400]:print n
.n+n^n
sama dengann^n*2
Ide: A006068 ("a (n) diberi kode Abu-abu menjadi n")
Gagasan Neil untuk menyortir semua
2n XOR n
membuat saya penasaran, jadi saya mencoba menemukan indeks di balik jenis ini. Saya menulis kode ini , dan itu mengungkapkan bahwa kita dapat menulis sesuatu seperti ini:Di mana
a(n)
A006068 (n). Cobalah online!Namun, ini mengasumsikan kita memiliki beberapa cara singkat untuk menghitung A006068. Ini sudah 38 byte, dengan asumsi kita dapat menghitungnya dalam 4 byte (
a(n)
bagian). Implementasi nyata (dalam header TIO) jauh lebih lama dari itu. Tidak banyak harapan untuk ini, saya pikir.sumber
Ide: Kurangi lebih dari XOR
Jika Anda XOR semua bit
n
bersama, itu akan0
untuk kejahatan dan1
untuk non-kejahatan. Anda dapat melakukan ini dengan fungsi rekursif (yang mungkin membutuhkan waktu lebih lama?), Seperti:Ini mengembalikan 1 untuk kejahatan.
Itu 35 byte, dan memeriksa apakah angka itu jahat atau tidak. Sayangnya,
filter
sudah 6 byte jadi ini bukan solusi yang optimal kata demi kata tetapi ide ini mungkin bisa golf.sumber
f=lambda n:n>1and f(n/2^n&1)or-~-n
untuk -1 byte.f(n/2^n&1)
mengembalikan 0 ...Metode substitusi: {1 -> {1, -1}, -1 -> {-1, 1}}
Anda juga dapat membuat substitusi ini 10 kali {1 -> {1, -1}, -1 -> {-1, 1}}, lalu ratakan dan periksa posisi 1's
di sini adalah kode Mathematica
sumber