Эффективно находить последовательности цифр в длинных целых числах

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

(редактировать) Я на самом деле какие числа, которые не содержат последовательности цифр, которые я ищу.

Целые числа будут большими, не менее 289 цифр. Последовательности для поиска могут быть любыми: «123», «5» (есть пятерка), «66666».

Меня интересует общее решение, но если вы хотите помочь с актуальной проблемой, я пытаюсь продолжить чтение.

В частности, я ищу повторяющиеся цифры длины 4, т.е. 1324322223313 "2222". Я смотрю на целые числа, потому что я буду увеличивать последовательные целые числа, если я не доберусь до целого числа с повторением длины 4, тогда я перейду к следующему целому числу без повторения. Также я не знаю, какие целые числа с цифрой больше 4, т.е. 12322135 (есть 5), будут исключены.

Проблема также может быть сформулирована как. Найдите все целые числа в z = диапазоне (x, y) такие, что z [a] не содержит повторяющихся цифр длины 4 и цифры больше 4. Диапазон (x, y) может быть очень большим

(Редактировать) в ответ на комментарий: Да, я действительно хотел бы создать список, но проблема в том, что я не уверен, как создать генератор, удовлетворяющий всем условиям, которые у меня есть. Может быть, мне следует подумать об этом больше, я согласен, это было бы проще, но это может быть похоже на генератор простых чисел, такого генератора нет.


person Vincent    schedule 11.01.2010    source источник
comment
Похоже, что на самом деле вам нужен способ генерировать все такие числа, а не способ проверить, подходит ли число или нет, поскольку это будет намного эффективнее, правильно ли это?   -  person James    schedule 11.01.2010
comment
Я не думаю, что возможно иметь генератор, а не фильтр / сито, но если у вас есть предложения о том, как я мог бы это сделать, это было бы здорово.   -  person Vincent    schedule 11.01.2010
comment
Я укажу, что 289-значные целые числа почти бесполезны в нашей вселенной. Это гораздо большее число, чем количество электронов во Вселенной. На самом деле никакая архитектура не хранит число, столь большое, как слово или что-то еще, поэтому вы не сэкономите много времени, рассматривая его как целое число, а не как строку.   -  person Triptych    schedule 11.01.2010
comment
@Triptych, преимущество числа в том, что в нем есть порядок, который, среди прочего, позволяет легко увеличивать его. Хотя правда есть сортировка по порядку для строк. Но, возможно, вы правы. Я сохраняю что-нибудь как целое число, а не как строку?   -  person Vincent    schedule 11.01.2010
comment
Если бы у вас был генератор, с чего бы он начинался? Вы говорите, что числа имеют по крайней мере 289 цифр.   -  person Ned Batchelder    schedule 12.01.2010


Ответы (5)


Вы можете использовать этот класс, чтобы иметь свой генератор цифр :-)

import math

class DecimalIndexing:
    def __init__(self, n):
        self.n = n
    def __len__(self):
        return int(math.floor(math.log10(self.n)+1))
    def __getitem__(self, i):
        if isinstance(i, slice):
            return [self[x] for x in range(i.start, i.stop, i.step or 1)]
        else:
            return (self.n/(10**i))%10
    def __iter__(self):
        for i in xrange(len(self)):
            yield self[i]

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

di = DecimalIndexing(31415927)
for i in xrange(len(di)):
    if di[i:i+4] == [9,5,1,4]:
        print "found"

или вот так:

for i in xrange(len(di)):
    if di[i:i+3] == [di[i]]*3:
        print "group of three equal digits at," i

или вот так:

if 5 in di:
    print "has a five"

или вот так:

if any(x > 5 in di):
    print "some digit was greater than five"

и т.п.

Имейте в виду, что индексы разрядов «обратные», т.е. читаются справа налево.

person fortran    schedule 11.01.2010

Список цифр довольно прост.

# given n, a long integer
digits = [] 
while n != 0:
    digits.append( n%10 )
    n //= 10
digits.reverse()

Затем вы можете выполнить сопоставление с образцом в этом списке цифр. Это то, что вы ищете?

person S.Lott    schedule 11.01.2010
comment
интересное решение для получения целого числа в список. Я не уверен, что это лучше, чем str(n) и сопоставление с образцом. Можно ли сопоставлять шаблоны непосредственно с целыми числами? Думаю, я лучше задаю свой вопрос, когда читаю комментарии и решения. - person Vincent; 11.01.2010
comment
Разве не проще получить список цифр в строке list(str(n)) ? - person Ned Batchelder; 12.01.2010

Вы можете сделать итератор с цифрами, упорядоченными слева направо таким образом

>>> import math
>>> number = int(123456789012345678901)
>>> #Get the maximum power of 10 using a logarithm
>>> max_digit = int(math.log10(number))
>>> range_pow = xrange(max_digit, 0, -1)
>>> # pot is an iterator with 1000, 100, 10, 1...
>>> pot = ( 10**x for x in range_pow)
>>> #Get the digits one by one on an iterator
>>> digits = ( (number/x)%10 for x in pot )
>>> l = list(digits)
>>> print l
[1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 0L]

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

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

>>> print l
[1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 0L]
>>> find = [1,2,3]
>>> lf = len(find)
>>> for i in xrange(len(l)):
...     if find == l[i:i+lf]:
...          print 'Found!', i
Found! 1
Found! 11

Отредактировано: я предложил более итеративный способ делать вещи... Параметр digits можно уточнить, чтобы создать список из числа, если это необходимо.

import math
from itertools import count

def find_digits_in_number(digits, number):
    #Get the maximum power of 10 using a logarithm
    max_digit = int(math.log10(number))
    range_pow = xrange(max_digit, -1, -1)
    # pot is an iterator with 1000, 100, 10, 1...
    pot = (10 ** x for x in range_pow)
    #Get the digits one by one on an iterator
    dig = ((number / x) % 10 for x in pot)

    #Current will store a moving windows with the 
    #size of the digits length to check if present
    current = []
    for i in digits:
        current.append(next(dig))

    digits = list(digits) 

    founds = []
    #The basic loop is this...
    #for digit, i in zip(dig, count()):
    #    if current == digits:
    #        founds.append(i)
    #    current.pop(0)
    #    current.append(digit)

    #But it can also be optimized like this list comprehension, 
    #while it's much less readable            
    [ (founds.append(i) if current == digits else None,\
      current.pop(0), current.append(digit)) \
      for digit, i in zip(dig, count()) ]

    #Check last posibility, with the last values
    if current == digits:
        founds.append(i + 1)

    return founds


if __name__ == '__main__':
    assert find_digits_in_number((3, 4, 5), 123456789012345678901) == [2, 12]
    assert find_digits_in_number((3, 4), 123456789034) == [2, 10]
person Khelben    schedule 11.01.2010

Возможно, вы захотите посмотреть здесь: Циклические номера; у них также есть алгоритм построения циклического числа.

Это также может быть полезно: обнаружение цикла

person Rubens Farias    schedule 11.01.2010

@Fortran дает отличное решение, оно очень универсально.

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

Чтобы проверить, находятся ли цифры 4444 в 35344442345321456754, и если я знаю, где их искать, то это хорошее решение и очевидное, как только вы его увидите.

(35344442345321456754 / 10**13) % 10**4 == 4444
person Vincent    schedule 12.01.2010