Я раскрываю все скрытые комментарии с правильными решениями к позавчерашней задачке. Если хотите все еще решать ее самостоятельно, не заглядывайте в комментарии. Спасибо всем, кто решал. Первый, кто нашел оптимальное решение за O(N) времени и O(1) памяти -
ilyaraz.
Вот еще одна программистская задачка. Я когда-то знал ее решение и забыл, недавно с удовольствием решил снова. Эта задачка довольно хорошо известна, и в частности часто преподается на курсах продвинутых структур данных, и упоминается в учебниках. Так что многие наверняка ее знают - если вы знаете решение, то не пишите в комментариях. Для тех, кто не знает, это отличное упражнение для ума, по-моему.
Задача: требуется воплотить массив с дефолтным значением для всех элементов, т.е. структуру данных с тремя операциями: get(i), set(i, value) и setdefault(value). Любой вызов get() с индексом, который до сих пор ни разу не передавался set(), должен возвращать последнее выставленное дефолтное значение (или 0, если такого не было). Сложность в том, что все три операции: get, set и setdefault, должны занимать O(1) времени. При этом нельзя рассчитывать на то, что отведенная нам память имеет какой-либо специальный вид, например, заполнена нулями. Любая используемая память изначально содержит случайные байты, которые мы никак не можем контролировать.
Вот еще одна программистская задачка. Я когда-то знал ее решение и забыл, недавно с удовольствием решил снова. Эта задачка довольно хорошо известна, и в частности часто преподается на курсах продвинутых структур данных, и упоминается в учебниках. Так что многие наверняка ее знают - если вы знаете решение, то не пишите в комментариях. Для тех, кто не знает, это отличное упражнение для ума, по-моему.
Задача: требуется воплотить массив с дефолтным значением для всех элементов, т.е. структуру данных с тремя операциями: get(i), set(i, value) и setdefault(value). Любой вызов get() с индексом, который до сих пор ни разу не передавался set(), должен возвращать последнее выставленное дефолтное значение (или 0, если такого не было). Сложность в том, что все три операции: get, set и setdefault, должны занимать O(1) времени. При этом нельзя рассчитывать на то, что отведенная нам память имеет какой-либо специальный вид, например, заполнена нулями. Любая используемая память изначально содержит случайные байты, которые мы никак не можем контролировать.
no subject
Date: 2011-01-07 03:05 pm (UTC)Как раз O(1) для set и get
no subject
Date: 2011-01-07 03:06 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2011-01-07 03:18 pm (UTC)no subject
Date: 2011-01-07 03:25 pm (UTC)Вы можете предположить, простоты ради, что максимальный размер массива известен заранее, и подготовить под него вашу структуру данных, но все эти подготовки должны занимать O(1) времени, а не O(n). Т.е. можно например заранее выделить нужную память с помощью malloc(), но нельзя обнулить ее всю в цикле, или что-то еще во все ее ячейки записать.
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2011-01-07 07:34 pm (UTC) - Expand(no subject)
From:(no subject)
From: (Anonymous) - Date: 2011-01-07 08:20 pm (UTC) - Expand(no subject)
From: (Anonymous) - Date: 2011-01-08 09:50 am (UTC) - Expandno subject
Date: 2011-01-07 03:19 pm (UTC)Если да, то я умею, буквально пару недель назад пришлось изобрести, чтобы не состариться за время инициализации :))
no subject
Date: 2011-01-07 03:29 pm (UTC)no subject
Date: 2011-01-07 03:25 pm (UTC)для хранения выделяется непрерывный участок виртуальной памяти - обычный массив в С++
плюс дополнительный "индексный" массив uint'ов длины array_length / sizeof(int) / 8 + 1 - т.е. по одному биту на элемент массива.
его придётся заполнить нулями, но эта операция в 32-64 раза быстрее чем заполнение нулями основного массива, которое в данном случае не потребуется
ну а дальше просто - по битовой маске и содержимому индексного массива понимаем были ли там пользовательские данные, и если нет - возвращаем текущее default-значение. Очевидно, set должен взводить бит соответствующей ячейки:
index[ i \ sizeof(int) ] & ( 1 << (i % sizeof(int)) )
no subject
Date: 2011-01-07 03:28 pm (UTC)no subject
Date: 2011-01-07 03:43 pm (UTC)no subject
Date: 2011-01-07 03:39 pm (UTC)Что-то я не догоняю.
Смотрю питоновый код.
берем входящие данные 1123
Идем по коду
def finddup(arr):
i = len(arr) - 1 # last //i=3
looplen = 0
while looplen < n:
i = arr[i] //i = arr[3] = 3
looplen += 1
if arr[i] == i: //3 == 3
# no loop, fixed point
return i //Ответ = 3
А должно быть 1.
Я понимаю, что это я дурак, но не пойму, где именно.
Просто я тоже, когда пытался решить, уперся в циклы, но не смог из них пользы достать со сложностью O(n).
no subject
Date: 2011-01-07 03:44 pm (UTC)Т.е. входящие данные должны быть 0012
(no subject)
From:(no subject)
From:no subject
Date: 2011-01-07 03:48 pm (UTC)При вызове setdefault(value) делаем lastDef = value и defCounter++.
При вызове set(i, value) в main[i] записываем value, а в aux[i] -- defCouner.
Смысл aux[i] -- "после aux[i] по счёту вызова setdefault() значение main[i] изменилось"
На вызов get(i) сначала сверям текущий defCounter и aux[i]. Если defCounter > aux[i], возвращаем lastDef (после последнего обновления дефолтного значения main[i] не менялся), если defCounter == aux[i], возвращаем main[i]
no subject
Date: 2011-01-07 03:59 pm (UTC)(no subject)
From:no subject
Date: 2011-01-07 04:18 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2011-01-07 04:49 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2011-01-07 04:44 pm (UTC)no subject
Date: 2011-01-07 06:18 pm (UTC)no subject
Date: 2011-01-07 05:03 pm (UTC)no subject
Date: 2011-01-07 06:12 pm (UTC)no subject
Date: 2011-01-07 06:05 pm (UTC)no subject
Date: 2011-01-07 06:11 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2011-01-07 06:13 pm (UTC)no subject
Date: 2011-01-07 07:00 pm (UTC)no subject
Date: 2011-01-07 07:17 pm (UTC)Но это как-то слишком просто!
no subject
Date: 2011-01-07 07:49 pm (UTC)(no subject)
From:no subject
Date: 2011-01-07 09:27 pm (UTC)Что-нибудь такое?
Date: 2011-01-07 09:32 pm (UTC)# будем учитывать все вызовы setval(), которые устанавливают значение для # какого-то индекса впервые counter=0 # количество вызовов setval(), впервые установивших значение для какого-либо индекса set_at=[] # для данного индекса i, set_at[i] == порядковому номеру вызова setval() iset_is=[] # для данного n - номера вызова setval(), iset_is[n] == индексу, для которого # установлено значение defval=0 size=1000 values=[] for i in range(size): # имитация случайной памяти values.append(random.randint(1,10000)) set_at.append(random.randint(1,10000)) iset_is.append(random.randint(1,10000)) def setdefval(x): global defval defval=x def setval(i,val): global iset_is, set_at, counter, values set_no=iset_is[i] if (set_no>=counter or set_at[set_no]<>i): iset_is[i]=counter set_at[counter]=i counter = counter + 1 values[i]=val def get(i): global iset_is, set_at, counter, values set_no=iset_is[i] if (set_no>=counter or set_at[set_no]<>i): return defval else: return values[i]Re: Что-нибудь такое?
Date: 2011-01-07 09:47 pm (UTC)Re: Что-нибудь такое?
From:Re: Что-нибудь такое?
From:Re: Что-нибудь такое?
From:Re: Что-нибудь такое?
From:Re: Что-нибудь такое?
From:no subject
Date: 2011-01-07 09:35 pm (UTC)int *aux1 = malloc(1000);
int *aux2 = malloc(1000);
int default = 0;
int counter = 0;
int get( int i )
{
if( counter >= 1000 )
return data[i];
if( i < counter && aux1[aux2[i]]!=i )
return default;
if( i >= counter )
return default;
return data[i];
}
void set( int i, int value )
{
data[i] = value;
if( counter >= 1000 )
return;
if( i >= counter ||
aux1[aux2[i]] != i )
{
aux1[counter] = i;
aux2[i] = counter;
++counter;
}
}
Как-то так, мне кажется :)
no subject
Date: 2011-01-07 09:58 pm (UTC)Попробуем так:
X[i] содержит значения.
default_x - значение по умолчанию.
Y[i] содержит "время" первой записи в X[i]. Эти "времена" представляют собой числа от 0 до максимально возможного i.
Z[t] содержит номер элемента, который был записан в момент времени t (t - от 0 до max_i).
max_t - содержит масимальное "время" на текущий момент.
init(): default_x = 0; max_t = 0;
setdefault(v): default_x = v;
is_set(i): return (Y[i] < max_t) && (Z[Y[i]] == i)
set(i, v): if (!is_set(i)) { Y[i]=max_t; Z[max_t++]=i; } X[i]=v;
get(i) if (is_set(i)) return X[i]; else return default_x;
Хотя мне почему-то кажется, что там, где я эту задачу встречал раньше, она решалась не с двумя, а с одним доп. массивом.
no subject
Date: 2011-01-07 10:27 pm (UTC)А вот как эта функция реализована на самом деле (http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strpbrk.c;h=620cfab7f9616ef45e7e059481f2f5232ae27e1e;hb=16c2895feabae0962e0eba2b9164c6a83014bfe4#l30). Ой.
no subject
Date: 2011-01-09 09:20 pm (UTC)(no subject)
From: (Anonymous) - Date: 2011-01-10 09:07 am (UTC) - Expand(no subject)
From:(no subject)
From: (Anonymous) - Date: 2011-01-10 11:37 am (UTC) - Expandno subject
Date: 2011-01-08 12:24 am (UTC)no subject
Date: 2011-01-08 01:04 am (UTC)Делаем вид, что не знаем про TLB (http://en.wikipedia.org/wiki/Translation_lookaside_buffer).
TLB для битовых флажокв dirty/read-write можно сделать руками, иерархичный. Тут уже несколько раз предлагали сделать битовый массив инициализации. Можно сделать битовый массив инициализированности для битового массива, затем битовый массив для битового массива инициализированности битового массива и тд. Один бит может соответствовать как раз размеру странички. Трёх-четырёх слоёв хватит для практических целей ( четыре слоя - это 40964 = 248 ).
Ещё можно в стек (реализованный как динамический массив типа std::vector<size_t> vec с capacity = N или std::deque) заносить уже инициализированные индексы массива array[i]. Что бы за (типа как бы) O(1) количества операций проверить есть ли индекс i в стеке - дополнительный массив index_at_vec[i] -> индекс в стеке. Если на месте index_at_vec[i] мусор из-за которого vec[index_at_vec[i]] не равно i ( или OutOfRange для vec ) - значит значение array[i] не инициализировано
no subject
Date: 2011-01-08 01:24 am (UTC)Первое решение не годится, так как не будет работать на компьютере Apple II, где нет ни SEH, ни сигналов :) Если серьёзно, то это должен быть чисто процедурный алгоритм, никак не опирающийся на особенности операционной системы.
Второе решение - та же инициализация за O(N), только размазанная во времени.
Третье решение правильное.
(no subject)
From:(no subject)
From:no subject
Date: 2011-01-08 02:13 am (UTC)int values[10000];
int wasSet[10000];
int indices[10000];
int defaultValue;
int setCount = 0;
void setDefault(int value) {defaultValue = value;}
int get(int i) {
if (i < 0 || i >= 10000) return defaultValue;
int setIndex = wasSet[i];
if (setIndex < 0 || setIndex >= setCount) return defaultValue;
if (indices[setIndex] != i) return defaultValue;
return values[i];
}
void set(int i, int value) {
if (i < 0 || i >= 10000) return;
values[i] = value;
int setIndex = wasSet[i];
if (setIndex >= 0 && setIndex < setCount && indices[setIndex] == i) return;
wasSet[i] = setCount;
indices[setCount] = i;
setCount++;
}
no subject
Date: 2011-01-08 02:14 am (UTC)