Standardizzare un numero phinary


32

sfondo

La maggior parte delle persone qui dovrebbe avere familiarità con alcuni sistemi di base interi: decimale, binario, esadecimale, ottale. Ad esempio nel sistema esadecimale, un numero abc.de 16 rappresenterebbe

a*16^2 + b*16^1 + c*16^0 + d*16^-1 + e*16^-2

Tuttavia, si possono anche usare basi non intere, come numeri irrazionali. Una volta che tale base usa il rapporto aureo φ = (1 + √5) / 2 ≈ 1,618 ... . Questi sono definiti in modo analogo alle basi intere. Quindi un numero abc.de φ (dove da a a e sono numeri interi) rappresenterebbe

a*φ^2 + b*φ^1 + c*φ^0 + d*φ^-1 + e*φ^-2

Nota che in linea di principio una qualsiasi delle cifre potrebbe essere negativa (anche se non ci siamo abituati): rappresenteremo una cifra negativa con un segno iniziale ~. Ai fini di questa domanda ci limitiamo alle cifre da ~9a 9, in modo da poter scrivere inequivocabilmente un numero come una stringa (con tilde in mezzo). Così

-2*φ^2 + 9*φ^1 + 0*φ^0 + -4*φ^-1 + 3*φ^-2

sarebbe scritto come ~290.~43. Chiamiamo un tale numero un numero phinary .

Un numero phinary può sempre essere rappresentato in forma standard , il che significa che la rappresentazione utilizza solo cifre 1e 0, senza contenere da 11nessuna parte, e con un segno meno facoltativo per indicare che l'intero numero è negativo. (È interessante notare che ogni numero intero ha una rappresentazione finita unica in forma standard.)

Le rappresentazioni che non sono in forma standard possono sempre essere convertite in forma standard usando le seguenti osservazioni:

  1. 011 φ = 100 φ (perché φ 2 = φ + 1)
  2. 0200 φ = 1001 φ (perché φ 2 + 1 / φ = 2φ)
  3. 0 ~ 10 φ = ~ 101 φ (perché φ - 1 / φ = 1)

Inoltre:

  1. Se la cifra più significativa è ~1(con il resto del numero in forma standard), il numero è negativo e possiamo convertirlo in forma standard scambiando tutto 1e ~1, anteponendo un segno meno e applicando nuovamente le tre regole precedenti fino a quando ottenere il modulo standard.

Ecco un esempio di tale normalizzazione di (sto usando spazi aggiuntivi per cifre positive, per mantenere allineata ogni posizione delle cifre): 1~3.2~1φ

      1~3. 2~1φ         Rule:
=     0~2. 3~1φ         (3)
=    ~1~1. 4~1φ         (3)
=  ~1 0 0. 4~1φ         (3)
=  ~1 0 0. 3 0 1φ       (3)
=  ~1 0 1. 1 0 2φ       (2)
=  ~1 1 0. 0 0 2φ       (1)
=  ~1 1 0. 0 1 0 0 1φ   (2)
= - 1~1 0. 0~1 0 0~1φ   (4)
= - 0 0 1. 0~1 0 0~1φ   (3)
= - 0 0 1.~1 0 1 0~1φ   (3)
= - 0 0 0. 0 1 1 0~1φ   (3)
= - 0 0 0. 0 1 1~1 0 1φ (3)
= - 0 0 0. 0 1 0 0 1 1φ (3)
= - 0 0 0. 0 1 0 1 0 0φ (1)

Cedendo .-0.0101φ

Per ulteriori letture, Wikipedia ha un articolo molto informativo sull'argomento.

La sfida

Quindi, o altrimenti, scrivere un programma o una funzione che, data una stringa che rappresenta un numero phinary (come descritto sopra), produce la sua forma standard, senza zeri iniziali o finali. L'input non contiene necessariamente il punto phinary, ma conterrà sempre la cifra rimasta (quindi no .123). L'output deve sempre includere il punto phinary e almeno una cifra alla sua sinistra.

È possibile accettare input tramite STDIN, ARGV o argomento della funzione e restituire il risultato o stamparlo su STDOUT.

È possibile utilizzare un algoritmo diverso rispetto alla procedura sopra indicata, purché sia ​​in linea di principio corretto ed esatto per input arbitrari (validi), ovvero gli unici limiti che potrebbero potenzialmente interrompere l'implementazione dovrebbero essere limitazioni tecniche come la dimensione del built-in tipi di dati o la RAM disponibile. Ad esempio, non è consentito valutare l'input come un numero in virgola mobile e quindi selezionare avidamente le cifre, poiché si potrebbero trovare input per i quali imprecisioni in virgola mobile porterebbero a risultati errati.

Questo è il codice golf, vince la risposta più breve (in byte).

Casi test

Input       Output

1           1.
9           10010.0101
1.618       10000.0000101
1~3.2~1     -0.0101
0.~1021     0. (or -0.)
105.~2      1010.0101
~31~5.~1    -100000.1001

Ora voglio usare cifre negative nei miei numeri! 1 ~ 3 * 6 == 5 ~ 8
Aaron

Risposte:


6

Javascript (ES6) - 446 418 422 420 byte

minified:

F=s=>{D=[];z='000000000';N=t=n=i=e=0;s=(z+s.replace(/^([^.]*)$/,'$1.')+z).replace(/~/g,'-').replace(/-?\d/g,s=>((D[n++]=s/1),0));for(;i<n-3;i=j){if(p=D[j=i+1]){if(!e&&p<0){D=D.map(k=>-k);N=~N;p=-p}e=1}d=D[i];x=D[i+2];m=D[i+3];if(p<0){d--;p++;x++;e=j=0}if(p>1){d++;m++;p-=2;e=j=0}if(!d&&p*x==1){d=p;e=j=p=x=0}D[i]=d;D[i+1]=p;D[i+2]=x;D[i+3]=m}return(N?'-':'')+s.replace(/0/g,()=>D[t++]).replace(/^(0(?!\.))+|0+$/g,'')}

Allargato:

F = s => {
    D = [];
    z = '000000000';
    N = t = n = i = e = 0;
    s = (z + s.replace( /^([^.]*)$/, '$1.' ) + z).replace( /~/g, '-' ).
        replace( /-?\d/g, s => ((D[n++]=s/1),0) );

    for( ; i < n-3; i = j ) {
        if( p = D[j = i+1] ) {
            if( !e && p < 0 ) {
                D = D.map( k=>-k );
                N = ~N;
                p = -p;
            }
            e = 1;
        }
        d = D[i];
        x = D[i+2];
        m = D[i+3];

        if( p < 0 ) {
            d--;
            p++;
            x++;
            e = j = 0;
        }
        if( p > 1 ) {
            d++;
            m++;
            p-=2;
            e = j = 0;
        }
        if( !d && p*x == 1 ) {
            d = p;
            e = j = p = x = 0;
        }

        D[i] = d;
        D[i+1] = p;
        D[i+2] = x;
        D[i+3] = m;
    }

    return (N ? '-' : '') + s.replace( /0/g, ()=>D[t++] ).replace( /^(0(?!\.))+|0+$/g, '' );
}

Il codice produce una funzione Fche esegue la conversione specificata.

È un problema difficile per il golf. Numerosi casi limite si insinuano che impediscono la semplificazione del codice. In particolare, trattare con i negativi è un dolore, sia in termini di analisi che in termini di gestione logica.

Devo notare che il codice gestisce solo un "intervallo ragionevole" di input. Per estendere il dominio della funzione senza limite, è zpossibile aumentare il numero di zeri in e aumentare il limite costante del while( c++ < 99 )ciclo. L'intervallo attualmente supportato è già eccessivo per i casi di test forniti.

Output di esempio

F('1')          1.
F('9')          10010.0101
F('1~3.2~1')    -0.0101
F('0.~1021')    -0.
F('105.~2')     1010.0101
F('~31~5.~1')   -100000.1001

Il -0.non è carino, ma la risposta è ancora corretta. Posso aggiustarlo se necessario.


@ MartinBüttner: potresti, ma sarebbe difficile. Limita il numero di "passaggi" sull'input completo e ogni passaggio comprende diverse operazioni. La mia sensazione è che il numero di passaggi necessari per normalizzare qualsiasi ninput di cifra sia compreso tra ne n log(n). In ogni caso, il numero di passaggi può essere aumentato di un fattore 10 per ogni personaggio aggiunto. Anche il numero di zeri nella zcostante è un problema interessante. Sospetto che 9 sia eccessivo per qualsiasi possibile input.
COTO

@ MartinBüttner: grazie. Ho rimosso la fuga nella classe personaggio. Per quanto riguarda il $0, Javascript non lo supporta. O almeno Firefox no. : P
COTO

Ok, penso che non avrai mai bisogno di più di 7 zero iniziali come buffer, ma penso che gli zero finali saranno un po 'più difficili da stimare. Per quanto riguarda il ciclo esterno, non penso che tu ne abbia nemmeno bisogno, se lo fai solo un ciclo while (o lo integri nel ciclo interno per) e scoppi solo quando non vengono rilevate ulteriori modifiche. Immagino che le mie specifiche avrebbero potuto essere un po 'più chiare a tale riguardo, ma con "in linea di principio corretta ed esatta per input arbitrari (validi)" intendevo dire che l'unico limite teorico dovrebbe essere la dimensione dei tipi di dati incorporati / della RAM.
Martin Ender,

1
@COTO Per salvare 1 byte, puoi provare a spostare la prima parte di for( i = e = 0; i < n-3; i = j )by for(; i < n-3; i = j )e spostare le dichiarazioni in alto, N = t = n = 0;sostituendole conN = t = n = i = e = 0;
Ismael Miguel,

1
@IsmaelMiguel: jnon viene mantenuto costante per un valore di i+1. Si noti che nei tre ifblocchi jviene ripristinato 0. Quindi in qualsiasi momento dopo il primo ifblocco non può essere utilizzato come proxy per i+1. La variabile istessa non può essere aggiornata fino alla fine del ciclo (utilizzando la terza istruzione in for) poiché il suo valore viene utilizzato fino alla fine del ciclo. Ma detto ciò, forse mi manca qualcosa. Se riesci ad accorciare il codice, testarlo e verificare che funzioni ancora, pubblica una copia su pastebin.com e un link qui. Ti darò il dovuto credito nella risposta. :)
COTO

2

Haskell, 336 byte

z=[0,0]
g[a,b]|a*b<0=g[b,a+b]
g x=x<z
k![a,b,c,d]=[b,a+b,d-c+read k,c]
p('.':s)=1:0:2`drop`p s
p('~':k:s)=['-',k]!p s
p(k:s)=[k]!p s
p[]=1:0:z
[1,0]&y='.':z?y
[a,b]&y=[b,a+b]?y
x@[a,b]?y@[c,d]|x==z,y==z=""|g y='-':x?[-c,-d]|g[c-1,d]='0':x&[d,c+d]|g[c,d-1]='1':x&[d,c+d-1]|0<1=[b-a,a]?[d-c,c]
m[a,b,c,d]=[1,0]?[a*d+b*c-a*c,a*c+b*d]
f=m.p

Questo è l'algoritmo avido, ma con una rappresentazione esatta [a,b]dei numeri a + ( a , b ∈ ℤ) per evitare errori in virgola mobile. g[a,b]verifica se a + <0. Esempio di utilizzo:

*Main> f "9"
"10010.0101"
*Main> f "1~3.2~1"
"-0.0101"
*Main> f "0.~1021"
"0."
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.