Frazione più vicina


24

Compito:

Al tuo programma viene data una frazione semplice , corretta , positiva nel formato .<numerator>/<denominator>

Per questo input, deve trovare due frazioni.

  1. Una frazione che è inferiore all'input.
  2. Una frazione che è maggiore dell'input.

Entrambe le frazioni devono avere un denominatore inferiore rispetto all'input. Di tutte le possibili frazioni, dovrebbero avere la differenza più bassa nell'input.

Produzione:

L'output del tuo programma deve essere:

  • Una frazione che è più piccola dell'input, nel formato <numerator>/<denominator>.
  • Seguito da un carattere spazio (codice ASCII 32).
  • Seguita da una frazione maggiore dell'input, nel formato <numerator>/<denominator>.

Come segue:

«fraction that is < input» «fraction that is > input»

Regole:

  • Tutte le frazioni emesse devono essere nei termini più bassi .
  • Tutte le frazioni emesse devono essere frazioni appropriate.
  • Se non sono possibili frazioni appropriate consentite dalle regole, è necessario produrre 0invece di una frazione <input e 1invece di una frazione> input.
  • È possibile scegliere se si desidera ricevere la frazione come argomento della riga di comando (ad esempio yourprogram.exe 2/5) o richiedere l'input dell'utente.
  • Puoi presumere che il tuo programma non riceverà input non validi.
  • Vince il codice più breve (in byte, in qualsiasi lingua).
  • Qualsiasi argomento non standard della riga di comando (argomenti che normalmente non sono richiesti per eseguire uno script) conta per il conteggio totale dei caratteri.

  • Cosa non deve fare il tuo programma :

    • Dipende da qualsiasi risorsa esterna.
    • Dipende dall'avere un nome file specifico.
    • Invia qualcosa di diverso da quello richiesto.
    • Ci vuole molto tempo per correre. Se il tuo programma dura più di un minuto per le frazioni con un numeratore e un denominatore a 6 cifre (ad es. 179565/987657) Sul computer di un utente domestico medio, non è valido.
    • Frazioni di output con 0come denominatore. Non puoi dividere per zero.
    • Frazioni di output con 0come numeratore. Il tuo programma deve generare 0invece di una frazione.
    • Ridurre una frazione immessa. Se la frazione indicata come input è riducibile, è necessario utilizzare la frazione così come viene immessa.
  • Il tuo programma non deve essere scritto in un linguaggio di programmazione per il quale non esisteva un compilatore / interprete disponibile pubblicamente prima che questa sfida fosse pubblicata.

Esempi:

Ingresso: 2/5
Uscita: 1/3 1/2

Ingresso: 1/2
Uscita: 0 1

Ingresso: 5/9
Uscita: 1/2 4/7

Ingresso: 1/3
Uscita: 0 1/2

Ingresso: 2/4
Uscita: 1/3 2/3

Ingresso: 179565/987657
Uscita: 170496/937775 128779/708320


1
Il tuo primo esempio non corrisponde alla specifica: entrambe le frazioni devono avere un denominatore più basso dell'input.
Howard,

1
Primo esempio, l'output dovrebbe essere 1/3 1/2.
Heiko Oberdiek,

@HeikoOberdiek Hai ragione. Fisso.
user2428118

1
Definire "computer dell'utente domestico medio". Sono accettabili 90 secondi su una macchina Intel Atom a 1,6 GHz?
John Dvorak,

2
Il tuo ultimo esempio non è corretto. La frazione di input è uguale alla prima delle frazioni di output.
DavidC,

Risposte:


3

Salvia - 119 117

x,X=map(int,raw_input().split('/'))
a=0
A=c=C=1
while C<X:exec("ab,,AB"[c*X>C*x::2]+"=c,C");c=a+b;C=A+B
print a/A,b/B

Sage è necessario solo nell'ultima riga, che si occupa dell'output. Tutto il resto funziona anche in Python.

Sostituisci raw_input()con sys.argv[1]per fare in modo che l'input venga letto da un argomento della riga di comando anziché da un prompt. Questo non cambia il conteggio dei personaggi. (Non funziona in Python senza sysprima importare .)

Questo essenzialmente costruisce in modo ricorsivo la rispettiva sequenza di Farey usando i medianti degli elementi esistenti, ma si limita a quegli elementi più vicini all'input. Da un altro punto di vista, esegue una ricerca a intervalli nidificati sulle rispettive sequenze di Farey.

Elabora correttamente tutti gli esempi in meno di un secondo sul mio computer.

Ecco una versione non golfata:

x,X = map(Integer,sys.argv[1].split('/'))
x = x/X
a = 0
c = b = 1
while c.denominator() < X:
    if c > x:
        b = c
    else:
        a = c
    c = ( a.numerator() + b.numerator() ) / ( a.denominator() + b.denominator() )
print a,b

Avevo già paura di non ricevere nuovi invii per questa taglia. Ottimo lavoro.
user2428118,

Bel trucco con il exec!
xnor

Come unica risposta inviata entro il periodo di grazia, ti conferisco la grazia. Congratulazioni.
user2428118,

Ho appena corretto un errore in uno degli esempi. Potresti voler correggere il tuo invio (anche se è passato sei mesi da quando lo hai inviato).
user2428118,

12

Python 2.7 - 138

x,y=n,d=map(int,raw_input().split('/'))
while y:x,y=y,x%y
def f(p,a=d):
 while(a*n+p)%d:a-=1
 print`(a*n+p)/d`+('/'+`a`)*(a>1),
f(-x);f(x)

Ho iniziato con l'ovvia soluzione a forza bruta, ma mi sono reso conto che, poiché l'OP voleva essere in grado di risolvere istanze con numeratori e denominatori a sei cifre in meno di un minuto, ho bisogno di una soluzione migliore rispetto a provare un trilione di possibilità. Ho trovato una formula utile sulla pagina di Wikipedia per la sequenza Farey: Se a / b, c / d sono vicini in una delle sequenze Farey, cona/b<c/d , allora b*c-a*b=1. Il ciclo while all'interno di f nel mio programma estende questo fatto a numeri non ridotti, usando gcd, che l'altro ciclo while calcola.

Ho già giocato a golf abbastanza difficile, ma mi piacerebbe sentire qualche suggerimento.

modifiche:

166-> 162: rimosso ae bdal programma esterno. Non erano necessari.
162-> 155: str()-> ``
155-> 154: Aggiunto k.
154-> 152: rimosso xdall'interno della funzione, invece l'ha passata come argomento.
152-> 150: ha dato aun valore predefinito invece di passarlo come argomento.
150-> 146: modificata l'inizializzazione di xe y.
146-> 145: rimosso k.
145-> 144: Modificato ... e ... o ... in (..., ...) [...], risparmiando così uno spazio.
144-> 138: modificato (..., ...) [...] in ... + ... * (...). Grazie a @ mbomb007.

Casi test:

2/5
1/3 1/2

1/2
0 1

2/4
1/3 2/3

179565/987657
170496/937775 128779/708320

12345678/87654321
12174209/86436891 11145405/79132382

Il penultimo test ha richiesto meno di un secondo sul mio computer, mentre l'ultimo ha richiesto circa 5-10 secondi.


Questa k=1è pura malvagità.
Evpok,

1
@Evpok: stavo cercando di far funzionare k = y = n, ma apparentemente se modifichi una variabile all'interno di una funzione, Python vuole che sia locale. Questo era l'unico modo per ottenere una variabile locale in 4 caratteri. Inoltre, poiché la frazione è positiva e corretta, il denominatore non può essere 1.
isaacg

Gli argomenti della riga di comando sono facili con Python, quindi avrebbero dovuto essere usati per l'input come indicato qui.
Alex Thornton,

1
" Puoi scegliere se desideri ricevere la frazione come argomento della riga di comando (ad esempio yourprogram.exe 2/5) o richiedere l'input dell'utente ."
isaacg,

Salva 6 caratteri:print`(a*n+p)/d`+('/'+`a`)*(a>1),
mbomb007,

5

Mathematica, 163 byte

{a,b}=FromDigits/@InputString[]~StringSplit~"/";r=Range[b-1];""<>Riffle[#~ToString~InputForm&/@(#@DeleteCases[#2[a/b*r]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}})," "]

Ciò è fortemente limitato dal requisito di input / output come input e stringhe dell'utente. Trattare con le stringhe è davvero complicato in Mathematica (almeno quando si desidera giocare a golf). Farlo in modo naturale in Mathematica (usando solo numeri interi e razionali) probabilmente lo ridurrei al 50% delle dimensioni.

Può fare numeri di 6 cifre in pochi secondi sulla mia macchina.

Leggermente più leggibile (anche se non davvero non golfizzato):

{a, b} = FromDigits /@ InputString[]~StringSplit~"/";
r = Range[b - 1];
"" <> Riffle[#~ToString~
     InputForm & /@ (#[DeleteCases[#2[a/b*r]/r, a/b]] & @@@ {{Max, 
       Floor}, {Min, Ceiling}}), " "]

Per divertirci, facendo questo "il modo naturale", cioè come una funzione che prende numeratore e denominatore e restituisce due razionali, questo è solo 84 caratteri (quindi la mia stima del 50% era in realtà abbastanza vicina):

f[a_,b_]:=#@DeleteCases[#2[a/b*(r=Range[b-1])]/r,a/b]&@@@{{Max,Floor},{Min,Ceiling}}

3

Julia - 127 125 byte

L'ho affrontato da una prospettiva matematica per evitare la necessità di loop, quindi questo codice funziona abbastanza velocemente per input di grandi dimensioni (nota: se a / b è l'input, allora a * b deve rientrare in Int64 (Int32 su sistemi a 32 bit) , altrimenti vengono generate risposte senza senso - se aeb sono entrambi espressibili in Int32 (Int16 su sistemi a 32 bit), non si verificano problemi).

AGGIORNAMENTO: Non è più necessario sovraccaricare la barra rovesciata per div, utilizzando ÷, una rete risparmiando 2 byte.

a,b=int(split(readline(),"/"));k=gcd(a,b);f=b-invmod(a÷k,b÷k);d=2b-f-b÷k;print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1))

Ungolfed:

a,b=int(split(readline(),"/")) # Read in STDIN in form a/b, convert to int
k=gcd(a,b)           # Get the greatest common denominator
f=b-invmod(a÷k,b÷k)  # Calculate the denominator of the next biggest fraction
d=2b-f-b÷k           # Calculate the denominator of the next smallest fraction
print(a*d÷b,d<2?" ":"/$d ",a*f÷b+1,"/$f"^(f>1)) # Calculate numerators and print

Idea di base: trova il più grande d e f minore di b che soddisfa ad-bc = gcd (a, b) (il più piccolo successivo) e be-af = gcd (a, b) (il più grande successivo), quindi calcola c ed e da Là. L'output risultante è c / de / f, a meno che d o f sia 1, nel qual caso viene omesso / d o / f.

È interessante notare che questo significa che il codice funziona anche per frazioni improprie positive, purché l'input non sia un numero intero (ovvero, gcd (a, b) = a).

Sul mio sistema, l'immissione 194857602/34512958303non richiede tempo percettibile per la produzione171085289/30302433084 23772313/4210525219


55552/999999Mi prova con -396/920632 486/936509.
user2428118

@ user2428118 - Sei su un sistema a 32 bit (o stai usando un Julia a 32 bit)? Ho usato "int", il che significa che su un sistema a 32 bit, utilizzerà Int32 anziché Int64. int32(55552*999999)-282630400. Per me, con quel test, ottengo 51143/920632 52025/936509- nota che i denominatori sono gli stessi e che 52025-51143 = 486 - (- 396). Aggiungerò una nota per menzionare questo problema.
Glen O,

Se vuoi assicurarti che il codice funzioni per tutti gli input di dimensione Int64, puoi sostituire "int" con "int128". Con quel cambiamento, inserendo i 1234567891234567/2145768375829475878risultati 869253326028691/1510825213275018197 365314565205876/634943162554457681. Questa modifica aggiunge solo 3 caratteri extra.
Glen O,

Sì, sto usando un computer a 32 bit. Lo proverò su una macchina a 64 bit qualche volta quando ne avrò il tempo.
user2428118

I test su un computer a 64 bit danno il risultato corretto, quindi accetto questa risposta.
user2428118

2

JavaScript, 131

Con notazione con freccia grassa e evalchiamate:

m=>{for(e=eval,n=e(m),i=p=0,q=1;++i</\d+$/.exec(m);)if(n*i>(f=n*i|0))g=f+1,p=f/i>e(p)?f+'/'+i:p,q=g/i<e(q)?g+'/'+i:q;return p+' '+q}

Lo 179565/987657stress test viene eseguito in circa 35 secondi su Firefox, molto di più su Chrome (~ 6 minuti)

Metodo più veloce e senza evale notazione freccia grassa

for(n=eval(m=prompt(a=i=p=0,b=c=d=q=1));++i<m.match(/\d+$/);)if(n*i>(f=n*i|0))g=f+1,p=f*c>i*a?(a=f)+'/'+(c=i):p,q=g*d<i*b?(b=g)+'/'+(d=i):q;alert(p+' '+q)

Lo 179565/987657stress test viene eseguito in circa 5 secondi.

Non giocato a golf:

m=prompt(); //get input
a=0; c=1; //first fraction
b=1; d=1; //second fraction
n=eval(m); //evaluate input
for (i=1; i<m.match(/\d+$/); i++) { //loop from 1 to input denominator
  f=Math.floor(n*i);
  if (n*i > f) { //if fraction not equal to simplification of input
    g=f+1; // f/i and g/i are fractions closer to input
    if (f/i>a/c) a=f, c=i;
    if (g/i<b/d) b=g; d=i; 
  }
}
alert(a+'/'+c+' '+b+'/'+d); //output values handling 0 and 1 correctly

troppo ... molto ... eval. EEK
John Dvorak,

3
Testare con 2/61/3 2/5, tuttavia, 1/3non è inferiore ma uguale a 2/6 .
user2428118

@utente2428118 risolto
Michael M.

Perché questa risposta è stata accettata così presto?
Evpok,

1
@ user2428118: Sai, puoi attendere un paio di giorni prima di accettare le soluzioni. Inoltre, questa soluzione non è più la più breve.
isaacg,

2

perl, 142 byte (155 senza CPAN)

use bare A..Z;$/="/";N=<>;D=<>;F=N/D;K=G=1;for$H(1..D){J<F&&J>E?(E,I):J>F&&J<G?(G,K):()=(J=$_/H,"$_/$H")for(Z=int F*H)..Z+1}print I||0," $K\n"

Oppure, se i moduli CPAN non sono ammessi / è necessario un codice 3-4 volte più veloce:

$/="/";$N=<>;$D=<>;$F=$N/$D;$g=$G=1;for$d(1..$D){$f<$F&&$f>$E?($E,$e):$f>$F&&$f<$G?($G,$g):()=($f=$_/$d,"$_/$d")for($z=int$F*$d)..$z+1}print$e||0," $g\n"

La prima versione impiega 9.55 secondi sul mio computer, la seconda versione 2.44 secondi.

Meno illeggibile:

($N, $D) = split(m[/], <>);
$F = $N / $D;
$G = 1;
foreach $d (1 .. $D) {
    $z = int $F * $d;
    foreach $_ ($z .. $z + 1) {
        $f = $_ / $d;
        ($f < $F && $f > $E ? ($E, $e) :
        ($f > $F && $f < $G ? ($G, $g) : ())) = ($f, "$_/$d");
    }
}
print $e || 0, ' ', $g || 1, "\n";
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.