Найдите целое число не из четырех миллиардов данных

Это вопрос интервью:

Учитывая входной файл с четырьмя миллиардами целых чисел, предоставьте алгоритм для генерации целого числа, которое не содержится в файле. Предположим, у вас есть 1 ГБ памяти. Следите за тем, что бы вы сделали, если у вас есть только 10 МБ памяти.

Мой анализ:

Размер файла составляет 4 × 10 9 × 4 байта = 16 ГБ.

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

Мое понимание (после прочтения всех ответов):

Предположим, мы говорим о 32-разрядных целых числах. Есть 2 ^ 32 = 4 * 10 9 различных целых чисел.

Случай 1: у нас 1 ГБ = 1 * 10 9 * 8 бит = 8 миллиардов бит памяти.

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

int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
    Scanner in = new Scanner(new FileReader("a.txt"));
    while(in.hasNextInt()){
        int n = in.nextInt();
        bitfield[n/radix] |= (1 << (n%radix));
    }

    for(int i = 0; i< bitfield.lenght; i++){
        for(int j =0; j<radix; j++){
            if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
        }
    }
}

Случай 2: 10 МБ памяти = 10 * 10 6 * 8 бит = 80 миллионов бит

Решение: Для всех возможных 16-битных префиксов есть 2 ^ 16 целых чисел = 65536, нам нужно 2 ^ 16 * 4 * 8 = 2 миллиона битов. Нам нужно построить 65536 ведер. Для каждого сегмента нам нужно 4 байта, содержащих все возможности, потому что в худшем случае все 4 миллиарда целых чисел принадлежат одному и тому же сегменту.

  1. Постройте счетчик каждого сегмента через первый проход по файлу.
  2. Просканируйте ведра, найдите первого, у которого попадание менее 65536.
  3. Создайте новые сегменты с высокими 16-битными префиксами, которые мы нашли в шаге 2 через второй проход файла
  4. Просканируйте ведра, созданные в шаге 3, найдите первое ведро, которое не имеет попадания.

Код очень похож на приведенный выше.

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


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

Ответ 1

Предполагая, что "integer" означает 32 бита: наличие 10 МБ пространства более чем достаточно для того, чтобы подсчитать, сколько чисел есть во входном файле с любым заданным 16-битным префиксом, для все возможные 16-битные префиксы за один проход через входной файл. По крайней мере один из ведер будет поражен менее чем в 2 раза 16 раз. Сделайте второй проход, чтобы найти, какие из возможных чисел в этом ковше уже используются.

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

Если "integer" означает математическое целое: Прочитайте ввод один раз и отслеживайте длину наибольшего числа самого длинного числа, которое вы когда-либо видели. Когда вы закончите, выведите максимум плюс один случайное число с еще одной цифрой. (Одно из чисел в файле может быть бонусом, который занимает более 10 МБ для представления точно, но если вход является файлом, то вы можете хотя бы представить длину всего, что в нем вписывается).

Ответ 2

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

Если допускаются очень большие целые числа, тогда можно создать число, которое может быть уникальным в O (1) раз. Псевдослучайное 128-битное целое число, такое как GUID, будет сталкиваться только с одним из существующих четырех миллиардов целых чисел в наборе менее чем за один из каждые 64 миллиарда миллиардов миллиардов случаев.

Если целые числа ограничены 32 битами, тогда можно создать число, которое, вероятно, будет уникальным за один проход, используя намного меньше 10 Мб. Вероятность того, что псевдослучайное 32-битное целое число столкнется с одним из 4 миллиардов существующих целых чисел, составляет около 93% (4e9/2 ^ 32). Шансы, что 1000 случайных целых чисел будут сталкиваться, составляют менее одного из 12 000 миллиардов миллиардов миллиардов (вероятность однократного столкновения ^ 1000). Поэтому, если программа поддерживает структуру данных, содержащую 1000 псевдослучайных кандидатов, и итерации через известные целые числа, исключая совпадения от кандидатов, все равно определенно найти хотя бы одно целое число, которое не находится в файле.

Ответ 3

Подробное обсуждение этой проблемы обсуждалось в книге Джона Бентли "Колонка 1. Сломать устрицу". Программирование "Жемчуг" Эддисон-Уэсли, с. 3-10.

Бентли обсуждает несколько подходов, в том числе внешнюю сортировку, сортировку слиянием с использованием нескольких внешних файлов и т.д. Но лучший метод, который предлагает Бентли, - это однопроходный алгоритм с использованием битовых полей, который он шутливо называет "сортировка по Чуду" :) Приходя к проблеме, 4 миллиарда числа могут быть представлены в:

4 billion bits = (4000000000 / 8) bytes = about 0.466 GB

Код для реализации набора битов прост: (взят со страницы решений)

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }
void clr(int i) {        a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }

Алгоритм Bentley делает один проход по файлу, set соответствующий бит в массиве и затем проверяет этот массив, используя test макрос выше, чтобы найти пропущенное число.

Если объем доступной памяти меньше 0,466 ГБ, Bentley предлагает алгоритм k-pass, который делит входные данные на диапазоны в зависимости от доступной памяти. Возьмем очень простой пример: если был доступен только 1 байт (то есть память для обработки 8 чисел), а диапазон был от 0 до 31, мы делим это на диапазоны от 0 до 7, 8-15, 16-22 и т.д. и обрабатывать этот диапазон в каждом из 32/8 = 4 прохода.

НТН.

Ответ 4

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

Ответ 5

Для 1-го варианта GB RAM вы можете использовать бит-вектор. Вы должны выделить 4 миллиарда бит == 500 массив байтов MB. Для каждого числа, которое вы читаете на входе, установите соответствующий бит в "1". Как только вы закончите, перебирайте биты, найдите первый, который все еще "0". Его индекс - это ответ.

Ответ 6

Если они являются 32-разрядными целыми числами (вероятно, из выбора ~ 4 миллиардов чисел, близких к 2 32), ваш список из 4 миллиардов чисел займет не более 93% возможных целых чисел (4 * 10 9/(2 32)). Поэтому, если вы создаете битовый массив из 2 32 бит, каждый из которых инициализируется нулем (что займет 2 29 байт ~ 500 МБ ОЗУ; запомните байт = 2 3 бита = 8 бит), прочитайте список целых чисел и для каждого типа int установите соответствующий элемент массива битов от 0 до 1; а затем прочитайте ваш битовый массив и верните первый бит, который все еще равен 0.

В случае, когда у вас меньше оперативной памяти (~ 10 МБ), это решение нужно немного изменить. 10 МБ ~ 83886080 битов все еще достаточно для создания битового массива для всех чисел от 0 до 83886079. Таким образом, вы можете прочитать свой список целых чисел; и только записи #, которые находятся между 0 и 83886079 в вашем битовом массиве. Если числа распределены случайным образом; с подавляющей вероятностью (она отличается на 100% примерно на 10 -2592069) вы найдете пропущенный int). Фактически, если вы выбираете только числа от 1 до 2048 (только с 256 байтами оперативной памяти), вы все равно найдете пропущенное число в подавляющем проценте (99,9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999995%) времени.

Но, скажем, вместо 4 миллиардов чисел; у вас было что-то вроде 2 32 - 1 цифр и менее 10 МБ ОЗУ; поэтому любой небольшой диапазон целых имеет небольшую вероятность не содержать числа.

Если вам было гарантировано, что каждое целое число в списке уникально, вы можете сложить числа и вычесть сумму с одним пропущенным # до полной суммы (½) (2 32) (2 32 - 1) = 9223372034707292160, чтобы найти пропущенное целое число, Однако, если int встречался дважды, этот метод потерпит неудачу.

Тем не менее, вы всегда можете разделить и победить. Наивным методом было бы прочитать массив и подсчитать количество чисел в первой половине (от 0 до 2 31 -1) и во второй половине (2 31 2 32). Затем выберите диапазон с меньшим числом чисел и повторите деление этого диапазона пополам. (Скажем, если в (2 31 2 32) было на два меньше числа, то при следующем поиске будет подсчитано число в диапазоне (2 31 3 * 2 30 -1), (3 * 2 30 2 32). Продолжайте повторять, пока не найдете диапазон с нулевыми числами, и у вас есть ответ. Должно пройти O (lg N) ~ 32 чтения массива.

Этот метод был неэффективным. Мы используем только два целых числа на каждом шаге (или около 8 байтов оперативной памяти с 4-байтовым (32-битным) целым числом). Лучшим методом было бы разделить на sqrt (2 32) = 2 16= 65536 бинов, каждый с 65536 числами в бине. Каждый бин требует 4 байта для хранения своего счетчика, поэтому вам нужно 2 18 байт = 256 кБ. Таким образом, интервал 0 равен (от 0 до 65535 = 2 16 -1), интервал 1 равен (2 16= 65536 - 2 * 2 16 -1 = 131071), интервал 2 равен (2 * 2 16= 131072 до 3 * 2). 16 -1 = 196607). В Python у вас будет что-то вроде:

import numpy as np
nums_in_bin = np.zeros(65536, dtype=np.uint32)
for N in four_billion_int_array:
    nums_in_bin[N // 65536] += 1
for bin_num, bin_count in enumerate(nums_in_bin):
    if bin_count < 65536:
        break # we have found an incomplete bin with missing ints (bin_num)

Прочитайте список целых чисел ~ 4 миллиарда; и посчитайте, сколько целых чисел попадает в каждый из 2 16 бинов, и найдите incomplete_bin, в котором нет всех 65536 чисел. Затем вы снова читаете список из 4 миллиардов целых чисел; но на этот раз заметьте только когда целые числа находятся в этом диапазоне; слегка переворачивая, когда вы найдете их.

del nums_in_bin # allow gc to free old 256kB array
from bitarray import bitarray
my_bit_array = bitarray(65536) # 32 kB
my_bit_array.setall(0)
for N in four_billion_int_array:
    if N // 65536 == bin_num:
        my_bit_array[N % 65536] = 1
for i, bit in enumerate(my_bit_array):
    if not bit:
        print bin_num*65536 + i
        break

Ответ 7

Почему это так сложно? Вы запрашиваете целое число, отсутствующее в файле?

В соответствии с указанными правилами единственное, что вам нужно сохранить, - это наибольшее целое число, с которым вы столкнулись до сих пор в файле. После того, как весь файл будет прочитан, верните номер 1 больше.

Нет риска попасть в maxint или что-либо еще, потому что в соответствии с правилами нет ограничений на размер целого числа или числа, возвращаемого алгоритмом.

Ответ 8

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

  • Начните с допустимого диапазона чисел, 0 до 4294967295.

  • Рассчитайте среднюю точку.

  • Прокрутите файл, считая, сколько чисел было равным, меньше или выше среднего значения.

  • Если числа не равны, все готово. Средний номер - это ответ.

  • В противном случае выберите диапазон с наименьшим числом и повторите шаг 2 с этим новым диапазоном.

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

Это по существу то же самое, что решение Хеннинга, за исключением того, что использует два бункера вместо 16k.

Ответ 9

EDIT Хорошо, это не было продумано, поскольку предполагается, что целые числа в файле следуют статическому распределению. По-видимому, им это не нужно, но даже тогда нужно попробовать:


Есть ≈4.3 миллиарда 32-битных целых чисел. Мы не знаем, как они распределены в файле, но худший случай - с самой высокой энтропией Шеннона: равным распределением. В этом случае вероятность того, что какое-либо целое число не будет присутствовать в файле, будет

((2³²-1)/2³²) ⁴ ⁰⁰⁰ ⁰⁰⁰ ⁰⁰⁰ ≈.4

Чем ниже энтропия Шэннона, тем выше вероятность этой вероятности в среднем, но даже в этом худшем случае у нас есть шанс на 90% найти неосознаваемое число после 5 догадок со случайными целыми числами. Просто создайте такие числа с псевдослучайным генератором, сохраните их в списке. Затем прочитайте int после int и сравните его со всеми вашими догадками. Когда есть совпадение, удалите этот список. Пройдя через весь файл, скорее всего, у вас останется несколько догадок. Используйте любой из них. В редком (10% даже в худшем случае) событии без догадки осталось получить новый набор случайных чисел, возможно, больше на этот раз (10- > 99%).

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


Фактический наихудший случай, когда мы не предполагаем статическое распределение, состоит в том, что каждое целое число имеет максимальное значение. один раз, потому что тогда только 1 - 4000000000/2³² ≈ 6% всех целых чисел не встречаются в файле. Поэтому вам понадобятся еще несколько догадок, но это все равно не будет стоить вредного количества памяти.

Ответ 10

Если у вас есть одно целое число, отсутствующее в диапазоне [0, 2 ^ x - 1], то просто соедините их все вместе. Например:

>>> 0 ^ 1 ^ 3
2
>>> 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 6 ^ 7
5

(Я знаю, что это точно не отвечает на вопрос, но это хороший ответ на очень похожий вопрос.)

Ответ 11

Основываясь на текущей формулировке исходного вопроса, самым простым решением является:

Найти максимальное значение в файле, а затем добавить к нему.

Ответ 12

Они могут посмотреть, слышали ли вы о вероятностном Bloom Filter, который может очень эффективно определить абсолютно, если значение не является частью большого набора (( но может только с большой вероятностью определить, что он является членом множества.)

Ответ 13

Используйте BitSet. 4 миллиарда целых чисел (предположительно до 2 ^ 32 целых чисел), упакованных в битсет с 8 байтами, составляет 2 ^ 32/2 ^ 3 = 2 ^ 29 = около 0,5 Гб.

Чтобы добавить немного более подробную информацию - каждый раз, когда вы читаете номер, установите соответствующий бит в BitSet. Затем выполните проход над BitSet, чтобы найти первое число, которое не присутствует. Фактически, вы могли бы сделать это так же эффективно, многократно подбирая случайное число и тестируя, если он присутствует.

На самом деле BitSet.nextClearBit(0) сообщит вам первый не установленный бит.

Глядя на API BitSet, он, по-видимому, поддерживает только 0..MAX_INT, поэтому вам может понадобиться 2 битовых набора - один для + номеров и один для -'всех номеров, но требования к памяти не меняются.

Ответ 14

Если ограничение по размеру отсутствует, самым быстрым способом является длина файла и генерация длины файла + 1 число случайных цифр (или просто "11111..." s). Преимущество: вам даже не нужно читать файл, и вы можете свести к минимуму использование памяти почти до нуля. Недостаток: вы будете печатать миллиарды цифр.

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

Ответ 15

Если мы предположим, что диапазон чисел всегда будет 2 ^ n (четная степень 2), то исключительная или будет работать (как показано другим плакатом). Что до чего, пусть это докажет:

Теория

Учитывая любой диапазон целых чисел, основанный на 0, который имеет элементы 2^n с отсутствием одного элемента, вы можете найти этот недостающий элемент, просто сопоставив известные значения вместе, чтобы получить недостающее число.

Доказательство

Посмотрим на n = 2. Для n = 2 мы можем представить 4 уникальных целых числа: 0, 1, 2, 3. Они имеют битовую структуру:

  • 0 - 00
  • 1 - 01
  • 2 - 10
  • 3 - 11

Теперь, если мы посмотрим, каждый бит устанавливается ровно в два раза. Поэтому, поскольку оно задано четным числом раз, а исключение - или чисел, будет равно 0. Если отсутствует один номер, исключение - или даст число, которое при исключительном использовании с недостающим числом приведет к 0. Таким образом, недостающее число и получаемое в результате исключающее число точно совпадают. Если мы удалим 2, результирующий xor будет 10 (или 2).

Теперь посмотрим на n + 1. Позвольте называть количество раз, когда каждый бит устанавливается в n, x и количество раз, когда каждый бит установлен в n+1 y. Значение y будет равно y = x * 2, потому что есть элементы x с битом n+1, установленным в 0, и x с битом n+1, установленным в 1. И поскольку 2x всегда будет четным, n+1 всегда будет каждый бит задавать четное количество раз.

Следовательно, поскольку n=2 работает, а n+1 работает, метод xor будет работать для всех значений n>=2.

Алгоритм для 0 базирующихся диапазонов

Это довольно просто. Он использует 2 * n бит памяти, поэтому для любого диапазона <= 32, 32 битных целых числа будут работать (игнорируя любую память, потребляемую файловым дескриптором). И он делает один проход файла.

long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
    result = result ^ supplied;
}
return result;

Алгоритм для произвольных базисных диапазонов

Этот алгоритм будет работать для диапазонов любого начального числа до любого конечного числа, если полный диапазон равен 2 ^ n... Это в основном восстанавливает диапазон, чтобы иметь минимум в 0. Но он требуется 2 прохода через файл (первый для получения минимума, второй - для вычисления отсутствующего int).

long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    result = result ^ (supplied - offset);
}
return result + offset;

Произвольные диапазоны

Мы можем применить этот модифицированный метод к множеству произвольных диапазонов, так как все диапазоны будут пересекаться с степенью 2 ^ n хотя бы один раз. Это работает только в том случае, если имеется один отсутствующий бит. Он занимает 2 прохода несортированного файла, но каждый раз он найдет одиночное пропущенное число:

long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    n++;
    result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing 
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
    result = result ^ (n++);
}
return result + offset;

В принципе, переопределяет диапазон вокруг 0. Затем он подсчитывает количество несортированных значений, добавляемых при его вычислении как исключающее или. Затем он добавляет 1 к счету несортированных значений, чтобы позаботиться о недостающем значении (подсчитайте отсутствующий). Затем сохраните xoring значение n, увеличиваемое на 1 каждый раз, пока n не станет силой 2. Результат затем будет повторно основан на исходной базе. Готово.

Здесь алгоритм, который я тестировал в PHP (используя массив вместо файла, но ту же концепцию):

function find($array) {
    $offset = min($array);
    $n = 0;
    $result = 0;
    foreach ($array as $value) {
        $result = $result ^ ($value - $offset);
        $n++;
    }
    $n++; // This takes care of the missing value
    while ($n == 1 || 0 != ($n & ($n - 1))) {
        $result = $result ^ ($n++);
    }
    return $result + $offset;
}

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

Другой подход

Поскольку мы можем использовать внешнюю сортировку, почему бы просто не проверить пробел? Если предположить, что файл был отсортирован до запуска этого алгоритма:

long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
    if (supplied != last + 1) {
        return last + 1;
    }
    last = supplied;
}
// The range is contiguous, so what do we do here?  Let return last + 1:
return last + 1;

Ответ 16

Проверьте размер входного файла, затем выведите любое число, которое слишком велико, чтобы быть представлено файлом такого размера. Это может показаться дешевым трюком, но это творческое решение для проблема интервью, она аккуратно обошла проблему памяти и технически O (n).

void maxNum(ulong filesize)
{
    ulong bitcount = filesize * 8; //number of bits in file

    for (ulong i = 0; i < bitcount; i++)
    {
        Console.Write(9);
    }
}

Должен печатать 10 bitcount - 1, который всегда будет больше 2 биткомета. Технически число, которое вы должны победить, 2 биткомета - (4 * 10 9 - 1), так как вы знаете, что есть (4 миллиарда - 1) другие целые числа в файле, и даже при идеальном сжатии они будут занимать по крайней мере один бит каждый.

Ответ 17

  • Самый простой способ - найти минимальное число в файле и вернуть 1 меньше этого. Это использует память O (1) и время O (n) для файла из n чисел. Однако он будет терпеть неудачу, если диапазон номеров ограничен, что может сделать min-1 not-a-number.

  • Простой и понятный метод использования растрового изображения уже упоминался. Этот метод использует O (n) время и память.

  • Также упоминался двухпроходный метод с 2 ^ 16 счетными ведрами. Он считывает 2 * n целых чисел, поэтому использует O (n) время и память O (1), но не может обрабатывать наборы данных с числом более 2 × 16. Однако он легко распространяется на (например) 2 ^ 60 64-битных целых чисел, запустив 4 прохода вместо 2 и легко адаптируется к использованию крошечной памяти, используя только столько бункеров, сколько поместилось в память, и увеличивая количество проходов соответственно, в время выполнения которого не больше O (n), а вместо этого O (n * log n).

  • Метод XOR'ing всех чисел вместе, упомянутый до сих пор rfrankel и, наконец, ircmaxell отвечает на вопрос, заданный в fooobar.com/questions/12863/..., как указывал ltn100. Он использует O (1) память и время O (n). Если на данный момент мы предполагаем 32-битные целые числа, XOR имеет 7% -ную вероятность создания отдельного числа. Обоснование: учитывая ~ 4G различных чисел XOR'd вместе, и ca. 300M не в файле, количество установленных бит в каждой битовой позиции имеет равные шансы быть нечетными или четными. Таким образом, 2 ^ 32 числа имеют равную вероятность возникновения как результат XOR, из которых 93% уже находятся в файле. Обратите внимание, что если числа в файле не все различны, вероятность успеха метода XOR возрастает.

Ответ 18

Задайте вопрос, если он не был указан неправильно. Просто прочитайте файл один раз, чтобы получить максимальное целое число n, и верните n+1.

Конечно, вам понадобится план резервного копирования, если n+1 вызывает переполнение целых чисел.

Ответ 19

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

Прочитайте первое число. Левая панель с нулевыми битами, пока у вас не будет 4 миллиарда бит. Если первый (высокий) бит равен 0, выход 1; else output 0. (Вам действительно не нужно левую панель: вы просто выводите 1, если в номере не хватает бит.) Сделайте то же самое со вторым номером, за исключением использования его второго бита. Продолжайте путь к файлу таким образом. Вы выведете бит 4-битного номера один бит за раз, и этот номер будет не таким, как любой в файле. Доказательство: это было то же самое, что и n-е число, тогда они согласились бы на n-й бит, но они не построены.

Ответ 20

Вы можете использовать битовые флаги, чтобы отметить, присутствует ли целое число.

После прохождения всего файла сканируйте каждый бит, чтобы определить, существует ли это число.

Предполагая, что каждое целое число равно 32 бит, они будут удобно помещаться в 1 ГБ ОЗУ, если будет выполняться бит.

Ответ 21

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

Пусть все возможные целые числа - это диапазон от int_min до int_max, и bool isNotInFile(integer) функция, которая возвращает true, если файл не содержит определенного целого и false else (путем сравнения этого определенного целого с каждым целым числом в файле)

for (integer i = int_min; i <= int_max; ++i)
{
    if (isNotInFile(i)) {
        return i;
    }
}

Ответ 22

Разделите пробел и не числовые символы из файла и добавьте 1. Теперь ваш файл содержит один номер, не указанный в исходном файле.

От Reddit от Carbonetc.

Ответ 23

Для ограничения памяти 10 МБ:

  • Преобразование числа в его двоичное представление.
  • Создайте двоичное дерево, где left = 0 и right = 1.
  • Вставьте каждое число в дерево, используя его двоичное представление.
  • Если число уже вставлено, листы уже созданы.

Закончив, просто создайте путь, который еще не был создан, чтобы создать запрошенный номер.

4 миллиарда чисел = 2 ^ 32, что означает, что 10 МБ может оказаться недостаточным.

ИЗМЕНИТЬ

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

EDIT II

Не нужно также строить дерево. Вам нужно только строить глубокие ветки, если числа схожи. Если мы также разрежем ветки, то это решение может работать на самом деле.

Ответ 24

Я отвечу на версию 1 GB:

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

Целое число составляет 32 бита с диапазоном от -2,147,483,648 до 2,147,483,647.

Псевдо-код:

var bitArray = new bit[4294967296];  // 0.5 GB, initialized to all 0s.

foreach (var number in file) {
    bitArray[number + 2147483648] = 1;   // Shift all numbers so they start at 0.
}

for (var i = 0; i < 4294967296; i++) {
    if (bitArray[i] == 0) {
        return i - 2147483648;
    }
}

Ответ 25

Пока мы делаем творческие ответы, вот еще один.

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

Ответ 26

Как сказал Райан в основном, отсортируйте файл, а затем перейдите по целым числам и когда значение пропущено там, вы его получите:)

EDIT в downvoters: OP упомянул, что файл может быть отсортирован, поэтому это допустимый метод.

Ответ 27

Устранение битов

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

long val = 0xFFFFFFFFFFFFFFFF; // (all bits set)
foreach long fileVal in file
{
    val = val & ~fileVal;
    if (val == 0) error;
}

Количество бит

Следите за количеством бит; и использовать биты с наименьшими суммами для генерации значения. Опять же, это не гарантирует получение правильного значения.

Логика диапазона

Следите за списком упорядоченных диапазонов (упорядочивается по началу). Диапазон определяется структурой:

struct Range
{
  long Start, End; // Inclusive.
}
Range startRange = new Range { Start = 0x0, End = 0xFFFFFFFFFFFFFFFF };

Пройдите каждое значение в файле и попробуйте удалить его из текущего диапазона. Этот метод не имеет гарантий памяти, но он должен делать довольно хорошо.

Ответ 28

2 128 * 10 18 + 1 (что (2 8) 16 * 10 18 + 1) - разве это не универсальный ответ на сегодня? Это число, которое не может быть сохранено в 16 файлах EB, что является максимальным размером файла в любой текущей файловой системе.

Ответ 29

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

Если существует ровно 4 294 967 295 (2 ^ 32 - 1) 32-битных целых чисел без повторов, и поэтому только один отсутствует, существует простое решение.

Запустите общее число в ноль и для каждого целого в файле добавьте это целое число с 32-разрядным переполнением (эффективно, runningTotal = (runningTotal + nextInteger)% 4294967296). После завершения добавьте 4294967296/2 к текущему итогу, снова с 32-битным переполнением. Вычтите это из 4294967296, и результатом будет отсутствующее целое число.

Проблема "только один недостающий целочисленный" разрешима только с одним прогоном и только 64 бит ОЗУ, выделенной для данных (32 для текущей суммы, 32 для чтения в следующем целом).

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

# Grab the file size
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
# Print a '2' for every bit of the file.
for (c=0; c<sz; c++) {
  for (b=0; b<4; b++) {
    print "2";
  }
}

Ответ 30

Если вы не предполагаете 32-битное ограничение, просто возвращайте случайно сгенерированное 64-битное число (или 128-бит, если вы пессимист). Вероятность столкновения 1 in 2^64/(4*10^9) = 4611686018.4 (примерно 1 из 4 миллиардов). Вы будете правы большую часть времени!

(Шутки... вроде.)