Період десяткового подання


16

Напишіть функцію, яка приймає єдине додатне ціле число n і повертає період десяткового подання 1 / n .

Тестові приклади:

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

Це . Вбудовані або бібліотеки, які безпосередньо повертають період, не дозволяються. Числа до не менше 100000 повинні працювати протягом розумного часу (щонайбільше декількох хвилин).


У запитанні йдеться про те, що "цифри до не менше 100000 повинні працювати протягом розумного часу", але чи має програма давати правильну відповідь для чисел, більших за це? Або було б прийнятно використовувати алгоритм, точний до 100000?
FireFly

1
Алгоритми @FireFly повинні дати правильну відповідь.
Говард

2
Чому б 1 повернути 1? Я б подумав 0?
Тімтех

@Timtech1.00000000000000000000000000000000000
Cruncher

@Cruncher О, дякую, я зараз це розумію.
Тімтех

Відповіді:


11

APL, 19 символів / байт *

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

Нарс2000 . Попередня версія помилялася в деяких номерах, це має бути правильно. Я вручну перевіряв це на всіх номерах до 50.

Знову ж таки, кредит іде на Бена Рейха за ідею розглянути період Росії10^i (mod x)

Вибухнув вигляд

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

Приклади

      {(↑⍳⍨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 може бути записаний у власному (попередньому) однобайтовому наборі, який відображає символи APL до верхніх значень 128 байт. Таким чином, з метою підрахунку, програма з N символів, яка використовує лише символи ASCII та символи APL, може вважатися довжиною N байтами.


Я не можу отримати правильну відповідь, наприклад, для введення даних 20. Ви можете підтвердити?
Говард

Я наслідував наведені вами приклади. У вашому прикладі 1/2 = 0,5 -> 1, так природно 1/20 = 0,05 -> 2. Що ви отримуєте?
Тобія

Правильна відповідь була б 1, оскільки 1/20 = 0,05_0_.
Говард

Я бачу. Дайте мені трохи, я перегляну свою відповідь.
Тобія

4видається, що це дало б неправильну відповідь, тому що 10 != 100 (mod 4).
Пітер Тейлор

7

GolfScript ( 42 27)

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

Орієнтовний час: 5 сек. Код бенчмаркінгу:

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

Подяка Бен-Райху за основну ідею розгляду періоду Росії 10^i (mod x).

Пояснення

Період pвизначається як найменше додатне ціле число, таке, що для всіх досить великих у iнас є frac(10^i * 1/x) = frac(10^(i+p) * 1/x). Ми можемо трохи спростити це frac(10^i / x) = frac(10^(i+p) / x). Тепер, frac(a / x) = frac(b / x)тоді і тільки тоді a == b (mod x), тому ми шукаємо найменше ціле позитивне число таке , що для всіх досить великих i: 10^i == 10^(i+p) (mod x).

Припустимо 10^i == 10^(i+p) (mod x). Тоді 10^(i+1) == 10 * 10^i == 10 * 10^(i+p) == 10^(i+p+1) (mod x); тож як тільки ми отримаємо повторення, ми переходимо до нерозривного циклу.

Існують лише xокремі значення (mod x), тому за принципом голубої дуги ми повинні отримати повторення у перших x + 1значеннях 10^i (mod x).

Тож, що робить код вище, це обчислити x + 2значення 10^i (mod x)*. Тоді останнє гарантовано буде повторенням, і, перевернувши список і шукаючи його, я можу знайти останнє явище. Більше того, тому що я роблю лише один пошук, це псевдолінійний час.

* Додатковий - це обробляти спеціальний кейс x = 1, тому що я не знижую 10^0 (mod x)і тому я б шукав 0в [1].


Дивовижно! Я видалив свою відповідь, оскільки краще рішення! -
Бен Рейх

7

Гольфскрипт - 26 байт

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

Редагувати: оновлено для виведення, 1якщо десятковий термін закінчується, а не довжина десяткового подання.

Досить ефективна версія. Значення 67890 працює приблизно за 10 секунд, а 99991 - близько 20 секунд. Це трохи повільніше, ніж було раніше (приблизно вдвічі швидше), тому що діапазон, який повторюється, був подвоєний, перша половина якого ігнорується.

Альтернативно, також 26 байт

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

Цей працює за допомогою ітерації над рядком "\n"*(2*i+1), де iє значення, передане функції. Значення, що передається блоку кожного разу, є порядковим значенням "\n", яке дорівнює 10 .

Це )^^трохи обробка. Коли ви від'єднуєте символ із рядка, результатом є порядкове значення символу, що видаляється, як було сказано вище. Однак додавання цього значення додасть рядкове подання цього числа, а не символу - досить несиметричне поведінку, і на мою думку, недолік дизайну. Якщо ви насправді хотіли це зробити, попереднє поглиблення коштувало б лише один байт.

Додаткова копія підсумкового значення вже є в стеці, тому я знову )видаляю остаточне значення , закреслюю його рядок, а потім знову xor, щоб будь-які символи, додані чи вилучені першим xor, були відновлені. Якщо int op stringтрактуватись як персонаж, а не його рядкове представлення, )^^можна було б замінити його |.

Зауважте, що в той час як рядки (які в Golfscript зберігаються як масив вкладень) відображатимуть значення кожного символу mod 256 , значення кожного символу можуть самі бути поза цим діапазоном. Під час тестування на унікальність (за допомогою встановлених операцій) чи вмістності (через ?) порівняно фактичне значення, а не значення відображення.

Файл виправлення для поточного інтерпретатора Golfscript :

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

Сказане лише вплине на поведінку string op int(і навпаки), де opє один із
+-|&^. Все інше залишається незмінним, включаючи поведінку Gint`.

Наступне 24-байтне рішення набуде чинності:

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

І це також фіксує безліч інших справді некрасивих робіт .


Пітон - 48 байт

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

Не найефективніше рішення, але розумне для значень менше 100000 .

FWIW, основний елемент ідентичний моєму рішенню для створення циклічних чисел у десяткових числах .

Більш ефективна версія того ж коду ( 70 байт ):

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

Значення 99991 займає менше секунди.


@PeterTaylor це orмасив на порожній рядок. Оскільки це набір операцій, усі дублікати заздалегідь видаляються.
прим

Але звідки береться порожня рядок? Якщо функція має бути самостійною, я думаю, вам доведеться витратити додатковий байт і зробити це .|.
Пітер Тейлор

1
@PeterTaylor виправлено .
прим

1
Зміна поведінки string int +може порушити багато програм. Я не впевнений, як часто інші оператори використовуються для цієї пари.
Пітер Тейлор

@PeterTaylor Я згоден, це було б. Але врахуйте: новонавернений Int символів: []+''+проти ''+. Append INT, як і напівкоксу, в рядок: []++проти +. Apend Int як строкове представлення, в рядок: +проти `+. В його теперішній реалізації, int''+це синонім int`, який здається марним, враховуючи багатослівність, що потрібно примушувати до масиву, а потім примушувати до рядка, якщо ви хочете, щоб знак ascii.
прим

3

GolfScript, 48 47 46

Дякуємо @PeterTaylor за те, що він відрізав дві знаки.

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

Я спробував використовувати J, але це не дало мені всіляких дивних результатів.

Тестуйте онлайн

Це, в основному, ділить 2 і 5 із числа (2 і 5 є простими множниками 10, і їхні зворотні запити закінчуються, і складають алгоритм), то найменше ціле число n таке, що отримане число ділить 10 ^ n - 1 є період.


3
Якщо ви знаєте, який буде перший виклик вашої функції, тоді ви можете ввести це визначення. Тобто замість {...}:d;...dвас заощадите 1 чару з...{...}:d~
Пітер Тейлор

@PeterTaylor дякую, не думав про це
Волатильність

1
Прокоментувавши Бена про те, що не залишати fна стеці, я помічаю, що ти це теж робиш. Вам слід дійсно додати функцію " ;pop" для виправлення порівняння з іншими мовами.
Пітер Тейлор

2
Ще одна мікрооптимізація: int array ,)\;може бути скорочена до int array +,.
Пітер Тейлор

2

Perl, 52 символи

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

Це нехитра реалізація прямого підходу. (На щастя, прямий підхід також досить ефективний: завдяки модульній арифметиці математика ніколи не повинна мати справу з числом, що перевищує 10 разів більше вхідного значення.)

Оскільки виклик визначав функцію, я відчував, що я змушений (повторно) ініціалізувати свої змінні, що я б не заважав робити для повної програми. Аналогічно, ~~заключний вислів є непотрібним, якщо функція може бути певною, вона буде викликатися в скалярному контексті.


Спробуйте ввести, 20де це дає неправильний результат.
Говард

2

Clojure, 102, 117, 115, 106

неоформлений:

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

відформатовано:

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

Масштаби часу роботи з періодом. Майже миттєво на моєму комп’ютері для вибіркових значень.

В основному, це обчислює результат віднімання після кожного кроку в довгому поділі. Цикл виявляється, якщо в будь-якій точці це число збігається з таким, яке було обчислено до нього.


Код розривається при введенні 20. Ви можете підтвердити?
Говард

Ви праві, вищевказане рішення є несправним. Подивіться, чи зможу це виправити.
RedDeckWins

Який очікуваний вихід на 20?
RedDeckWins

Правильною відповіддю було б 1.
Говард

Якщо добре піти, перший алгоритм вийшов з ладу на багатьох входах, наприклад, 12 і 20.
RedDeckWins

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.