Periodo della rappresentazione decimale


16

Scrivi una funzione che accetta un singolo numero intero positivo n e restituisce il periodo della rappresentazione decimale di 1 / n .

Casi test:

1 -> 1               # 1/1 = 1.0000...... = 1._0
2 -> 1               # 1/2 = 0.5000...... = 0.5_0
3 -> 1               # 1/3 = 0.3333...... = 0._3
7 -> 6               # 1/7 = 0.14285714.. = 0._142857
13 -> 6
14 -> 6
123 -> 5
345 -> 22
654 -> 108
12345 -> 822
67890 -> 120

Questo è . Non sono consentiti built-in o librerie che restituiscono direttamente il periodo. I numeri fino ad almeno 100000 dovrebbero funzionare entro un tempo ragionevole (al massimo diversi minuti).


La domanda afferma che "i numeri fino ad almeno 100000 dovrebbero funzionare entro un tempo ragionevole", ma il programma deve dare la risposta giusta per numeri più grandi di questo? O sarebbe accettabile usare un algoritmo che è accurato solo fino a 100000?
FireFly,

1
@FireFly Algorithms deve fornire la risposta corretta.
Howard,

2
Perché 1 restituirebbe 1? Vorrei pensare a 0?
Timtech,

@Timtech1.00000000000000000000000000000000000
Cruncher,

@Cruncher Oh grazie, ho capito ora.
Timtech,

Risposte:


11

APL, 19 caratteri / byte *

{(↑⍳⍨1∘↓)⌽⍵|10x*⍳⍵}

Nars2000 . La versione precedente era errata su alcuni numeri, questo dovrebbe essere giusto. L'ho controllato manualmente su tutti i numeri fino a 50.

Ancora una volta, il merito va a Ben Reich per l'idea di guardare al periodo di10^i (mod x)

Vista esplosa

{                     ⍳⍵}   generate all naturals up to the argument ⍵
                 10x*       raise 10 to each of them, with unlimited precision
              ⍵|            compute the respective remainders mod ⍵
            ⌽               reverse the list
 (  ⍳⍨    )                 (fork) find the position of the first occurrence
  ↑                         of the fist element of the list
       1∘↓                  in the remainder of the list

Esempi

      {(↑⍳⍨1∘↓)⌽⍵|10x*⍳⍵}¨1 2 3 7 13 14 123 345 654 12345 67890
1 1 1 6 6 6 5 22 108 822 120

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
*: APL può essere scritto nel proprio set di caratteri a byte singolo (legacy) che associa i simboli APL ai valori superiori di 128 byte. Pertanto, ai fini del calcolo del punteggio, un programma di N caratteri che utilizza solo caratteri ASCII e simboli APL può essere considerato lungo N byte.


Non riesco a ottenere la risposta corretta per esempio input 20. Puoi per favore verificare?
Howard,

Ho seguito gli esempi che hai pubblicato. Nel tuo esempio, 1/2 = 0,5 -> 1, quindi naturalmente 1/20 = 0,05 -> 2. Cosa ottieni?
Tobia,

La risposta corretta sarebbe 1, poiché 1/20 = 0,05_0_.
Howard,

Vedo. Dammi un po ', rivedrò la mia risposta.
Tobia,

4sembra che darebbe anche la risposta sbagliata, perché 10 != 100 (mod 4).
Peter Taylor,

7

GolfScript ( 42 27)

{:x)1\[{.10*x%}*]-1%(?)}:P;

Tempo di riferimento: 5 secondi. Codice di benchmarking:

'"The time is #{Time.now#1
}"'~ puts
[1 2 3 7 13 14 123 345 654 12345 67890 99991]{[P]p}%
'"The time is #{Time.now#2
}"'~ puts

Ringraziamo Ben Reich per l'idea centrale di guardare al periodo di 10^i (mod x).

Spiegazione

Il periodo pè definito come il numero intero positivo più piccolo tale che per tutto ciò che è sufficientemente grande iabbiamo frac(10^i * 1/x) = frac(10^(i+p) * 1/x). Possiamo semplificare leggermente questo frac(10^i / x) = frac(10^(i+p) / x). Ora, frac(a / x) = frac(b / x)iff a == b (mod x), quindi stiamo cercando il numero intero positivo più piccolo tale che per tutti sufficientemente grandi i:10^i == 10^(i+p) (mod x) .

Supponiamo 10^i == 10^(i+p) (mod x). Poi10^(i+1) == 10 * 10^i == 10 * 10^(i+p) == 10^(i+p+1) (mod x) ; quindi una volta che riceviamo una ripetizione, siamo in un ciclo indistruttibile.

Ci sono solo xvalori distinti (mod x), quindi per il principio del buco del piccione dobbiamo ottenere una ripetizione nei primi x + 1valori di 10^i (mod x).

Quindi, ciò che il codice sopra fa è calcolare x + 2 valori di 10^i (mod x)*. Quindi l'ultimo è garantito per essere una ripetizione e, invertendo l'elenco e cercandolo, posso trovare l'occorrenza più recente. Inoltre, poiché sto solo facendo una ricerca, questo è il tempo pseudolinear.

* Quello in più è gestire il caso speciale x = 1, perché non riduco 10^0 (mod x)e quindi cercherò un 0in [1].


Eccezionale! Ho cancellato la mia risposta da una soluzione migliore! -
Ben Reich,

7

Golfscript - 26 byte

{:i.)+.,{;10*i%.}%i>|,}:f;

Modifica: aggiornato all'output 1se termina il decimale, anziché la lunghezza della rappresentazione decimale.

Una versione abbastanza efficiente. Il valore 67890 viene eseguito in circa 10 secondi e 99991 circa 20 secondi. È un po 'più lento di prima (circa la metà della velocità), perché l'intervallo iterato è stato raddoppiato, la prima metà è stata ignorata.

Alternativa, anche 26 byte

{:i.)+.n*{*i%.}%i>)^^,}:f;

Questo funziona ripetendo la stringa "\n"*(2*i+1), dove iè il valore passato alla funzione. Il valore passato al blocco ogni volta è il valore ordinale di "\n", che è 10 .

Il )^^è un po 'un work-around. Quando si annulla la rimozione di un carattere da una stringa, il risultato è il valore ordinale del carattere rimosso, come menzionato sopra. Tuttavia, aggiungendo nuovamente quel valore si aggiungerà la rappresentazione in forma di stringa di quel numero, piuttosto che il carattere - un comportamento abbastanza non simmetrico e, a mio avviso, un difetto di progettazione. Se davvero volessi farlo, stringere prima costerebbe solo un byte.

Una copia aggiuntiva del valore finale è già nello stack, quindi rimuovo di nuovo il valore finale ), lo xo con la stringa e quindi lo xo di nuovo, in modo che tutti i caratteri aggiunti o rimossi dal primo xor vengano ripristinati. Se int op stringfossero trattati come un personaggio, piuttosto che la sua rappresentazione di stringa, )^^potrebbero essere sostituiti da| .

Si noti che mentre le stringhe (che in Golfscript sono memorizzate come una matrice di ints) visualizzeranno il valore di ciascun carattere mod 256 , i valori di ciascun carattere potrebbero essere al di fuori di questo intervallo. Quando si esegue il test di unicità (tramite operazioni impostate) o contenimento (tramite ?), viene confrontato il valore effettivo, anziché il valore visualizzato.

Un file patch per l' attuale interprete Golfscript :

61c61
<       to_gs
---
>       Gstring.new([self])

Quanto sopra influenzerà solo il comportamento di string op int(e viceversa), dove opè uno dei
+-|&^. Tutto il resto rimane inalterato, incluso il comportamento diGint` .

La seguente soluzione a 24 byte diventerebbe quindi valida:

{:i.)+.n*{*i%.}%i>|,}:f;

E questo risolve anche molti altri aggettivi davvero brutti .


Python - 48 byte

f=lambda n:len(set(10**-~i%n for i in range(n)))

Non è la soluzione più efficiente, ma ragionevole per valori inferiori a 100000 .

FWIW, l'elemento principale è identico alla mia soluzione per generare numeri ciclici in decimale .

Una versione più efficiente dello stesso codice ( 70 byte ):

 def f(n):
  a=[];i=10%n
  while i not in a:a+=i,;i=i*10%n
  return len(a)

Il valore 99991 richiede meno di un secondo.


@PeterTaylor è orl'array su una stringa vuota. Poiché è un'operazione saggia, tutti i duplicati vengono rimossi in anticipo.
primo

Ma da dove viene la stringa vuota? Se la funzione deve essere autonoma, penso che dovrai spendere un byte in più e farlo .|.
Peter Taylor,

1
@PeterTaylor riparato .
primo

1
La modifica del comportamento di string int +interromperebbe molti programmi. Non sono sicuro di quanto spesso vengano utilizzate le altre operazioni su quella coppia di tipi.
Peter Taylor,

@PeterTaylor Sono d'accordo, lo farebbe. Ma prendere in considerazione: convertire int a char: []+''+vs ''+. Append int, come char, a stringa: []++vs +. Apend int, come rappresentazione di stringa, a stringa: +vs `+. Nella sua attuale implementazione, int''+è sinonimo di int`, che sembra dispendioso considerando la verbosità di dover forzare l'array e quindi forzare una stringa se si desidera il carattere ASCII.
primo

3

GolfScript, 48 47 46

Grazie a @PeterTaylor per aver tagliato due caratteri.

{2{1$1$%!{.@\/\d}*}:d~;5d;9{2$%}{10*9+}/+,}:f;

Ho provato a usare J, ma continuava a darmi ogni sorta di strani risultati.

Test online

Questo sostanzialmente divide 2 e 5 dal numero (2 e 5 sono i fattori primi di 10, e i loro reciproci terminano e riempiono l'algoritmo), quindi il numero intero più basso n tale che il numero risultante divide 10 ^ n - 1 è il periodo.


3
Se sai quale sarà la prima chiamata alla tua funzione, puoi incorporare la definizione lì. Vale a dire, invece di {...}:d;...dte, risparmia 1 carattere con...{...}:d~
Peter Taylor,

@PeterTaylor grazie, non ci avevo pensato
Volatilità il

1
Avendo commentato a Ben di non lasciare fin pila, noto che lo stai facendo anche tu. Dovresti davvero aggiungere a ;per far apparire la funzione per un confronto equo con altre lingue.
Peter Taylor,

2
Un'altra micro-ottimizzazione: int array ,)\;può essere abbreviata in int array +,.
Peter Taylor,

2

Perl, 52 caratteri

sub f{($p,%r)=1;1until$r{$p=$p*10%$_[0]}++;~~keys%r}

Questa è un'implementazione semplice dell'approccio diretto. (Fortunatamente anche l'approccio diretto è piuttosto efficiente: grazie all'aritmetica del modulo, la matematica non deve mai affrontare un numero superiore di 10 volte il valore di input.)

Dato che la sfida ha specificato una funzione, mi sono sentito obbligato a (ri) inizializzare le mie variabili, cosa che non mi preoccuperei di fare per un programma completo. Allo stesso modo, l' ~~istruzione in the final non è necessaria se la funzione può essere certa che verrà invocata in un contesto scalare.


Prova a inserire 20dove produce il risultato sbagliato.
Howard,

2

Clojure, 102, 117, 115, 106

unformated:

(defn r([n](r{}(iterate #(mod(* % 10)n)10)0))([a[f & s]i](if(a f)(- i(a f))(recur(assoc a f i)s(inc i)))))

formattato:

(defn r
  ([n] (r {} (iterate #(mod (* % 10) n) 10) 0))
  ([a [f & s] i]
    (if (a f)
      (- i (a f))
      (recur
        (assoc a f i)
        s
        (inc i)))))

Il tempo di esecuzione scala con il periodo. Quasi istantaneo sul mio computer per i valori di esempio.

Fondamentalmente, questo calcola il risultato della sottrazione dopo ogni passaggio in una divisione lunga. Viene rilevato un ciclo se in qualsiasi momento quel numero è uguale a quello che è stato calcolato prima di esso.


Il codice si rompe con l'input 20. Puoi per favore verificare?
Howard,

Hai ragione, la soluzione sopra è difettosa. Vedrò se riesco a risolverlo.
RedDeck vince il

Qual è l'output previsto per 20?
RedDeck vince il

La risposta corretta sarebbe 1.
Howard

Dovrebbe andare bene, il primo algoritmo fallirebbe su molti input, ad esempio 12 e 20.
RedDeckWins

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.