Testen, ob eine Zahl ein Quadrat ist


16

Schreiben Sie ein GOLF- Assemblerprogramm, das bei einer 64-Bit-Ganzzahl ohne Vorzeichen im Register neinen Wert ungleich Null in das Register schreibt, swenn nes sich um ein Quadrat handelt, andernfalls 0in s.

Ihre GOLF- Binärdatei (nach dem Zusammenbau) muss in 4096 Bytes passen.


Ihr Programm wird mit dem folgenden Python3-Programm bewertet (das im GOLF- Verzeichnis abgelegt werden muss ):

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)

Stellen Sie sicher, dass Sie GOLF mit auf die neueste Version aktualisieren git pull. Führen Sie das Score-Programm mit python3 score.py your_source.golf.

Es verwendet einen statischen Startwert, um eine Menge von Zahlen zu generieren, von denen ungefähr 1/16 ein Quadrat ist. Die Optimierung in Richtung dieser Menge von Zahlen ist gegen den Geist der Frage, ich kann den Samen jederzeit ändern. Ihr Programm muss für alle nicht negativen 64-Bit-Eingabenummern funktionieren, nicht nur für diese.

Die niedrigste Punktzahl gewinnt.


Da GOLF sehr neu ist, füge ich hier einige Hinweise ein. Sie sollten die GOLF- Spezifikation mit allen Anweisungen und Zykluskosten lesen . Im Github-Repository finden Sie Beispielprogramme.

Kompilieren Sie zum manuellen Testen Ihr Programm durch Ausführen in eine Binärdatei python3 assemble.py your_source.golf. Führen Sie dann Ihr Programm mit aus python3 golf.py -p s your_source.bin n=42. Dies sollte das Programm mit der nEinstellung 42 starten und das Register sund die Zykluszahl nach dem Beenden ausgeben. Zeigen Sie alle Werte des Registerinhalts beim Programmende mit dem -dFlag an - verwenden Sie --helpdiese Option, um alle Flags anzuzeigen.


Ich habe eine Schleife mit 32 Iterationen abgewickelt, um ca. 64 Vorgänge pro Test zu sparen. Das liegt wahrscheinlich außerhalb des Geistes der Herausforderung. Vielleicht würde dies besser funktionieren, wenn die Geschwindigkeit durch die Codegröße geteilt wird?
Sparr

@Sparr Loop Unrolling ist erlaubt, solange Ihre Binärdatei in 4096 Bytes passt. Halten Sie dieses Limit für zu hoch? Ich bin bereit, es zu senken.
Orlp

@Sparr Deine Binärdatei ist momentan 1.3k, aber ich denke, dass das Entrollen von 32-Iterations-Schleifen ein bisschen viel ist. Wie klingt ein binäres Limit von 1024 Bytes?
Orlp

Warnung an alle Teilnehmer! Aktualisieren Sie Ihren GOLF Interpreter mit git pull. Ich habe einen Fehler im linken Operanden gefunden, bei dem der Zeilenumbruch nicht richtig war.
Orlp

Ich bin mir nicht sicher. 1024 würde nur eine Schleife von mir verlangen; Ich würde immer noch ~ 62 Operationen pro Test durch das Abrollen sparen. Ich vermute, jemand könnte so viel Platz auch als Nachschlagetabelle nutzen. Ich habe einige Algorithmen gesehen, die 2-8.000 Nachschlagetabellen für 32-Bit-Quadratwurzeln benötigen.
Sparr

Antworten:


2

Bewertung: 22120 (3414 Bytes)

Meine Lösung verwendet eine 3-KB-Nachschlagetabelle, um einen Newton-Methodenlöser zu generieren, der je nach Ergebnisgröße für null bis drei Iterationen ausgeführt wird.

    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

Ergebnis: 27462

Mit der Zeit würde ich an einer GOLF- Herausforderung teilnehmen: 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

Wenn ich Ihre Nachschlageidee klaue, sinkt meine Punktzahl von 161558 auf 47289. Ihr Algorithmus gewinnt immer noch.
Sparr

Haben Sie versucht, die Newton-Schleife zu entrollen? Wie viele Iterationen sind für den schlimmsten Fall erforderlich?
Sparr

@Sparr Ja, das Abrollen ist nicht schneller, da die Anzahl der Iterationen sehr unterschiedlich ist.
Orlp

Wird es jemals in null oder einer Iteration abgeschlossen? was ist das max?
Sparr

Die Idee der Nachschlagetabelle war auch in der Antwort stackoverflow.com/a/18686659/4339987 enthalten .
Lirtosiast

5

Ergebnis: 161558 227038 259038 260038 263068

Ich habe den schnellsten Ganzzahl-Quadratwurzel-Algorithmus genommen, den ich finden und zurückgeben konnte, ob der Rest Null ist.

# 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: Quadriertest entfernt, Rest direkt zurück, 3 Operationen pro Test sparen

BEARBEITEN 2: Verwenden Sie n direkt als Rest, speichern Sie 1 Operation pro Test

EDIT 3: Schleifenbedingung vereinfacht, 32 Operationen pro Test speichern

EDIT 4: Entrollt die Schleife, spart etwa 65 Operationen pro Test


1
Sie können vollständige Python-Ausdrücke in Anweisungen verwenden, sodass Sie schreiben können 0x4000000000000000als 1 << 62:)
orlp

3

Ergebnis: 344493

Führt eine einfache binäre Suche innerhalb des Intervalls durch, um [1, 4294967296)zu approximieren sqrt(n), und prüft dann, ob nes sich um ein perfektes Quadrat handelt.

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

Schöne Ausgangsantwort! Haben Sie Feedback zur Programmierung in GOLF Baugruppe, zu den Tools, die ich für GOLF erstellt habe , oder zur Herausforderung? Diese Art der Herausforderung ist sehr neu und ich bin gespannt auf Feedback :)
orlp

Ihre Antwort ist abgehört für n = 0 leider 0 ist 0 im Quadrat :)
Orlp

@orlp fest für n = 0. Außerdem würde ich vorschlagen, eine Anweisung hinzuzufügen, um den Wert eines Registers während der Ausführung zu drucken, was das Debuggen von GOLF- Programmen erleichtern könnte.
Es1024

Ich werde eine solche Anweisung nicht hinzufügen (das würde bedeuten, dass Herausforderungen zusätzliche Regeln für nicht zulässige Debuganweisungen hinzufügen müssen), sondern ich plane ein interaktives Debugging mit Haltepunkten und der Anzeige aller Registerinhalte.
Orlp

Sie könnten dies möglicherweise beschleunigen, indem Sie Ihre binäre Suche so gewichten, dass sie an einer anderen Stelle als dem Mittelpunkt landet. das geometrische Mittel der beiden Werte vielleicht?
Sparr
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.