Kiểm tra nếu một số là một hình vuông


16

Viết chương trình lắp ráp GOLF có số nguyên không dấu 64 bit trong thanh ghi nsẽ đặt giá trị khác không vào thanh ghi snếu nlà hình vuông, nếu không thì 0vào s.

Nhị phân GOLF của bạn (sau khi lắp ráp) phải vừa với 4096 byte.


Chương trình của bạn sẽ được ghi bằng chương trình Python3 sau (phải được đặt trong thư mục 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)

Đảm bảo cập nhật GOLF lên phiên bản mới nhất với git pull. Chạy chương trình điểm số bằng cách sử dụng python3 score.py your_source.golf.

Nó sử dụng một hạt giống tĩnh để tạo ra một tập hợp các số trong đó khoảng 1/16 là hình vuông. Tối ưu hóa cho bộ số này là trái với tinh thần của câu hỏi, tôi có thể thay đổi hạt giống bất cứ lúc nào. Chương trình của bạn phải hoạt động với mọi số đầu vào 64 bit không âm, không chỉ các số này.

Điểm số thấp nhất chiến thắng.


Bởi vì GOLF rất mới, tôi sẽ bao gồm một số gợi ý ở đây. Bạn nên đọc các GOLF đặc điểm kỹ thuật với tất cả các hướng dẫn và các chi phí chu kỳ . Trong kho chương trình ví dụ kho lưu trữ Github có thể được tìm thấy.

Để kiểm tra thủ công, biên dịch chương trình của bạn thành nhị phân bằng cách chạy python3 assemble.py your_source.golf. Sau đó chạy chương trình của bạn bằng cách sử dụng python3 golf.py -p s your_source.bin n=42, điều này sẽ khởi động chương trình với nthiết lập là 42, và in thanh ghi svà số chu kỳ sau khi thoát. Xem tất cả các giá trị của nội dung đăng ký khi thoát chương trình bằng -dcờ - sử dụng --helpđể xem tất cả các cờ.


Tôi đã hủy vòng lặp 32 vòng để lưu ~ 64 thao tác trên mỗi bài kiểm tra. Đó có lẽ là ngoài tinh thần của thách thức. Có lẽ điều này sẽ làm việc tốt hơn khi tốc độ chia cho mã hóa?
Sparr

@Sparr Việc hủy đăng ký vòng lặp được cho phép, miễn là nhị phân của bạn phù hợp với 4096 byte. Bạn có cảm thấy giới hạn này quá cao không? Tôi sẵn sàng hạ nó xuống.
orlp

@Sparr Nhị phân của bạn ngay bây giờ là 1,3k, nhưng tôi nghĩ rằng việc không kiểm soát vòng lặp 32 lần là hơi nhiều. Làm thế nào để một giới hạn nhị phân của 1024 byte âm thanh?
orlp

Cảnh báo đến tất cả các thí sinh! Cập nhật trình thông dịch GOLF của bạn với git pull. Tôi đã tìm thấy một lỗi trong toán hạng leftshift nơi nó không được bọc đúng cách.
orlp

Tôi không chắc. 1024 sẽ chỉ yêu cầu tôi lặp một lần; Tôi vẫn sẽ tiết kiệm ~ 62 ops cho mỗi bài kiểm tra bằng cách hủy đăng ký. Tôi nghi ngờ ai đó cũng có thể sử dụng nhiều không gian đó để sử dụng tốt như một bảng tra cứu. Tôi đã thấy một số thuật toán muốn 2-8k bảng tra cứu cho căn bậc hai 32 bit.
Sparr

Câu trả lời:


2

Điểm: 22120 (3414 byte)

Giải pháp của tôi sử dụng bảng tra cứu 3kB để chọn một bộ giải phương thức của Newton chạy từ 0 đến 3 lần lặp tùy thuộc vào kích thước của kết quả.

    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

10

Điểm: 27462

Về thời gian tôi sẽ cạnh tranh trong một thử thách 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

Nếu tôi đánh cắp ý tưởng tra cứu của bạn thì điểm của tôi giảm từ 161558 xuống còn 47289. Thuật toán của bạn vẫn thắng.
Sparr

Bạn đã thử bỏ kiểm soát vòng lặp newton? Cần bao nhiêu lần lặp, trong trường hợp xấu nhất?
Sparr

@Sparr Có, nó không nhanh hơn để hủy đăng ký vì có số lần lặp lại cao.
orlp

nó có bao giờ hoàn thành trong 0 hoặc một lần lặp không? tối đa là gì?
Sparr

Ý tưởng bảng tra cứu cũng nằm trong câu trả lời stackoverflow.com/a/18686659/4339987 .
lirtosiast

5

Điểm: 161558 227038 259038 260038 263068

Tôi lấy thuật toán căn bậc hai nhanh nhất mà tôi có thể tìm và trả về cho dù phần còn lại của nó bằng không.

# 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: loại bỏ kiểm tra bình phương, trả lại trực tiếp! Phần còn lại, lưu 3 op mỗi bài kiểm tra

EDIT 2: sử dụng n làm phần còn lại trực tiếp, tiết kiệm 1 op cho mỗi bài kiểm tra

EDIT 3: đơn giản hóa điều kiện vòng lặp, lưu 32 ops mỗi lần kiểm tra

EDIT 4: không kiểm soát vòng lặp, lưu khoảng 65 op mỗi bài kiểm tra


1
Bạn có thể sử dụng các biểu thức Python đầy đủ trong hướng dẫn, vì vậy bạn có thể viết 0x4000000000000000dưới dạng 1 << 62:)
orlp

3

Điểm: 344493

Liệu một tìm kiếm nhị phân đơn giản trong khoảng [1, 4294967296)để gần đúng sqrt(n), sau đó kiểm tra xem ncó phải là một hình vuông hoàn hảo không.

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

Câu trả lời bắt đầu tốt đẹp! Bạn có phản hồi nào về lập trình trong lắp ráp GOLF , các công cụ tôi tạo cho GOLF hoặc thử thách không? Loại thử thách này rất mới và tôi rất muốn nghe phản hồi :)
orlp

Câu trả lời của bạn bị lỗi vì n = 0 thật đáng buồn, 0 là 0 bình phương :)
orlp

@orlp đã sửa cho n = 0. Ngoài ra, tôi khuyên bạn nên thêm một lệnh để in thực thi giữa giá trị của người đăng ký, điều này có thể giúp việc gỡ lỗi chương trình GOLF dễ dàng hơn.
es1024

Tôi sẽ không thêm một hướng dẫn như vậy (điều đó có nghĩa là các thách thức phải thêm các quy tắc bổ sung về hướng dẫn gỡ lỗi không được phép), thay vào đó tôi đã lên kế hoạch gỡ lỗi tương tác, với các điểm dừng và xem tất cả nội dung đăng ký.
orlp

bạn có thể tăng tốc độ này bằng cách cân nhắc tìm kiếm nhị phân của bạn để hạ cánh ở đâu đó ngoài điểm giữa. ý nghĩa hình học của hai giá trị có lẽ?
Sparr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.