Menguji apakah angka adalah kuadrat

16

Tulis program perakitan GOLF yang memberikan bilangan bulat unsigned 64-bit dalam register nmemberikan nilai bukan nol ke dalam register sjika nberbentuk bujur sangkar, sebaliknya 0ke dalam s.

Biner GOLF Anda (setelah perakitan) harus sesuai dengan 4096 byte.


Program Anda akan dinilai menggunakan program Python3 berikut (yang harus dimasukkan ke dalam direktori GOLF ):

import random, sys, assemble, golf, decimal

def is_square(n):
    nd = decimal.Decimal(n)
    with decimal.localcontext() as ctx:
        ctx.prec = n.bit_length() + 1
        i = int(nd.sqrt())
        return i*i == n

with open(sys.argv[1]) as in_file:
    binary, debug = assemble.assemble(in_file)

score = 0
random.seed(0)
for i in range(1000):
    cpu = golf.GolfCPU(binary)

    if random.randrange(16) == 0: n = random.randrange(2**32)**2
    else:                         n = random.randrange(2**64)

    cpu.regs["n"] = n
    cpu.run()
    if bool(cpu.regs["s"]) != is_square(n):
        raise RuntimeError("Incorrect result for: {}".format(n))
    score += cpu.cycle_count
    print("Score so far ({}/1000): {}".format(i+1, score))

print("Score: ", score)

Pastikan untuk memperbarui GOLF ke versi terbaru dengan git pull. Jalankan program skor menggunakan python3 score.py your_source.golf.

Ia menggunakan seed statis untuk menghasilkan sekumpulan angka yang kira-kira 1/16 persegi. Optimalisasi terhadap serangkaian angka ini bertentangan dengan semangat pertanyaan, saya dapat mengubah seed kapan saja. Program Anda harus bekerja untuk semua nomor input 64-bit non-negatif, bukan hanya ini.

Skor terendah menang.


Karena GOLF sangat baru, saya akan memasukkan beberapa petunjuk di sini. Anda harus membaca dengan GOLF spesifikasi dengan semua instruksi dan biaya siklus . Dalam repositori contoh program Github dapat ditemukan.

Untuk pengujian manual, kompilasi program Anda ke biner dengan menjalankan python3 assemble.py your_source.golf. Kemudian jalankan program Anda menggunakan python3 golf.py -p s your_source.bin n=42, ini harus memulai program dengan nset ke 42, dan mencetak register sdan jumlah siklus setelah keluar. Lihat semua nilai isi register di program keluar dengan -dbendera - gunakan --helpuntuk melihat semua bendera.

orlp
sumber
Saya membuka gulungan 32-iterasi untuk menghemat ~ 64 operasi per tes. Itu mungkin di luar semangat tantangan. Mungkin ini akan bekerja lebih baik karena kecepatan dibagi dengan kodeisasi?
Sparr
@Sparr Loop membuka gulungan diizinkan, selama biner Anda sesuai dengan 4096 byte. Apakah Anda merasa batas ini terlalu tinggi? Saya bersedia menurunkannya.
orlp
@Parr Biner Anda sekarang adalah 1,3 k, tapi saya pikir 32-iterasi loop membuka gulungan agak banyak. Bagaimana batas biner 1024 byte terdengar?
orlp
Peringatan untuk semua kontestan! Perbarui juru bahasa GOLF Anda dengan git pull. Saya menemukan bug di operan pergeseran kiri di mana itu tidak dibungkus dengan benar.
orlp
Saya tidak yakin. 1024 hanya akan meminta saya untuk mengulang satu kali; Saya masih akan menghemat ~ 62 ops per tes dengan membuka gulungannya. Saya menduga seseorang mungkin juga menggunakan ruang sebanyak itu untuk digunakan sebagai tabel pencarian. Saya telah melihat beberapa algoritma yang menginginkan 2-8k tabel pencarian untuk akar kuadrat 32 bit.
Sparr

Jawaban:

2

Nilai: 22120 (3414 bytes)

Solusi saya menggunakan tabel pencarian 3kB untuk seed pemecah metode Newton yang berjalan untuk nol hingga tiga iterasi tergantung pada ukuran hasilnya.

    lookup_table = bytes(int((16*n)**0.5) for n in range(2**10, 2**12))

    # use orlp's mod-64 trick
    and b, n, 0b111111
    shl v, 0xc840c04048404040, b
    le q, v, 0
    jz not_square, q
    jz is_square, n

    # x will be a shifted copy of n used to index the lookup table.
    # We want it shifted (by a multiple of two) so that the two most 
    # significant bits are not both zero and no overflow occurs.
    # The size of n in bit *pairs* (minus 8) is stored in b.
    mov b, 24
    mov x, n 
    and c, x, 0xFFFFFFFF00000000
    jnz skip32, c
    shl x, x, 32
    sub b, b, 16
skip32:
    and c, x, 0xFFFF000000000000
    jnz skip16, c
    shl x, x, 16
    sub b, b, 8
skip16:
    and c, x, 0xFF00000000000000
    jnz skip8, c
    shl x, x, 8
    sub b, b, 4
skip8:
    and c, x, 0xF000000000000000
    jnz skip4, c
    shl x, x, 4
    sub b, b, 2
skip4:
    and c, x, 0xC000000000000000
    jnz skip2, c
    shl x, x, 2
    sub b, b, 1
skip2:

    # now we shift x so it's only 12 bits long (the size of our lookup table)
    shr x, x, 52

    # and we store the lookup table value in x
    add x, x, data(lookup_table)
    sub x, x, 2**10
    lbu x, x

    # now we shift x back to the proper size
    shl x, x, b

    # x is now an intial estimate for Newton's method.
    # Since our lookup table is 12 bits, x has at least 6 bits of accuracy
    # So if b <= -2, we're done; else do an iteration of newton
    leq c, b, -2
    jnz end_newton, c
    divu q, r, n, x
    add x, x, q
    shr x, x, 1

    # We now have 12 bits of accuracy; compare b <= 4
    leq c, b, 4
    jnz end_newton, c
    divu q, r, n, x
    add x, x, q
    shr x, x, 1

    # 24 bits, b <= 16
    leq c, b, 16
    jnz end_newton, c
    divu q, r, n, x
    add x, x, q
    shr x, x, 1

    # 48 bits, we're done!

end_newton:

    # x is the (integer) square root of n: test x*x == n
    mulu x, h, x, x
    cmp s, n, x
    halt 0

is_square:
    mov s, 1

not_square:
    halt 0
2012 Arcampion
sumber
10

Nilai: 27462

Tentang waktu saya akan bersaing dalam tantangan GOLF : D

    # First we look at the last 6 bits of the number. These bits must be
    # one of the following:
    #
    #     0x00, 0x01, 0x04, 0x09, 0x10, 0x11,
    #     0x19, 0x21, 0x24, 0x29, 0x31, 0x39
    #
    # That's 12/64, or a ~80% reduction in composites!
    #
    # Conveniently, a 64 bit number can hold 2**6 binary values. So we can
    # use a single integer as a lookup table, by shifting. After shifting
    # we check if the top bit is set by doing a signed comparison to 0.

    and b, n, 0b111111
    shl v, 0xc840c04048404040, b
    le q, v, 0
    jz no, q
    jz yes, n

    # Hacker's Delight algorithm - Newton-Raphson.
    mov c, 1
    sub x, n, 1
    geu q, x, 2**32-1
    jz skip32, q
    add c, c, 16
    shr x, x, 32
skip32:
    geu q, x, 2**16-1
    jz skip16, q
    add c, c, 8
    shr x, x, 16
skip16:
    geu q, x, 2**8-1
    jz skip8, q
    add c, c, 4
    shr x, x, 8
skip8:
    geu q, x, 2**4-1
    jz skip4, q
    add c, c, 2
    shr x, x, 4
skip4:
    geu q, x, 2**2-1
    add c, c, q

    shl g, 1, c
    shr t, n, c
    add t, t, g
    shr h, t, 1

    leu q, h, g
    jz newton_loop_done, q
newton_loop:
    mov g, h
    divu t, r, n, g
    add t, t, g
    shr h, t, 1
    leu q, h, g
    jnz newton_loop, q
newton_loop_done:

    mulu u, h, g, g
    cmp s, u, n 
    halt 0
yes:
    mov s, 1
no:
    halt 0
orlp
sumber
Jika saya mencuri ide pencarian Anda maka skor saya turun dari 161558 ke 47289. Algoritma Anda masih menang.
Sparr
Sudahkah Anda mencoba membuka gulungan newton? Berapa banyak iterasi yang dibutuhkan, untuk kasus terburuk?
Sparr
@Parr Ya, ini tidak lebih cepat untuk membuka gulungan karena ada variasi tinggi dalam jumlah iterasi.
orlp
apakah pernah selesai dalam nol atau satu iterasi? apa maksnya?
Sparr
Ide tabel pencarian juga ada di jawaban stackoverflow.com/a/18686659/4339987 .
lirtosiast
5

Nilai: 161558 227038 259038 260038 263068

Saya mengambil algoritma root integer kuadrat tercepat yang dapat saya temukan dan mengembalikan apakah sisanya nol.

# based on http://www.cc.utah.edu/~nahaj/factoring/isqrt.c.html
# converted to GOLF assembly for http://codegolf.stackexchange.com/questions/49356/testing-if-a-number-is-a-square

# unrolled for speed, original source commented out at bottom
start:
    or u, t, 1 << 62
    shr t, t, 1
    gequ v, n, u
    jz nope62, v
    sub n, n, u
    or t, t, 1 << 62
    nope62:

    or u, t, 1 << 60
    shr t, t, 1
    gequ v, n, u
    jz nope60, v
    sub n, n, u
    or t, t, 1 << 60
    nope60:

    or u, t, 1 << 58
    shr t, t, 1
    gequ v, n, u
    jz nope58, v
    sub n, n, u
    or t, t, 1 << 58
    nope58:

    or u, t, 1 << 56
    shr t, t, 1
    gequ v, n, u
    jz nope56, v
    sub n, n, u
    or t, t, 1 << 56
    nope56:

    or u, t, 1 << 54
    shr t, t, 1
    gequ v, n, u
    jz nope54, v
    sub n, n, u
    or t, t, 1 << 54
    nope54:

    or u, t, 1 << 52
    shr t, t, 1
    gequ v, n, u
    jz nope52, v
    sub n, n, u
    or t, t, 1 << 52
    nope52:

    or u, t, 1 << 50
    shr t, t, 1
    gequ v, n, u
    jz nope50, v
    sub n, n, u
    or t, t, 1 << 50
    nope50:

    or u, t, 1 << 48
    shr t, t, 1
    gequ v, n, u
    jz nope48, v
    sub n, n, u
    or t, t, 1 << 48
    nope48:

    or u, t, 1 << 46
    shr t, t, 1
    gequ v, n, u
    jz nope46, v
    sub n, n, u
    or t, t, 1 << 46
    nope46:

    or u, t, 1 << 44
    shr t, t, 1
    gequ v, n, u
    jz nope44, v
    sub n, n, u
    or t, t, 1 << 44
    nope44:

    or u, t, 1 << 42
    shr t, t, 1
    gequ v, n, u
    jz nope42, v
    sub n, n, u
    or t, t, 1 << 42
    nope42:

    or u, t, 1 << 40
    shr t, t, 1
    gequ v, n, u
    jz nope40, v
    sub n, n, u
    or t, t, 1 << 40
    nope40:

    or u, t, 1 << 38
    shr t, t, 1
    gequ v, n, u
    jz nope38, v
    sub n, n, u
    or t, t, 1 << 38
    nope38:

    or u, t, 1 << 36
    shr t, t, 1
    gequ v, n, u
    jz nope36, v
    sub n, n, u
    or t, t, 1 << 36
    nope36:

    or u, t, 1 << 34
    shr t, t, 1
    gequ v, n, u
    jz nope34, v
    sub n, n, u
    or t, t, 1 << 34
    nope34:

    or u, t, 1 << 32
    shr t, t, 1
    gequ v, n, u
    jz nope32, v
    sub n, n, u
    or t, t, 1 << 32
    nope32:

    or u, t, 1 << 30
    shr t, t, 1
    gequ v, n, u
    jz nope30, v
    sub n, n, u
    or t, t, 1 << 30
    nope30:

    or u, t, 1 << 28
    shr t, t, 1
    gequ v, n, u
    jz nope28, v
    sub n, n, u
    or t, t, 1 << 28
    nope28:

    or u, t, 1 << 26
    shr t, t, 1
    gequ v, n, u
    jz nope26, v
    sub n, n, u
    or t, t, 1 << 26
    nope26:

    or u, t, 1 << 24
    shr t, t, 1
    gequ v, n, u
    jz nope24, v
    sub n, n, u
    or t, t, 1 << 24
    nope24:

    or u, t, 1 << 22
    shr t, t, 1
    gequ v, n, u
    jz nope22, v
    sub n, n, u
    or t, t, 1 << 22
    nope22:

    or u, t, 1 << 20
    shr t, t, 1
    gequ v, n, u
    jz nope20, v
    sub n, n, u
    or t, t, 1 << 20
    nope20:

    or u, t, 1 << 18
    shr t, t, 1
    gequ v, n, u
    jz nope18, v
    sub n, n, u
    or t, t, 1 << 18
    nope18:

    or u, t, 1 << 16
    shr t, t, 1
    gequ v, n, u
    jz nope16, v
    sub n, n, u
    or t, t, 1 << 16
    nope16:

    or u, t, 1 << 14
    shr t, t, 1
    gequ v, n, u
    jz nope14, v
    sub n, n, u
    or t, t, 1 << 14
    nope14:

    or u, t, 1 << 12
    shr t, t, 1
    gequ v, n, u
    jz nope12, v
    sub n, n, u
    or t, t, 1 << 12
    nope12:

    or u, t, 1 << 10
    shr t, t, 1
    gequ v, n, u
    jz nope10, v
    sub n, n, u
    or t, t, 1 << 10
    nope10:

    or u, t, 1 << 8
    shr t, t, 1
    gequ v, n, u
    jz nope8, v
    sub n, n, u
    or t, t, 1 << 8
    nope8:

    or u, t, 1 << 6
    shr t, t, 1
    gequ v, n, u
    jz nope6, v
    sub n, n, u
    or t, t, 1 << 6
    nope6:

    or u, t, 1 << 4
    shr t, t, 1
    gequ v, n, u
    jz nope4, v
    sub n, n, u
    or t, t, 1 << 4
    nope4:

    or u, t, 1 << 2
    shr t, t, 1
    gequ v, n, u
    jz nope2, v
    sub n, n, u
    or t, t, 1 << 2
    nope2:

    or u, t, 1 << 0
    shr t, t, 1
    gequ v, n, u
    jz nope0, v
    sub n, n, u
    nope0:

end:
    not s, n        # return !remainder
    halt 0


# before unrolling...
#
# start:
#     mov b, 1 << 62  # squaredbit = 01000000...
# loop:               # do {
#     or u, b, t      #   u = squaredbit | root
#     shr t, t, 1     #   root >>= 1
#     gequ v, n, u    #   if remainder >= u:
#     jz nope, v
#     sub n, n, u     #       remainder = remainder - u
#     or t, t, b      #       root = root | squaredbit
# nope:
#     shr b, b, 2     #   squaredbit >>= 2
#     jnz loop, b      # } while (squaredbit > 0)
# end:
#     not s, n        # return !remainder
#     halt 0

EDIT 1: tes kuadrat dihapus, langsung kembali! Sisanya, menghemat 3 ops per tes

EDIT 2: gunakan n sebagai sisanya secara langsung, simpan 1 op per tes

EDIT 3: menyederhanakan kondisi loop, menghemat 32 ops per tes

EDIT 4: membuka gulungannya, menghemat sekitar 65 ops per tes

Sparr
sumber
1
Anda dapat menggunakan ekspresi Python penuh dalam instruksi, sehingga Anda dapat menulis 0x4000000000000000sebagai 1 << 62:)
orlp
3

Nilai: 344493

Lakukan pencarian biner sederhana dalam interval [1, 4294967296)untuk perkiraan sqrt(n), lalu periksa apakah nkuadrat sempurna.

mov b, 4294967296
mov c, -1

lesser:
    add a, c, 1

start:
    leu k, a, b
    jz end, k

    add c, a, b
    shr c, c, 1

    mulu d, e, c, c

    leu e, d, n
    jnz lesser, e
    mov b, c
    jmp start

end:
    mulu d, e, b, b
    cmp s, d, n

    halt 0
es1024
sumber
Jawaban awal yang bagus! Apakah Anda memiliki umpan balik tentang pemrograman dalam perakitan GOLF , alat yang saya buat untuk GOLF , atau tantangan? Jenis tantangan ini sangat baru, dan saya ingin sekali mendengar umpan balik :)
orlp
Jawaban Anda disadap untuk n = 0 sayangnya, 0 adalah 0 kuadrat :)
orlp
@ orlp diperbaiki untuk n = 0. Juga, saya sarankan menambahkan instruksi untuk mencetak nilai tengah eksekusi register, yang dapat membuat debug program GOLF lebih mudah.
es1024
Saya tidak akan menambahkan instruksi seperti itu (itu berarti tantangan harus menambahkan aturan tambahan tentang instruksi debugging yang dianulir), sebagai gantinya saya telah merencanakan debugging interaktif, dengan breakpoints dan melihat semua isi register.
orlp
Anda mungkin bisa mempercepat ini dengan menimbang pencarian biner Anda untuk mendarat di tempat lain selain titik tengah. mean geometrik dari dua nilai mungkin?
Sparr