Найти период Пизано


20

Последовательность Фибоначчи - это хорошо известная последовательность, в которой каждая запись является суммой предыдущих двух, а первые две записи равны 1. Если мы возьмем по модулю каждого члена константу, последовательность станет периодической. Например, если бы мы решили вычислить последовательность мод 7, мы получили бы следующее:

1 1 2 3 5 1 6 0 6 6 5 4 2 6 1 0 1 1 ...

Это имеет период 16. Связанная последовательность, называемая последовательностью Пизано , определяется так, что a(n)это период последовательности Фибоначчи при вычислении по модулю n.

задача

Вы должны написать программу или функцию, которая при получении nбудет вычислять и выводить период мод последовательности Фибоначчи n. Это n-й член в последовательности Пизано.

Вы должны поддерживать только целые числа в диапазоне 0 < n < 2^30

Это соревнование по поэтому вы должны стремиться минимизировать размер исходного кода, измеряемый байтами.

Контрольные примеры

1  -> 1
2  -> 3
3  -> 8
4  -> 6
5  -> 20
6  -> 24
7  -> 16
8  -> 12
9  -> 24
10 -> 60
11 -> 10
12 -> 24

3
Ограничение 2 ^ 30 может гарантировать, что все промежуточные значения меньше 2 ^ 31, но все равно не гарантирует, что период Пизано будет соответствовать 32-разрядному целому числу со знаком. (Полагаю, в этом причина вашего ограничения?) Периоды Пизано могут быть значительно больше, чем их n . Например, период Пизано 6 равен 24. Полномочия 10 выше 100 выходят на 50 процентов больше, чем n .
Изи

3
Принцип Pigeonhole гласит, что f(i),f(i+1)может принимать не более n^2значений мод n. Таким образом, nограничение 2^30может закончиться, производя период до 2^60. Ограничение n <= 2^16дало бы P(n) <= 2^32.
Boothby

@boothby Я не совсем уверен, что понимаю, о чем ты говоришь, или даже если это правильно решает ту же проблему, что и я. Не могли бы вы объяснить немного дальше, возможно, с дополнительными ссылками? Не стесняйтесь втягивать меня в чат, если это необходимо.
Изи

2
@Iszi Заметьте f(i+2) = f(i+1)+f(i), что «состояние» машины, зацикливающейся на период, можно описать с помощью пары целых чисел мод n. Есть в большинстве n^2штатов, поэтому период не более n^2. Ой! Википедия утверждает, что период не более 6n. Не берите в голову мою мелочь.
Boothby

Ответы:


11

GolfScript ( 28 25 24 23 символов)

~1.{(2$+}{.@+2$%}/+\-,)

Принимает ввод в stdin, оставляет его в stdout (или в стеке, если вы хотите продолжить его обрабатывать ...)

Это правильно обрабатывает угловые случаи ( Демо ).

Как интерес для программистов GolfScript, я думаю, что это первая программа, которую я написал с развёртыванием, которая на самом деле оказалась короче, чем другие подходы, которые я пробовал.


7

GolfScript, 24 символа

~:&1.{.2$+&%.2$(|}do](-,

Следующая итерация реализации GolfScript. Вторая версия теперь также обрабатывает 1 правильно. Это стало довольно долго, но, возможно, кто-то может найти способ сократить эту версию. Вы можете попробовать вышеуказанную версию онлайн .


1Правильно ли это обрабатывает ввод ?
Питер Тейлор

@PeterTaylor Нет, не проверял этот угловой случай. Вернуться к доске для рисования.
Говард

@PeterTaylor Новый код также работает для ввода 1- и все еще только 24 символа.
Говард

4

Python, 188 132 101 95 87 символов

n=input()
s=[]
a=k=0
b=1
while s[:k]!=s[k:]or k<1:s+=[a%n];k=len(s)/2;a,b=b,a+b
print k

использование

$ echo 10 | python pisano.py
60

Например:

$ for i in {1..50}; do; echo $i | python pisano.py; done
1
3
8
6
20
24
16
12
24
60
10
24
28
48
40
24
36
24
18
60
16
30
48
24
100
84
72
48
14
120
30
48
40
36
80
24
76
18
56
60
40
48
88
30
120
48
32
24
112
300

Спасибо, beary605 , за дополнительный гольф!
ESultanik

Вы можете пересчитать свои символы снова. Мой счет вашего ответа ниже вашего счета вашего ответа.
DavidC

@ Дэвид: Вы считаете пробелы? Я просто перепроверены (по catтин , чтобы wc -cи я получаю тот же номер.
ESultanik

Я использую рутину, предоставленную Wolfram Research. Я считаю, что он считает необходимым пробел.
DavidC

if k>0 and s[0:k]==s[k:]:breakможно изменить на if s and s[:k]==s[k:]:break. Вы также можете существенно сократить, удалив итератор, изменив forцикл while 1:и выполнив его a,b=a,a+bв конце цикла while.
Стригоидес

4

питон 90 85 96 94 90 82

n=input();c=[1,1];a=[]
while(c in a)<1%n:a+=[c];c=[c[1],sum(c)%n]
print len(a)or 1

Редактировать: Реализованные предложения от beary и primo


85:, a.append(c) -> a+=[c]хотя цикл можно поместить в одну строку,((n>1)>>(c in a)) -> (n>1)>>(c in a)
beary605

appendна самом деле имеет другую функциональность, чем +=. Спасибо за советы, хотя.
scleaver

Я думаю, что это работает так же, как в этом случае.
beary605

(n>1)>>(c in a) -> (c in a)<1%nза 3 байта. И я согласен с Беари в добавлении. Независимо от того, добавляете ли вы ссылку cили расширяете ее aпо значению c, это в любом случае одинаково (так как вы немедленно уничтожаете свою ссылку c).
Примо

Ах, хорошо, моя ошибка была в том, что я использовал a+=cвместоa+=[c]
scleaver

2

Mathematica 73

p = {1, 0}; j = 0; q = p;
While[j++; s = Mod[Plus @@ p, n]; p = RotateLeft@p; p[[2]] = s; p != q]; j

2

PHP - 61 57 байт

<?for(;1<$a.$b=+$a+$a=!$i+++$b%$n+=fgets(STDIN););echo$i;

Этот сценарий будет ошибочно сообщать 2для n=1, но все остальные значения являются правильными.

Пример ввода / вывода, усекаемый слева ряд, где π (n) = 2n + 2:

$ echo 3 | php pisano.php
8
$ echo 13 | php pisano.php
28
$ echo 313 | php pisano.php
628
$ echo 3313 | php pisano.php
6628
$ echo 43313 | php pisano.php
86628
$ echo 543313 | php pisano.php
1086628
$ echo 4543313 | php pisano.php
9086628
$ echo 24543313 | php pisano.php
49086628

1
1<$a.$b=+$a+$a=!$i+++$b%$n+=fgets(STDIN)О боже, это какой-то порядок эксплуатации операции прямо там.
Мистер Лама

1

PowerShell: 98

Гольф-код:

for($a,$b=0,(1%($n=read-host))){$x++;if($a+$b-eq0-or("$a$b"-eq10)){$x;break}$a,$b=$b,(($a+$b)%$n)}

Ungolfed, с комментариями:

for
(
    # Start with $a as zero, and $b as 1%$n.
    # Setting $b like this at the start helps catch the exceptional case where $n=1.
    $a,$b=0,(1%
    (
        # Grab user input for n.
        $n=read-host
    ))
)
{
    # Increasing the counter ($x) and testing for the end of the period at the start ensures proper output for $n=1.
    $x++;

    # Test to see if we've found the end of the Pisano Period.
    if
    (
        # The first part catches $n=1, since $a and $b will both be zero at this point.
        $a+$b-eq0-or
        (
            # A shorter way of testing $a-eq1-and$b-eq0, which is the end of a "normal" Pisano Period.
            "$a$b"-eq10
        )
    )
    {
        # Pisano Period has reached its end. Output $x and get out of the loop.
        $x;break
    }

    # Pisano Period still continues, perform operation to calculate next number.
    # Works pretty much like a Fibonacci sequence, but uses ($a+$b)%$n for the new $b instead.
    # This takes advantage of the fact we don't really need to track the actual Fibonacci numbers, just the Fibonacci pattern of %$n.
    $a,$b=$b,(($a+$b)%$n)
}

# Variable cleanup - not included in golfed code.
rv n,a,b,x

Примечания:

Я не уверен точно, что максимальный надежный предел для $ n с этим сценарием. Вполне возможно, что меньше 2 ^ 30, так как $ x может переполнить int32 до того, как туда попадет $ n. Кроме того, я не проверял верхний предел самостоятельно, потому что время выполнения сценария уже достигло 30 секунд в моей системе для $ n = 1e7 (что чуть больше 2 ^ 23). По той же причине я не склонен быстро тестировать и устранять любые дополнительные синтаксисы, которые могут потребоваться для обновления переменных до uint32, int64 или uint64, где это необходимо, чтобы расширить диапазон этого скрипта.


Образец вывода:

Я завернул это в другой цикл:

for($i=1;;$i++)

Затем установите $n=$iвместо =read-hostи измените вывод на, "$i | $x"чтобы получить представление об общей надежности сценария. Вот некоторые из результатов:

1 | 1
2 | 3
3 | 8
4 | 6
5 | 20
6 | 24
7 | 16
8 | 12
9 | 24
10 | 60
11 | 10
12 | 24
13 | 28
14 | 48
15 | 40
16 | 24
17 | 36
18 | 24
19 | 18
20 | 60

...

9990 | 6840
9991 | 10192
9992 | 624
9993 | 4440
9994 | 1584
9995 | 6660
9996 | 1008
9997 | 1344
9998 | 4998
9999 | 600
10000 | 15000
10001 | 10212
10002 | 3336
10003 | 5712
10004 | 120
10005 | 1680
10006 | 10008
10007 | 20016
10008 | 552
10009 | 3336
10010 | 1680

Sidenote: Я не совсем уверен, что некоторые периоды Пизано значительно короче, чем $ n. Это нормально или что-то не так с моим скриптом? Неважно - я только что вспомнил, что после 5 числа Фибоначчи быстро становятся намного больше, чем их место в последовательности. Итак, теперь это имеет смысл.


1

Perl, 75 , 61 , 62 + 1 = 63

$k=1%$_;$a++,($m,$k)=($k,($m+$k)%$_)until$h{"$m,$k"}++;say$a-1

использование

$ echo 8 | perl -n -M5.010 ./pisano.pl
12

Ungolfed

$k = 1 % $_;
$a++, ($m, $k) = ($k, ($m + $k) % $_) until $h{"$m,$k"}++;
say $a - 1

+1 байт за -nфлаг. Побрил 13 байтов благодаря Габриэлю Бенами.


1
Вы можете избавиться от $n=<>;(-6) и заменить его -nфлагом (+1), тогда все экземпляры $nможно заменить на $_. Вы можете использовать -M5.010бесплатно, что позволяет использовать sayкоманду вместо print(-2). whileОператоры- модификаторы не нуждаются в скобках вокруг условия (-2). Вместо этого @{[%h]}/2вы можете иметь счетчик $a++,до, ($m,$k)=а затем просто иметь say$a-1в конце (-2). Вместо "$m,$k"использования $m.$k(-2). Это должно получиться $k=1%$_;$a++,($m,$k)=($k,($m+$k)%$_)while!$h{$m.$k}++;say$a-1с -nфлагом, для 61 + 1 = 62 байта.
Габриэль Бенами

Очевидно, я не такой умный с Perl, как я думал. Спасибо за советы.
Сильвио Майоло,

В Совете по игре в гольф в Perl есть много полезных указателей ! Удачи! ^^
Габриэль Бенами

На самом деле, я был не прав - вам нужно "$m,$k"вместо $m.$k(+2), но вы можете сэкономить 1 байт, изменив while!$hна until$h(-1). Сожалею!
Габриэль Бенами

Хм? Под каким входом происходит $m.$kсбой? Казалось, сработало на моем конце.
Сильвио Майоло,

0

Clojure, 102 байта

Не слишком захватывающе, итерация формулы, пока мы не вернемся [1 1](я надеюсь, что это всегда так). Специальная обработка, (f 1)поскольку это сходится к [0 0].

#(if(< % 2)1(+(count(take-while(fn[v](not=[1 1]v))(rest(iterate(fn[[a b]][b(mod(+ a b)%)])[1 1]))))1))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.