#c #recursion #memory-management #memory-optimization
#c #рекурсия #управление памятью #оптимизация памяти
Вопрос:
Я решаю задачу из школьного конкурса по программированию. Вот краткое описание:
У нас есть сетка высотой h и шириной w. Сетка заполнена символами ‘#’ и ‘.’. Восьмиугольники представляют землю, а точки представляют воду. Гарантируется, что первая и последняя строка и столбец сетки будут состоять из точек. Связанные октоторпы образуют острова, на которых могут быть озера, и каждое озеро также может иметь острова, и на этих островах могут быть озера и так далее… У каждого острова есть градус, который определяется как количество озер, которые нужно пересечь, чтобы добраться до него, если начинать с воды на краях сетки (эта вода рассматривается как море и исключается из числа озер, которые вы должны пересечь, чтобы добраться до острова). Найдите максимальную степень среди островов.
Я запускаю свою программу на школьном сервере и получаю сообщение о превышении лимита памяти, потому что программа предположительно занимает более 300 МБ памяти. Вот программа:
#include <stdio.h>
char a[3010][3010];
int degree[3010][3010];
bool visited[3010][3010];
int h, w;
void searchSame(int x, int y, int d)
{
degree[x][y] = d;
if(x 1 < h amp;amp; a[x 1][y] == a[x][y] amp;amp; degree[x 1][y] == -1)
searchSame(x 1, y, d);
if(x - 1 >= 0 amp;amp; a[x - 1][y] == a[x][y] amp;amp; degree[x - 1][y] == -1)
searchSame(x - 1, y, d);
if(y 1 < w amp;amp; a[x][y 1] == a[x][y] amp;amp; degree[x][y 1] == -1)
searchSame(x, y 1, d);
if(y - 1 >= 0 amp;amp; a[x][y - 1] == a[x][y] amp;amp; degree[x][y - 1] == -1)
searchSame(x, y - 1, d);
}
void search(int x, int y, int d)
{
visited[x][y] = true;
if(degree[x][y] == -1)
searchSame(x,y,d);
if(x 1 < h amp;amp; a[x 1][y] == a[x][y] amp;amp; !visited[x 1][y])
search(x 1, y, d);
else if(x 1 < h amp;amp; !visited[x 1][y])
search(x 1, y, degree[x][y] 1);
if(x - 1 >= 0 amp;amp; a[x - 1][y] == a[x][y] amp;amp; !visited[x - 1][y])
search(x - 1, y, d);
else if(x - 1 >= 0 amp;amp; !visited[x - 1][y])
search(x - 1, y, degree[x][y] 1);
if(y 1 < w amp;amp; a[x][y 1] == a[x][y] amp;amp; !visited[x][y 1])
search(x, y 1, d);
else if(y 1 < w amp;amp; !visited[x][y 1])
search(x, y 1, degree[x][y] 1);
if(y - 1 >= 0 amp;amp; a[x][y - 1] == a[x][y] amp;amp; !visited[x][y - 1])
search(x, y - 1, d);
else if(y - 1 >= 0 amp;amp; !visited[x][y - 1])
search(x, y - 1, degree[x][y] 1);
}
int main()
{
int mx = 0;
scanf("%d %d",amp;h,amp;w);
for(int i = 0; i < h; i )
scanf("%s",a[i]);
for(int i = 0; i < h; i )
{
for(int j = 0; j < w ; j )
degree[i][j] = -1;
}
search(0,0,1);
for(int i = 0; i < h; i )
{
for(int j = 0; j < w ; j )
{
if(degree[i][j] > mx)
mx = degree[i][j];
}
}
printf("%d",mx/2 - 1);
}
Значения h и w варьируются от 3 до 3000. Почему эта программа занимает так много памяти? Может быть, это как-то связано с рекурсивными функциями? Как я могу улучшить управление памятью?
Комментарии:
1. Добро пожаловать в Code Review! Этот вопрос привлекает голоса «вниз» и «закрыть», потому что он не по теме — мы не отвечаем на вопросы о том, как исправить код (или как понять код) — мы рассматриваем только рабочий код, автором / сопровождающим которого является постер, и который постер понимает. Подробности см. на странице по теме .
2. @Dannnno Но разве это не то, что я делаю? Я написал этот код, а не кто-то другой. Я понимаю этот код. Моя программа работает отлично, она просто занимает слишком много памяти. Я спрашиваю о том, почему требуется так много памяти, чтобы знать, как улучшить управление памятью.
3. Перейдите от рекурсивного к итеративному, используя некоторую компактную форму, чтобы отслеживать, где вы были и где вы остановились.
4. Программа потенциально выполняет миллионы вложенных рекурсивных вызовов. Это не поддерживается по умолчанию ни одной обычной ОС.
5. Я попробовал вашу программу, она выполняет сегментацию на глубине около 200 000 вызовов с настройками размера стека по умолчанию и на глубине 3 000 000 с максимальными настройками.
Ответ №1:
int degree[3010][3010];
резервирует пространство для 3010 * 3010 int
сек, то есть более 9 миллионов. Если вы используете 32-битную систему, это означает, что вам нужно 9,06 миллионов раз по 4 байта = 36,2 МБ только для этого массива; в 64-битной системе (стандартной на сегодняшний день) для этого требуется 72,5 МБ.
Добавьте место для двух других, и вы поймете, почему программе требуется так много места.
Комментарии:
1. » в 64-битной системе (стандартной на сегодняшний день) ей требуется 72,5 МБ » — что? Почему? Какая система использует 8-байтовые
int
секунды? Никогда не слышал о таком.2. Как размеры массивов в сумме превышают 300 Мбайт? Прежде всего, есть аргумент Fureeish. Во-вторых, даже если бы у меня было три таких массива целых чисел и даже если бы вы были правы относительно размера целых чисел, 72,5 МБ * 3 = 217,5 МБ. В-третьих, у меня есть только один массив целых чисел. Два других массива char и bool намного меньше. Я действительно не понимаю, как размеры массивов составляют более 300 Мбайт. Я что-то упускаю?
3. Для быстрого доступа компилятор помещает каждый
bool
вint
= 4 байта. В 64-разрядной системе каждомуint
присваивается (быстро адресуемая) граница в 8 байт, что означает, что даже bool использует 8 байт. Вы можете контролировать это с помощью флагов компилятора — упаковкаbool
s в байты или даже биты делает программу намного меньше (и намного медленнее). И, наконец, есть некоторые другие накладные расходы, такие как стандартные библиотеки и сам код.4. Если вы в это не верите, просто измените один из массивов на другой размер и проверьте, как исполняемый файл сжимается.
5. Я уменьшил массив целых чисел до высоты 1 и ширины 1. Использование памяти уменьшилось всего на 34,5 Мбайт. Однако, когда я изменил массив bool на тот же размер, использование памяти резко сократилось. Так что, скорее всего, это как-то связано с рекурсивными функциями.