Есть ли более Pythonic способ кодирования этого рекуррентного отношения re: OEIS A077947 - PullRequest
0 голосов
/ 18 февраля 2019

Я работаю над тезисом о последовательностях Якобсталя (A001045) и о том, как их можно рассматривать как состоящие из некоторого числа различных подпоследовательностей.Я сделал комментарий к A077947, указав это, и включил программу на Python.К сожалению, написанная программа оставляет желать лучшего, и поэтому я, конечно, хотел обратиться к Stack, чтобы узнать, знает ли кто-нибудь здесь, как улучшить код!

Вот код:

a=1
b=1
c=2
d=5
e=9
f=18
for x in range(0, 100):
  print(a, b, c, d, e, f)
  a = a+(36*64**x)
  b = b+(72*64**x)
  c = c+(144*64**x)
  d = d+(288*64**x)
  e = e+(576*64**x)
  f = f+(1152*64**x) 

Объясняю причину этого следующим образом:

Последовательность A077947 генерируется 6 последовательностями, сохраняющими цифровые корни, сшитыми вместе;согласно коду Python эти последовательности инициируются при начальных значениях af.Число итераций, необходимое для вычисления данного A077947 a (n), составляет ~ n / 6.Код при выполнении возвращает все значения для A077947 вплоть до диапазона (x) или ~ x * 6 членов A077947.Я нахожу повторяющиеся цифровые корни интересными, так как я ищу периодическое сохранение цифровых корней в последовательностях как метод для определения закономерностей в данных.Например, последовательности сохранения цифрового корня позволяют анализировать временные ряды больших наборов данных при оценке истинного или ложного состояния для аварийных сигналов в крупных ИТ-экосистемах, которые подвергаются обслуживанию (среды mod7);такой анализ также связан с прогнозированием потребительского спроса / моделей поведения.Используя эти методы анализа, разделение A077947 на 6 последовательностей, сохраняющих цифровой корень, должно было уменьшить сложность;код Python воспроизводит A077947 через 6 «каналов» со начальными значениями af.Этот длинный абзац сводится к утверждению: «Цифровые корни терминов последовательности повторяются в схеме (1, 1, 2, 5, 9, 9)».Большее утверждение состоит в том, что все последовательности, цифровые корни которых повторяются с шаблоном, могут быть разделены / разделены на равное количество различных последовательностей, и эти последовательности могут быть вычислены независимо.С этой последовательностью была щедрость.

Этот код безобразен, но я не могу получить правильный ответ, не кодировав его таким образом;

Я не выяснил, как написать это как функцию, потому что мне кажется, что я не могу получить значения повторения для правильного хранения в функции.

Так что, конечно, если это дает хорошиерезультаты, которые мы надеемся связать обсуждение с ссылками OEIS.

Вот ссылка на последовательность: https://oeis.org/A077947

Ответы [ 3 ]

0 голосов
/ 18 февраля 2019

Вот альтернативный способ сделать это без секунды для цикла:

sequences   = [ 1,  1,  2,   5,   9,   18   ]
multipliers = [ 36, 72, 144, 288, 576, 1152 ]

for x in range(100):
    print(*sequences)
    sequences = [ s + m*64**x for s,m in zip(sequences,multipliers) ]

[РЕДАКТИРОВАТЬ] Глядя на значения, я заметил, что эту конкретную последовательность также можно получить с помощью:

N [i + 1] = 2 * N [i] + (-1,0,1 по очереди)

или

N [i + 1] = 2 * N [i]+ i mod 3 - 1 (при условии индекса на основе нуля)

i  N[i] [-1,0,1]             N[i+1]
0  1    -1   --> 2*1 - 1 --> 1
1  1     0   --> 2*1 + 0 --> 2
2  2     1   --> 2*2 + 1 --> 5
3  5    -1   --> 2*5 - 1 --> 9
4  9     0   --> 2*9 + 0 --> 18
... 

Таким образом, более простой цикл для создания последовательности может быть:

n = 1
for i in range(100):
    print(n)
    n = 2*n + i % 3 - 1

ИспользованиеФункция Reduction от functools может сделать это еще более кратким:

from functools import reduce

sequence = reduce(lambda s,i: s + [s[-1]*2 + i%3 - 1],range(20),[1])
print(sequence)

>>> [1, 1, 2, 5, 9, 18, 37, 73, 146, 293, 585, 1170, 2341, 4681, 9362, 18725, 37449, 74898, 149797, 299593, 599186]

Используя ваш многоканальный подход и мою предложенную формулу, это даст:

sequences   = [ 1,  1,  2,   5,   9,   18   ]
multipliers = [ 36, 72, 144, 288, 576, 1152 ]

allSequences = reduce(lambda ss,x: ss + [[ s + m*64**x for s,m in zip(ss[-1],multipliers) ]],range(100),[sequences])
for seq in allSequences: print(*seq) # print 6 by 6

[EDIT2] Если все ваши последовательностибудет иметь похожий шаблон (т. е. стартовые каналы, множители и формулу расчета), вы можете обобщить печать таких последовательностей в функции, поэтому для каждой последовательности требуется только одна строка:

def printSeq(calcNext,sequence,multipliers,count):
    for x in range(count):
        print(*sequence)
        sequence = [ calcNext(x,s,m) for s,m in zip(sequence,multipliers) ]

printSeq(lambda x,s,m:s*2+m*64**x,[1,1,2,5,9,18],multipliers=[36,72,144,288,576,1152],count=100)

[EDIT3] Улучшениев функции printSeq.

Я считаю, что вам не всегда понадобится массив множителей для вычисленияследующее значение в каждом канале.Улучшение функции будет заключаться в предоставлении индекса канала для лямбда-функции вместо множителя.Это позволит вам использовать массив множителей, если вам нужно, но также позволит вам использовать более общие вычисления.

def printSeq(name,count,calcNext,sequence):
    p = len(sequence)
    for x in range(count):
        print(name, x,":","\t".join(str(s) for s in sequence))
        sequence = [ calcNext(x,s,c,p) for c,s in enumerate(sequence) ]

Функция лямбда имеет 4 параметра и, как ожидается, вернет следующую последовательностьзначение для указанного канала:

s : current sequence value for the channel
x : iteration number
c : channel index (zero based)
p : number of channels

Таким образом, использование массива внутри формулы будет выражать его следующим образом:

printSeq("A077947",100,lambda x,s,c,p: s + [36,72,144,288,576,1152][c] * 64**x, [1,1,2,5,9,18])

Но вы также можете использовать более общую формулу, основанную напо индексу каналов (и количеству каналов):

printSeq("A077947",100,lambda x,s,c,p: s + 9 * 2**(p*x+c+2), [1,1,2,5,9,18])

или (6 каналов на основе 2 * S + i% 3 - 1):

printSeq("A077947",100,lambda x,s,c,p: 64*s + 9*(c%3*2 - (c+2)%3 - 1) ,[1,1,2,5,9,18])
printSeq("A077947",100,lambda x,s,c,p: 64*s + 9*[-3,1,2][c%3],[1,1,2,5,9,18])

Мои рассуждения здесь таковычто если у вас есть функция, которая может вычислить следующее значение на основе текущего индекса и значения в последовательности, вы сможете определить пошаговую функцию, которая будет вычислять значение, равное N индексам дальше.

УчитываяF (i, S [i]) -> i + 1, S [i + 1]

F2(i,S[i]) --> i+2,S[i+2] = F(F(i,S[i]))
F3(i,S[i]) --> i+3,S[i+3] = F(F(F(i,S[i])))
...
F6(i,S[i]) --> i+6,S[i+6] = F(F(F(F(F(F(i,S[i]))))))
...
Fn(i,S[i]) --> i+n,S[i+n] = ...

Это всегда будет работать и не должно требовать массив множителей.В большинстве случаев должно быть возможно упростить Fn, используя простую алгебру.

, например, A001045: F (i, S) = i + 1, 2 * S + (-1) ** i

printSeq("A001045",20,lambda x,s,c,p: 64*s + 21*(-1)**(x*p+c),[0,1,1,3,5,11])

Обратите внимание, что начиная с 3-го значения, следующее значение в этой последовательности может быть вычислено без знания индекса:

A001045: F (S) = 2 * S + 1 - 2 * 0** ((S + 1)% 4) * +1056 *

0 голосов
/ 18 февраля 2019

Вот альтернативная версия с использованием генераторов:

def Jacobsthal():
    roots = [1, 1, 2, 5, 9, 18]
    x = 0
    while True:
        yield roots
        for i in range(6):
            roots[i] += 36 * 2**i * 64**x
        x += 1

А вот безопасный способ ее использования:

j = Jacobsthal()
for _ in range(10):
    print(j.send(None))
0 голосов
/ 18 февраля 2019

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

factors = [ 1, 1, 2, 5, 9, 18 ]
cofactors = [ 36*(2**n) for n in range(6) ]

for x in range(10):
    print(*factors)
    for i in range(6):
        factors[i] = factors[i] + cofactors[i] * 64**x

Чтобы вычислить только одну из подпоследовательностей, было бы достаточно сохранить i фиксированным во время итерации.

...