Período da representação decimal

16

Escreva uma função que use um único número inteiro positivo n e retorne o período da representação decimal de 1 / n .

Casos de teste:

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

Isso é . Built-ins ou bibliotecas que retornam o período diretamente não são permitidos. Números de até pelo menos 100000 devem funcionar dentro de um prazo razoável (no máximo vários minutos).

Howard
fonte
A pergunta afirma que "números de até pelo menos 100000 devem funcionar dentro de um prazo razoável", mas o programa precisa fornecer a resposta certa para números maiores que isso? Ou seria aceitável usar um algoritmo com precisão de até 100000?
FireFly
11
Os algoritmos do @FireFly devem fornecer a resposta correta.
Howard
2
Por que 1 retornaria 1? Eu pensaria 0?
Timtech 30/01
@Timtech1.00000000000000000000000000000000000
Cruncher
@ Cruncher Oh, obrigado, eu entendi agora.
Timtech 30/01

Respostas:

11

APL, 19 caracteres / bytes *

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

Nars2000 . A versão anterior estava errada em alguns números, isso deveria estar certo. Eu verifiquei manualmente em todos os números até 50.

Mais uma vez, Ben Reich dá crédito à idéia de observar o período de10^i (mod x)

Vista expandida

{                     ⍳⍵}   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

Exemplos

      {(↑⍳⍨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 pode ser escrito na sua própria (legado) de conjunto de caracteres de byte único que mapeia símbolos APL para os 128 valores de bytes superiores. Portanto, para fins de pontuação, um programa de N caracteres que usa apenas caracteres ASCII e símbolos APL pode ser considerado como N bytes.

Tobia
fonte
Não consigo obter a resposta correta para, por exemplo, entrada 20. Você pode verificar?
276 Howard Howard
Eu segui os exemplos que você postou. No seu exemplo, 1/2 = 0,5 -> 1, então naturalmente 1/20 = 0,05 -> 2. O que você está recebendo?
Tobia 02/02
A resposta correta seria 1, pois 1/20 = 0,05_0_.
305 Howard Howard
Entendo. Me dê um pouco, vou revisar minha resposta.
Tobia
4parece que daria a resposta errada também, porque 10 != 100 (mod 4).
31413 Peter Peter Taylor
7

GolfScript ( 42 27)

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

Tempo de referência: 5 segundos. Código de 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

Agradecemos a Ben Reich pela idéia central de observar o período de 10^i (mod x).

Explicação

O período pé definido como o menor inteiro positivo tal que para todo suficientemente grande ique temos frac(10^i * 1/x) = frac(10^(i+p) * 1/x). Podemos simplificar isso levementefrac(10^i / x) = frac(10^(i+p) / x) . Agora, frac(a / x) = frac(b / x)se e somente se a == b (mod x), por isso estamos procurando o menor inteiro positivo tal que para todo suficientemente grande i: 10^i == 10^(i+p) (mod x).

Suponha 10^i == 10^(i+p) (mod x). Então 10^(i+1) == 10 * 10^i == 10 * 10^(i+p) == 10^(i+p+1) (mod x); então, quando temos uma repetição, estamos em um ciclo inquebrável.

Tem apenas x valores distintos (mod x), portanto, pelo princípio do buraco de pombo, devemos obter uma repetição nos primeiros x + 1valores de 10^i (mod x).

Então, o que o código acima faz é calcular x + 2valores de 10^i (mod x)*. Então, o último é garantido como uma repetição e, revertendo a lista e pesquisando, posso encontrar a ocorrência mais recente. Além disso, como só estou fazendo a pesquisa, é hora de pseudo-tempo.

* O único extra é para lidar com o caso especial x = 1, porque eu não reduzem 10^0 (mod x)e assim eu estaria à procura de um 0em [1].

Peter Taylor
fonte
Impressionante! Excluí minha resposta desde uma solução melhor! -
Ben Reich
7

Golfscript - 26 bytes

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

Editar: atualizado para a saída 1se o decimal terminar, em vez do comprimento da representação decimal.

Uma versão bastante eficiente. O valor 67890 é executado em aproximadamente 10 segundos e 99991 em cerca de 20 segundos. É um pouco mais lento do que era antes (aproximadamente metade da velocidade), porque o intervalo iterado foi duplicado, a primeira metade do qual é ignorada.

Alternativa, também 26 bytes

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

Este funciona iterando sobre a string "\n"*(2*i+1), onde ié o valor passado para a função. O valor passado para o bloco de cada vez é o valor ordinal de "\n", que é 10 .

O )^^é um pouco de uma solução alternativa. Quando você desmarca um caractere de uma sequência, o resultado é o valor ordinal do caractere removido, conforme mencionado acima. No entanto, anexar esse valor novamente anexará a representação de string desse número, em vez do caractere - comportamento bastante não simétrico e, na minha opinião, uma falha de design. Se você realmente quisesse fazer isso, a string primeiro custaria apenas um byte.

Uma cópia extra do valor final já está na pilha, portanto, removo o valor final novamente ), xou-o com a string e, em seguida, xor novamente, para que todos os caracteres adicionados ou removidos pelo primeiro xor sejam restaurados. Se int op stringfosse tratado como um caractere, em vez de sua representação em cadeia, )^^poderia ser substituído por| .

Observe que, enquanto as strings (que no Golfscript são armazenadas como uma matriz de ints) exibem o valor de cada caractere mod 256 , os valores de cada caractere podem estar fora desse intervalo. Ao testar a exclusividade (via operações definidas) ou a contenção (via ?), é o valor real que é comparado, em vez do valor de exibição.

Um arquivo de correção para o atual intérprete Golfscript :

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

O exemplo acima afetará apenas o comportamento de string op int(e vice-versa), onde opé um dos
+-|&^. Todo o resto permanece inalterado, incluindo o comportamento deGint` .

A seguinte solução de 24 bytes se tornaria válida:

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

E isso também corrige muitas outras soluções realmente feias .


Python - 48 bytes

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

Não é a solução mais eficiente, mas é razoável para valores inferiores a 100000 .

FWIW, o elemento principal é idêntico à minha solução para Gerar números cíclicos em decimal .

Uma versão mais eficiente do mesmo código ( 70 bytes ):

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

O valor 99991 leva menos de um segundo.

primo
fonte
@PeterTaylor oré o array em uma string vazia. Por ser uma operação em conjunto, todas as duplicatas são removidas previamente.
Primo
Mas de onde vem a corda vazia? Se a função for independente, acho que você precisará gastar um byte extra e conseguir .|.
Peter Taylor
11
@PeterTaylor fixed .
primo
11
Mudar o comportamento de string int +quebraria muitos programas. Não sei com que frequência as outras operações são usadas nesse par de tipos.
Peter Taylor
@ PeterTaylor eu concordo, seria. Mas considere: converter int para Char: []+''+vs ''+. Anexar int, como char, a string: []++vs +. Apend int, como representação de cadeia, a cadeia: +vs `+. Na sua implementação atual, int''+é sinônimo de int`, o que parece um desperdício, considerando a verbosidade de ter que coagir à matriz e, em seguida, coagir a uma string, se você quiser o caractere ascii.
primo
3

GolfScript, 48 47 46

Graças a @ PeterTaylor por cortar dois caracteres.

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

Eu tentei usar J, mas ele me deu todos os tipos de resultados estranhos.

Teste on-line

Isso basicamente divide 2 e 5 do número (2 e 5 são os fatores primos de 10, e seus recíprocos terminam e enchem o algoritmo), então o número inteiro mais baixo n, de modo que o número resultante divida 10 ^ n - 1 é o período.

Volatilidade
fonte
3
Se você souber qual será a primeira chamada para sua função, poderá incorporar a definição lá. Ou seja, em vez de {...}:d;...dvocê salvar 1 caractere com #...{...}:d~
4405 Peter Taylor
@PeterTaylor graças, não tinha pensado nisso
Volatilidade
11
Tendo comentado com Ben sobre não deixar fa pilha, percebo que você também está fazendo isso. Você realmente deve adicionar um ;para exibir a função para uma comparação justa com outros idiomas.
Peter Taylor
2
Outra micro-otimização: int array ,)\;pode ser reduzida para int array +,.
Peter Taylor
2

Perl, 52 caracteres

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

Esta é uma implementação descomplicada da abordagem direta. (Felizmente, a abordagem direta também é bastante eficiente: graças à aritmética do módulo, a matemática nunca precisa lidar com um número superior a 10 vezes o valor de entrada.)

Como o desafio especificou uma função, senti-me compelido a (re) inicializar minhas variáveis, algo que não me incomodaria em fazer por um programa completo. Da mesma forma, a ~~declaração final é desnecessária se a função puder ter certeza de que será invocada em um contexto escalar.

caixa de pão
fonte
Experimente a entrada 20onde ela produz o resultado errado.
Howard
2

Clojure, 102, 117, 115106

não formatado:

(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)))))

formatado:

(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)))))

O tempo de execução é escalado com o período. Quase instantâneo no meu computador para os valores da amostra.

Basicamente, isso calcula o resultado da subtração após cada etapa na divisão longa. Um ciclo é detectado se, a qualquer momento, esse número for o mesmo que foi calculado antes dele.

RedDeckWins
fonte
O código quebra com a entrada 20. Você pode verificar?
Howard
Você está certo, a solução acima está com defeito. Vou ver se consigo consertar.
RedDeckWins
Qual é o resultado esperado para 20?
RedDeckWins
A resposta correta seria 1.
Howard
Deve ser bom para ir, primeiro algoritmo falharia em lotes de entradas, por exemplo 12 e 20.
RedDeckWins