Okres reprezentacji dziesiętnej


16

Napisz funkcję, która przyjmuje pojedynczą dodatnią liczbę całkowitą n i zwraca okres dziesiętnej reprezentacji 1 / n .

Przypadki testowe:

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

To jest . Wbudowane biblioteki lub biblioteki zwracające okres bezpośrednio nie są dozwolone. Liczby do co najmniej 100000 powinny działać w rozsądnym czasie (maksymalnie kilka minut).


Pytanie brzmi: „liczby do co najmniej 100 000 powinny zadziałać w rozsądnym czasie”, ale czy program musi udzielić prawidłowej odpowiedzi dla liczb większych niż to? Czy też dopuszczalne byłoby użycie algorytmu o dokładności do 100000?
FireFly,

1
Algorytmy @FireFly muszą zawierać poprawną odpowiedź.
Howard

2
Dlaczego 1 zwraca 1? Myślałbym 0?
Timtech

@Timtech1.00000000000000000000000000000000000
Cruncher

@Cruncher Och, dziękuję, rozumiem teraz.
Timtech

Odpowiedzi:


11

APL, 19 znaków / bajtów *

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

Nars2000 . Poprzednia wersja była błędna w przypadku niektórych liczb, powinna być poprawna. Sprawdziłem go ręcznie dla wszystkich numerów do 50.

Ponownie, uznanie należy się Benowi Reichowi za pomysł spojrzenia na okres10^i (mod x)

Widok rozstrzelony

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

Przykłady

      {(↑⍳⍨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 może być zapisany we własnym (starszym) jednobajtowym zestawie znaków, który odwzorowuje symbole APL na górne 128 bajtów. Dlatego do celów oceniania program N znaków, który używa tylko znaków ASCII i symboli APL, można uznać za N-bajtowy.


Nie mogę uzyskać poprawnej odpowiedzi np 20. Na dane wejściowe . Czy możesz to zweryfikować?
Howard

Postępowałem zgodnie z opublikowanymi przez ciebie przykładami. W twoim przykładzie 1/2 = 0,5 -> 1, więc naturalnie 1/20 = 0,05 -> 2. Co otrzymujesz?
Tobia,

Prawidłowa odpowiedź to 1, ponieważ 1/20 = 0,05_0_.
Howard

Widzę. Daj mi trochę, zrewiduję swoją odpowiedź.
Tobia,

4wydaje się, że dałoby to również złą odpowiedź, ponieważ 10 != 100 (mod 4).
Peter Taylor

7

GolfScript ( 42 27)

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

Czas testu: 5 sekund. Kod porównawczy:

'"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

Podziękowania dla Bena Reicha za podstawową ideę spojrzenia na okres 10^i (mod x).

Wyjaśnienie

Okres pjest definiowany jako najmniejsza dodatnia liczba całkowita taka, że ​​dla wszystkich wystarczająco dużych imamy frac(10^i * 1/x) = frac(10^(i+p) * 1/x). Możemy to nieco uprościć frac(10^i / x) = frac(10^(i+p) / x). Teraz, frac(a / x) = frac(b / x)IFF a == b (mod x), więc szukamy najmniejszej liczby całkowitej dodatniej takie, że dla wszystkich wystarczająco duża i: 10^i == 10^(i+p) (mod x).

Załóżmy 10^i == 10^(i+p) (mod x). Wtedy 10^(i+1) == 10 * 10^i == 10 * 10^(i+p) == 10^(i+p+1) (mod x); więc kiedy otrzymamy powtórzenie, jesteśmy w niezniszczalnym cyklu.

Istnieją tylko xodrębne wartości (mod x), więc zgodnie z zasadą szufladki musimy uzyskać powtórzenie w pierwszych x + 1wartościach 10^i (mod x).

Tak więc powyższy kod oblicza x + 2wartości 10^i (mod x)*. Wtedy na pewno będzie to powtórzenie, a odwracając listę i szukając jej, mogę znaleźć ostatnie wystąpienie. Ponadto, ponieważ wykonuję tylko jedno wyszukiwanie, jest to czas pseudoliniowy.

* Dodatkowym jest załatwienie specjalnego przypadku x = 1, ponieważ nie zmniejszam, 10^0 (mod x)więc szukałbym 0w [1].


Niesamowite! Usunąłem odpowiedź, ponieważ jest to lepsze rozwiązanie! -
Ben Reich,

7

Golfscript - 26 bajtów

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

Edycja: zaktualizowane, aby wyświetlało dane wyjściowe, 1jeśli liczba dziesiętna zakończy się, a nie długość reprezentacji dziesiętnej.

Dość wydajna wersja. Wartość 67890 działa w około 10 sekund, a 99991 około 20 sekund. Jest nieco wolniejszy niż wcześniej (mniej więcej o połowę szybciej), ponieważ iterowany zasięg został podwojony, z czego pierwsza połowa jest ignorowana.

Alternatywnie, również 26 bajtów

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

Ten działa poprzez iterowanie ciągu "\n"*(2*i+1), gdzie ijest wartość przekazywana do funkcji. Wartość przekazana w każdym bloku jest numerem porządkowym wartość "\n", która jest 10 .

To )^^jest trochę obejścia. Kiedy odczytujesz znak z ciągu, wynikiem jest wartość porządkowa usuniętego znaku, jak wspomniano powyżej. Jednak ponowne dołączenie tej wartości spowoduje dodanie reprezentacji ciągu tej liczby zamiast znaku - dość niesymetryczne zachowanie, a moim zdaniem wada projektowa. Jeśli tak naprawdę chciałbyś to zrobić, najpierw strajkowanie kosztowałoby tylko jeden bajt.

Dodatkowa kopia wartości końcowej znajduje się już na stosie, więc ponownie )usuwam wartość końcową , xor ją za pomocą łańcucha, a następnie xor ponownie, aby przywrócić wszystkie znaki, które zostały dodane lub usunięte przez pierwszy xor. Gdyby int op stringbył traktowany jako znak, a nie jego ciąg znaków, )^^mógłby zostać zastąpiony przez |.

Zauważ, że chociaż ciągi znaków (które w Golfscript są przechowywane jako tablica liczb całkowitych) będą wyświetlać wartość każdego znaku mod 256 , wartości każdego znaku mogą być poza tym zakresem. Podczas testowania niepowtarzalności (za pomocą operacji ustawiania) lub ograniczania (za pośrednictwem ?) porównywana jest rzeczywista wartość, a nie wartość wyświetlana.

Plik łatki dla bieżącego interpretera Golfscript :

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

Powyższe wpłynie tylko na zachowanie string op int(i odwrotnie), gdzie opjest jedno z
+-|&^. Wszystko inne pozostaje nienaruszone, w tym zachowanie Gint`.

Następujące 24-bajtowe rozwiązanie stałoby się wtedy prawidłowe:

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

To naprawia również wiele innych naprawdę brzydkich obejść .


Python - 48 bajtów

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

Nie jest to najbardziej wydajne rozwiązanie, ale rozsądne w przypadku wartości mniejszych niż 100000 .

FWIW, rdzeń jest identyczny z moim rozwiązaniem generowania liczb cyklicznych w systemie dziesiętnym .

Bardziej wydajna wersja tego samego kodu ( 70 bajtów ):

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

Wartość 99991 zajmuje mniej niż sekundę.


@PeterTaylor to ortablica na pusty ciąg. Ponieważ jest to operacja ustalona, ​​wszystkie duplikaty są wcześniej usuwane.
primo

Ale skąd pochodzi pusty ciąg? Jeśli funkcja ma być samodzielna, myślę, że będziesz musiał wydać dodatkowy bajt i zrobić to .|.
Peter Taylor

1
@PeterTaylor naprawiony .
primo

1
Zmiana zachowania string int +spowodowałaby uszkodzenie wielu programów. Nie jestem pewien, jak często inne operacje są używane w tej parze typów.
Peter Taylor

@PeterTaylor Zgadzam się, że tak. Ale rozważyć: Konwersja int na char: []+''+vs ''+. Dołącz int, jako char, aby ciąg: []++vs +. Dołącz int, jako ciąg reprezentujący, do ciągu: +vs`+ . W obecnej implementacji int''+jest synonimem int`, co wydaje się marnotrawstwem, biorąc pod uwagę gadatliwość konieczności zmuszania do tablicy, a następnie zmuszania do łańcucha, jeśli chcesz znaku ascii.
primo

3

GolfScript, 48 47 46

Dzięki @PeterTaylor za odcięcie dwóch znaków.

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

Próbowałem użyć J, ale ciągle dawało mi to różnego rodzaju dziwne wyniki.

Przetestuj online

To zasadniczo dzieli 2 i 5 z liczby (2 i 5 są pierwszymi czynnikami 10, a ich wzajemności kończą się i upychają algorytm), a następnie najniższa liczba całkowita n taka, że ​​wynikowa liczba dzieli 10 ^ n - 1 wynosi okres.


3
Jeśli wiesz, które będzie pierwszym wywołaniem funkcji, możesz wstawić tam definicję. To znaczy zamiast {...}:d;...dciebie zaoszczędź 1 char z...{...}:d~
Peter Taylor

@PeterTaylor dzięki, nie myślałem o tym
Zmienność

1
Skomentowałem Benowi, że nie pozostawia fna stosie, zauważam, że ty też to robisz. Naprawdę powinieneś dodać funkcję ;pop, aby uzyskać uczciwe porównanie z innymi językami.
Peter Taylor

2
Kolejna mikrooptymalizacja: int array ,)\;można ją skrócić int array +,.
Peter Taylor

2

Perl, 52 znaki

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

Jest to nieskomplikowana implementacja bezpośredniego podejścia. (Na szczęście bezpośrednie podejście jest również dość wydajne: dzięki arytmetyki modulo matematyka nigdy nie musi radzić sobie z liczbą większą niż 10-krotność wartości wejściowej.)

Ponieważ wyzwanie określiło funkcję, czułem się zmuszony (ponownie) zainicjować moje zmienne, czego nie zawracałbym sobie głowy tworzeniem kompletnego programu. Podobnie, ~~w końcowej instrukcji nie jest konieczne, jeśli funkcja może być pewna, że ​​zostanie wywołana w kontekście skalarnym.


Spróbuj na wejściu, 20gdzie daje zły wynik.
Howard

2

Clojure, 102, 117, 115, 106

nieformowane:

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

sformatowany:

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

Czas pracy jest skalowany wraz z okresem. Niemal natychmiastowe na moim komputerze dla przykładowych wartości.

Zasadniczo oblicza to wynik odejmowania po każdym kroku w długim podziale. Cykl jest wykrywany, jeśli w dowolnym momencie liczba ta jest taka sama jak ta, która została obliczona przed nim.


Kod psuje się przy wprowadzaniu 20. Czy możesz to zweryfikować?
Howard

Masz rację, powyższe rozwiązanie jest wadliwe. Zobaczę, czy mogę to naprawić.
RedDeckWins

Jaka jest oczekiwana wydajność dla 20?
RedDeckWins

Prawidłowa odpowiedź to 1.
Howard

Powinien być dobry, pierwszy algorytm
zawiódłby

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.