Тестування, якщо число є квадратом


16

Написати GOLF програму збирання , що дана 64-бітове ціле число без знака в регістрі nставить нульове значення в регістр , sякщо nє квадрат, в іншому випадку 0в s.

Ваш двійковий код GOLF (після складання) повинен містити 4096 байт.


Ваша програма буде оцінена за допомогою наступної програми Python3 (яку потрібно помістити всередині каталогу 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)

Не забудьте оновити GOLF до останньої версії с git pull. Запустіть програму за допомогою, використовуючи python3 score.py your_source.golf.

Він використовує статичне насіння, щоб генерувати набір чисел, приблизно 1/16 - квадрат. Оптимізація до цього набору чисел суперечить духу питання, я можу змінити насіння в будь-який момент. Ваша програма повинна працювати на будь-яке невід’ємне 64-бітове число введення, не тільки на це.

Виграє найнижчий рахунок.


Оскільки GOLF зовсім новий, я включу сюди декілька покажчиків. Ви повинні прочитати ГОЛЬФ специфікацію та зрозумійте всі інструкції і витрат циклу . У репозиторії Github можна знайти приклади програм.

Для тестування вручну складіть програму до двійкового файлу, запустивши python3 assemble.py your_source.golf. Потім запустіть програму, використовуючи python3 golf.py -p s your_source.bin n=42цю програму , слід запустити програму зі nзначенням 42, а sпісля виходу з друку реєструвати та кількість циклів. Переглянути всі значення вмісту регістра при виході з програми з -dпрапором - використовуйте --helpдля перегляду всіх прапорів.


Я розгорнув цикл 32 ітерації, щоб зберегти ~ 64 операції за тест. Це, мабуть, поза духом виклику. Може, це спрацює краще, оскільки швидкість поділяється на кодовий розмір?
Спарр

Розгортання циклу @Sparr дозволено, доки ваш двійковий вміст відповідає 4096 байтам. Чи вважаєте ви, що ця межа занадто висока? Я готовий знизити його.
orlp

@Sparr Ваш бінарний файл зараз - 1,3 тис., Але я думаю, що розгортання циклу з 32 ітераціями - це небагато. Як звучить двійковий ліміт 1024 байти?
orlp

Попередження усім учасникам конкурсу! Оновіть перекладач GOLF за допомогою git pull. Я знайшов помилку в операнді лівої зміни, де він не був належним чином завернутий.
orlp

Я не впевнений. 1024 вимагатиме від мене циклу лише один раз; Я б все-таки заощадив ~ 62 ops за тест, розгорнувшись. Я підозрюю, що хтось також може використати стільки місця для хорошого використання в якості таблиці пошуку. Я бачив деякі алгоритми, які хочуть 2-8k таблиць пошуку для 32-бітових квадратних коренів.
Спарр

Відповіді:


2

Оцінка: 22120 (3414 байт)

У моєму рішенні використовується таблиця пошуку 3 КБ для виведення розв'язувача методу Ньютона, який працює від нуля до трьох ітерацій залежно від розміру результату.

    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

Оцінка: 27462

Про час я змагався б у виклику 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

Якщо я вкраду вашу ідею пошуку, то моя оцінка знижується з 161558 до 47289. Ваш алгоритм все одно виграє.
Спарр

Ви спробували розкрутити цикл Ньютона? Скільки ітерацій потрібно для гіршого випадку?
Спарр

@Sparr Так, розгортатися це не швидше, оскільки велика різниця в кількості повторень.
orlp

це коли-небудь завершується нульовою чи однією ітерацією? що макс?
Спарр

Ідея таблиці пошуку також була у відповіді stackoverflow.com/a/18686659/4339987 .
lirtosiast

5

Оцінка: 161558 227038 259038 260038 263068

Я взяв найшвидший цілочисловий алгоритм квадратного кореня, який я міг знайти, і повернути його нуль.

# 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: вилучений тест квадратиків, повернення! Залишок безпосередньо, окрім 3 ops за тест

EDIT 2: використовуйте n як залишок безпосередньо, окрім 1 op за тест

EDIT 3: спрощена умова циклу, економте 32 ops за тест

EDIT 4: розгорнув цикл, заощадивши близько 65 ops за тест


1
Ви можете використовувати повні вирази Python в інструкціях, так що ви можете написати 0x4000000000000000як 1 << 62:)
orlp

3

Оцінка: 344493

Здійснює простий двійковий пошук в інтервалі [1, 4294967296)для наближення sqrt(n), а потім перевіряє, чи nє ідеальним квадрат.

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

Приємна початкова відповідь! Чи є у вас відгуки про програмування в монтажі GOLF , інструменти, які я створив для GOLF , або про проблеми? Цей тип викликів дуже новий, і я нетерплячий почути відгуки :)
orlp

Ваша відповідь помиляється на n = 0 сумно, 0 - в квадраті :)
orlp

@orlp зафіксовано для n = 0. Також я б запропонував додати інструкцію для друку значення регістра в середині виконання, що може полегшити налагодження програм GOLF .
es1024

Я не збираюсь додавати таку інструкцію (це означатиме, що завдання мають додати додаткові правила щодо заборонених інструкцій налагодження), натомість я планую інтерактивну налагодження, з точками перерви та переглядом усього вмісту реєстру.
orlp

Ви можете, можливо, прискорити це, зваживши свій двійковий пошук, щоб приземлитися десь, окрім середини. можливо, середнє геометричне значення двох значень?
Спарр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.