Обнаружение повторения с бесконечным вводом - PullRequest
1 голос
/ 17 февраля 2010

Какой самый оптимальный способ найти повторение в бесконечной последовательности целых чисел?

т.е. если в бесконечной последовательности число «5» появится дважды, то мы вернем «false» в первый раз и «true» во второй раз.

В конце концов нам нужна функция, которая возвращает «true», если целое число появилось раньше, и «false», если функция получила целое число в первый раз.

Если есть два решения, одно в пространстве, а второе во времени, то упомяните оба. Я напишу свое решение в ответах, но я не думаю, что оно оптимальное.

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

Ответы [ 5 ]

1 голос
/ 28 января 2012

На самом деле, если максимальное количество значений бесконечно, вы можете использовать любой алгоритм сжатия без потерь для монохромного растрового изображения. Если вы представляете квадрат с как минимум количеством пикселей, равным количеству возможных значений, вы можете отобразить каждое значение на пиксель (с несколькими запасными). Затем вы можете представить белый цвет как появившиеся пиксели и черный для остальных и использовать любой алгоритм сжатия, если пространство имеет большую цену (это, безусловно, проблема, которая была изучена)

Вы также можете хранить блоки. Наихудший случай такой же в пространстве O (n), но для этого наихудшего случая вам нужно, чтобы появившееся число имело ровно 1 между ними. Как только появятся новые цифры, память уменьшится: Я напишу псевдокод, и я буду использовать список, но вы всегда можете использовать другую структуру

List changes // global

boolean addNumber(int number):
  boolean appeared = false
  it = changes.begin()
  while it.hasNext():
    if it.get() < number:
      appeared != appeared
      it = it.next()
    else if it.get() == number:
      if !appeared: return true
      if it.next().get() == number + 1
        it.next().remove() // Join 2 blocks 
      else 
        it.insertAfter(number + 1)  // Insert split and create 2 blocks
      it.remove()
        return false
    else: // it.get() > number
      if appeared: return true
      it.insertBefore(number)
      if it.get() == number + 1:
        it.remove() // Extend next block
      else:
        it.insertBefore(number + 1)  
  }
  return false
}

Что этот код следующий: он хранит список блоков. Для каждого добавляемого вами числа он перебирает список, в котором хранятся блоки с номерами, которые появились, и номера, которые не были. Позвольте мне проиллюстрировать это на примере; Я добавлю [), чтобы проиллюстрировать, какие числа в блоке, первое число включено, последнее нет. В псевдокоде оно заменяется логическим appeared. Например, если вы получите 5, 9, 6, 8, 7 (в этом порядке), у вас будут следующие последовательности после каждой функции:

[5,6)

[5,6), [9,10)

[5,7), [9,10)

[5,7), [8,10)

[5,10)

В последнем значении вы сохраняете блок из 5 чисел только с 2.

1 голос
/ 17 февраля 2010

BitSet для значений int (2 ^ 32 числа) будет занимать 512 МБ. Это может быть нормально, если битовые наборы назначаются не часто, достаточно быстро и мем доступен.

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

1 голос
/ 17 февраля 2010

Я бы использовал следующий подход:

Используйте хеш-таблицу в качестве своей структуры данных. Для каждого прочитанного числа сохраните его в своей структуре данных. Если оно уже сохранено до того, как вы нашли повторение.

Если n - это количество элементов в последовательности от начала до повторения, то для этого требуется только O (n) времени и пространства. Сложность по времени оптимальна, так как вам нужно, по крайней мере, прочитать элементы входной последовательности до точки повторения.

Как долго мы говорим (до повторения)? Повторение вообще гарантировано? В крайних случаях сложность пространства может стать проблематичной. Но чтобы улучшить его, вам, вероятно, потребуется знать больше структурной информации о вашей последовательности.

Обновление: если последовательность, как вы говорите, очень длинная с редко повторениями, и вам необходимо сократить требования к пространству, то вы (при наличии достаточной структурной информации о последовательности) сможете сократить затраты на место.

В качестве примера: предположим, вы знаете, что ваша бесконечная последовательность имеет общую тенденцию возвращать числа, которые соответствуют текущему диапазону засвидетельствованных минимальных и максимальных чисел. Тогда у вас будут целые интервалы, которые уже содержатся в последовательности. В этом случае вы можете сэкономить место, сохраняя такие интервалы вместо всех элементов, содержащихся в нем.

0 голосов
/ 17 февраля 2010

Что ж, кажется очевидным, что в любом решении нам нужно сохранить уже появившиеся числа, поэтому в пространстве всегда будет худший случай O (N), где N <= возможные числа с размером слова нашего типа чисел (то есть 2 ^ 32 для C # int) - это проблематично в течение длительного времени, если последовательность действительно бесконечна / редко повторяется. </p>

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

0 голосов
/ 17 февраля 2010

Возврат ИСТИНА

Если последовательность бесконечна, то будет повторение каждого мыслимого паттерна.

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

...