Nessuna ramificazione per favore


14

Chiunque sia moderatamente interessato all'ottimizzazione del codice a basso livello conosce i pericoli della ramificazione, sia che sia implementato come se dichiarazioni, cicli o dichiarazioni selettive, la possibilità di un errore nel ramo sia una cosa terribile che fa perdere tempo.

I problemi semplici possono essere risolti molto meglio con l'aritmetica semplice, quindi facciamolo.

Per i seguenti problemi, tutte le variabili sono numeri interi senza segno a 32 bit e l'unico codice consentito è costituito da semplici istruzioni che coinvolgono solo i seguenti operatori:

+ addition
- subtraction
* multiplication
/ integer division, rounds down, division by 0 not allowed
% modulo
& binary and
| binary or
^ binary exclusive or
>> bitshift right
<< bitshift left

Logic operators, return 1 if the expression is true and 0 if it is false.
== equal
!= not equal
< less than
<= less than or equal
> greater than
>= greater than or equal

Set operator
=

Ogni riga deve essere composta da un identificatore di variabile seguito da un operatore set, seguito da un'espressione.

Un'espressione non può contenere operatori di set aggiuntivi, ma può contenere identificatori di variabili, numeri letterali e parentesi.

Il punteggio del golf deve contare solo il numero di operatori.

Esempio:

myvar = ( ( ( foo + 5 ) * bar ) % 7 ) == 3

Ha un punteggio di 5 operatori.

Una soluzione può includere tutte le variabili che l'autore ritiene più adatte.
Le variabili che non sono state impostate hanno valore 0.
Overflow e underflow sono consentiti, tutti i numeri negativi sono underflow, così come lo 3 - 5sono 4294967294, anche come parte di un'istruzione più grande.

Attività 1: max

Due valori, Aed Besistono nell'ambito, fanno sì che la RESULTvariabile contenga il più grande di quei valori al termine del programma.

Compito 2: mediana

Tre valori A, Be C, presenti nell'area, rendono la RESULTvariabile contiene la mediana dei valori quando il programma termina.

Attività 3: radice quadrata

Un valore, Aesistente nell'ambito, fa sì che la RESULTvariabile contenga la radice quadrata di A, arrotondata per difetto, al termine del programma.

È giusto pubblicare una risposta solo a una o due delle domande, per alcuni di voi solo trovare soluzioni valide sarà una sfida.


Dove sono gli operatori unari? Non mi interessa -ma ~potrebbe essere carino (anche se non so per cosa).
John Dvorak,

Certo, 0xFFFF_FFFF_FFFF_FFFF ^ xe 0 - x. Come avrei potuto dimenticarlo?
John Dvorak,

@JanDvorak Ha fatto la descrizione più breve, per la logica di completezza non !è anche abbastanza banale: x == 0.
aaaaaaaaaaaa,

Qual è il comportamento della divisione per zero?
John Dvorak,

In Mathematica (a> b) restituisce True o False. Boole converte False in 0 e True in 1. È legale usare Boole[a-b]?
DavidC

Risposte:


5

Compito 3, 23 operazioni

x = (A >> 16) + A / ((A >> 13) + 511) + 15
x = (x + A/x) >> 1
x = (x + A/x) >> 1
x = (x + A/x) >> 1
RESULT = x - (x > A/x)

Usando il metodo di Newton, come fanno le altre soluzioni, con un seme scelto più delicatamente. Il primo bit A >> 16mantiene felice la parte superiore dell'intervallo, il secondo bit A / ((A >> 13) + 511)rende felice la parte centrale dell'intervallo e l'ultimo bit 15la parte inferiore e impedisce anche la divisione per zero errori (15 è il valore più grande possibile che consente 0di convergere correttamente - dimezzato tre volte meno la correzione è uguale a zero). Per i valori di input 225, 275625, 82137969, 2908768489(e valori vicini) il seme iniziale è esatto. Tutti i casi limite (quadrati perfetti, quadrati perfetti + 1 e quadrati perfetti - 1) sulla gamma 0 .. 2**32-1sono stati testati e sono corretti.

Alcuni commenti sulle regole:
overflow e underflow sono consentiti, tutti i numeri negativi sono underflow, quindi 3 - 5 è 4294967294, anche come parte di un'istruzione più grande .

Quest'ultimo pezzo si rivela essere una specie di killer dell'innovazione. Inizialmente ho tentato una soluzione usando una forma generalizzata del metodo di Halley , ma mi sono reso conto che non era valido data la restrizione di cui sopra. L'iterazione (applicata alle radici quadrate) è questa:

x = x * (3*A + x*x) / (A + 3*x*x)

Questa iterazione ha belle qualità che Newton non ha. Converge cubicamente (piuttosto che quadraticamente), converge dall'alto o dal basso (piuttosto che solo dall'alto), e non è sensibile a un seme scelto male (se l'iterazione di Newton viene fornita a un seme che è troppo basso, lo farà sovraccaricare notevolmente il punto di convergenza e quindi tornare indietro).

Il metodo di Newton ha anche il problema (almeno quando si tratta di numeri interi) che abbastanza spesso raggiungerà una x tale che A / x - x = 2 - in questo caso, convergerà in un valore uno maggiore della radice intera corretta, per il quale è necessario correggere; Il metodo di Halley non ha bisogno di tale correzione. Ma sfortunatamente, il valore di 3*A + x*xsarà abbastanza spesso più grande dello spazio intero a 32 bit consentito.

Ci sono una serie di altri n generalizzate th algoritmi di root, ma tutti condividono questa stessa caratteristica:

x = x + x*(v - x**n)/(v*n)
x = (x*(n+1) - x**(n+1)/v)/n
x = ((n-2)*x + (4*v*x)/(v + x**n))/n
x = x*((n+2)*v + (n-2)*x**n)/(v + x**n)/n
x = ((n-2)*x + (n*x*v)/(v + (n-1)*x**n))/(n-1)
x = ((n-2)*x + x*((n*2-1)*v + x**n)/(v + (n*2-1)*x**n))/(n-1)

x = x + 2*x*(v - x**n)/(v + x**n)/n
x = x + x*31*(v - x**n)/(10*v + 21*x**n)/n
x = x + x*561*(v - x**n)/(181*v + 380*x**n)/n
x = x + x*1153*(v - x**n)/(372*v + 781*x**n)/n

ecc. Molti di questi mostrano una convergenza cubica o quadratica. Gli ultimi quattro fanno parte di una serie di iterazioni che convergono sulla convergenza quartica. Ma nella prassi, il metodo di Newton ti fornirà ciò di cui hai bisogno con meno operazioni, a meno che non sia necessario calcolare molte centinaia di cifre.


Abbastanza bello, ma fallisce per 4294967295. Per quanto riguarda le regole, devono essere rigorose per renderlo interessante. Puoi argomentare quali premesse esatte rappresentano la migliore sfida, ma alla fine è molto più importante che le regole siano chiare e inequivocabili rispetto a ciò che esattamente consentono.
aaaaaaaaaaaa

Non penso che Halley ne sarebbe valsa la pena comunque, da una supposizione lontana migliorerà di un po 'meno di un fattore 3, Newton fa un po' meno di un fattore 2. Analogamente da una buona ipotesi Halley triplicherà l'accuratezza, Newton la raddoppierà. Quindi una iterazione di Halley vale esattamente esattamente le log(3)/log(2) ~= 1.585iterazioni di Newton.
aaaaaaaaaaaa

@eBusiness Inizialmente avevo 2 Halley con un seme scelto in modo simile per un totale di 25 operazioni - con errore quando A = 0- quindi questo è in realtà più breve. Circa 4294967295 , che era una svista: come 65536² ≡ 0 , l'iterazione di correzione non riesce a correggere. Vedrò se riesco a trovare un'alternativa.
primo

@eBusiness risolto.
primo

Radice quadrata più elegante del gruppo, bel lavoro e un distintivo di vittoria ufficiale.
aaaaaaaaaaaa,

5

65 (61) operazioni (5 + 13 + 47 (43))

Attività 1 - Max (A, B)

RESULT = A + (B - A) * (A <= B)

Questa è la soluzione ovvia. È necessario il compito, è necessario il confronto, è necessario moltiplicare il confronto con qualcosa, il multiplicando non può essere una delle variabili e il prodotto non può essere il risultato.

Task 2 - Mid (A, B, C)

RESULT = A                               \
       + (B - A) * (A > B) ^ (B <= C)    \
       + (C - A) * (A > C) ^ (C <  B)

Questo è un miglioramento rispetto alla mia precedente soluzione a 15 op, che ha condizionato tutte e tre le variabili - questo ha salvato due sottrazioni, ma ha introdotto un altro test di centralità. Il test stesso è semplice: un elemento è nel mezzo se esattamente uno dei due è sopra.

Attività 3 - sqrt (A)

X1     = 1024 + A / 2048
X2     = (X1  + A / X1 ) / 2
...
X10    = (X9 + A / X9 ) / 2
RESULT = X16 - (X16 * X16 > A)

Undici round di approssimazione di Newton. La costante magica di 1024 è già battuta da WolframW (e 512 causa la divisione per zero per a = 0 prima che a = 2 ** 32 converge), ma se possiamo definire 0/0 come zero, dieci iterazioni funzioneranno con il valore iniziale di 512. Ammetto che la mia affermazione di dieci iterazioni non è del tutto chiara, ma le rivendico ancora tra parentesi. Dovrò investigare se nove è possibile, comunque.La soluzione di WolframH è di nove iterazioni.


Penso che la prima riga del Task 3 non sia corretta: la seconda costante dovrebbe essere 4 volte la prima costante (per avere Newton "puro").
Ripristina Monica il

@WolframH Un'ipotesi iniziale migliore potrebbe spiegare perché sto sprecando cicli. Dove ti è venuto in mente il 4 *? Sembra che due iterazioni siano state raggruppate in una.
John Dvorak,

(1024 + A/1024)/2 == (512 + A/2048)(che è come X0 = 1024e quindi avviare Newton).
Ripristina Monica il

Bella soluzione al compito 1. L'uovo di Colombo.
DavidC,

@DavidCarraher, naturalmente, la soluzione corretta sarebbe MOV RESULT, A; CMP A,B; CMOVA RESULT, B;-)
John Dvorak,

5

Operatori 1: 5

RESULT = B ^ (A ^ B)*(A > B)

2: 13 operatori

RESULT = B ^ (A ^ B)*(A > B) ^ (A ^ C)*(A > C) ^ (B ^ C)*(B > C)

3: 27 operatori

g = 47|((0x00ffffff & A)>>10)|(A>>14)
r = (g + A/g)/3
r = (r + A/r)>>1
r = (r + A/r)>>1
r = (r + A/r)>>1
RESULT = r - (r*r-1>=A)

5

Compito 3, 39 Operazioni

EDIT: ultima riga modificata; vedi commenti.

Questa è un'implementazione del metodo Newthon. Testato con tutti i quadrati positivi, e anche con i quadrati positivi meno uno, e anche un milione di numeri casuali nell'intervallo da 0 a 2 ^ 32-1. Il valore apparentemente divertente di partenza è l'abbreviazione di (1022 + A/1022) / 2, che ha bisogno il minor numero di iterazioni (credo), e rende anche il RESULTper la A=0destra (che non sarebbe il caso di 1024posto di 1022).

r = (511 + A/2044)
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
r = (r + A/r) / 2
RESULT = r - (r > A/r)

Devo conservare la mia copia inferiore del metodo di Newton che è stato ottimizzato in parallelo al tuo e pubblicato un discreto periodo di tempo dopo? Le grandi menti pensano allo stesso modo e avere la soluzione divisa in due due risposte è un male, ma è quello che è lo stato attuale delle cose, poiché non hai risposto al secondo.
John Dvorak,

@JanDvorak: Grazie per avermelo chiesto. Va bene se metti il ​​mio metodo leggermente più breve nella tua risposta. Inoltre, grazie per avermi dato credito :-)
Ripristina Monica il

Davvero un bel tentativo, ma non riesce per input da 4294965360 a 4294967295.
aaaaaaaaaaaa

@eBusiness: quale risultato ottieni per quegli input? Ottengo 65535 nei miei test, il che è OK.
Ripristina Monica il

Ottengo 65536. Forse non usi il formato intero prescritto.
aaaaaaaaaaaa
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.