Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
...и все это должно быть написано на С (не путать с C++) и работать под Linux. В общем, переизобрести GNU "sort -n". Ладно, я, покурив, сочинил овнокод, отослал, никакого ответа, ессно — как модно нынче стало выражаться, "слив засчитан". Но все равно интересно, чтобы коллеги посмотрели со стороны и высказали замечания — на будущее.
Собственно, вот оно:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/*
Merge-sort algorithm implementation.
Application read data from stdin, sort it using merge-sort
with RAM usage limitation and write data to stdout.
Data values are integers in range 1..32768
(NB! Zero value is excluded).
*/
/* RAM buffer size (in bytes)*/#define BUF_RAW_SIZE 4096
/* Input values total number limit */#define NUM_VALUES_MAX 32768
/* Value type */typedef unsigned VAL_T;
/* Iterator type */typedef unsigned ITER_T;
/* Size of one input value (in bytes) */#define VAL_SIZE sizeof(VAL_T)
/* RAM buffer size (in input values) */#define BUF_SIZE (BUF_RAW_SIZE / VAL_SIZE)
/* Temporary files quantity limit */#define NUM_TMP_FILES_MAX (NUM_VALUES_MAX / BUF_SIZE + 1)
/* Single data value format string, is used by printf/scanf */#define VAL_FMT "%u\n"/* Cast some arbitrary pointer to (VAL_T*) and dereference it */#define VAL_DEREF(Z) (*((const VAL_T*)Z))
/* Struct represents data stored in the temporary file */typedef struct {
/*
Temporary file associated with data,
NULL if file is not created or was exhausted
*/
FILE* pfile;
/*
Stack top value, is 0 if no data is present;
NB! We rely, that each data value is >= 1
*/
VAL_T top;
} OnDiskStack;
/* Size of one OnDiskStack instance */#define ODS_SIZE (sizeof(OnDiskStack))
/* Get the top value of OnDiskStack instance given by pointer to. */#define ODSREF_TOP(Z) (((const OnDiskStack*)Z)->top)
/*
Predicate, compares two VAL_T values,
is needed by std library qsort algorithm.
See qsort info for detailes.
*/int compare(const void* a, const void* b) {
return (VAL_DEREF(a) > VAL_DEREF(b)) - (VAL_DEREF(a) < VAL_DEREF(b));
}
/*
Predicate, compares two OnDiskStack instances by
top values. Is used by std library qsort algorithm
and also at stacks resorting (see main(), resort() functions).
Truth table:
a->top | b->top | compare_s(a, b)
------------+-----------+-----------------
0 | 0 | 0
------------+-----------+-----------------
S!=0 | 0 | -1
------------+-----------+-----------------
0 | S!=0 | 1
------------+-----------+-----------------
S1 | S2 | compare(S1, S2)
*/int compare_s(const void* a, const void* b) {
return
compare(&ODSREF_TOP(a), &ODSREF_TOP(b)) *
((ODSREF_TOP(a) && ODSREF_TOP(b)) ? 1: -1);
}
/*
Sort data in ascending order by std library qsort
and place to OnDiskStack (i.e. to temporary file);
Arguments:
buf - pointer to data buffer;
size - buffer size;
stack - pointer to destination stack (
stack s.b. empty, with no temporary file associated,
stack data would be lost otherwise
).
*/void sortnflush (
VAL_T* buf, ITER_T size,
OnDiskStack* stack
) {
stack->pfile = NULL;
stack->top = 0;
switch (size) {
case 1:
stack->top = buf[0];
case 0:
break;
default:
if (!(stack->pfile = tmpfile())) {
fprintf(stderr, "Temporary file creation failed\n");
exit(EXIT_FAILURE);
};
qsort(buf, size, VAL_SIZE, compare);
stack->top = buf[0];
/*
HACK! Write operation may be interrupted
in some cicrcuimstances, but we treat
such situation as an error
*/if (fwrite(&buf[1], VAL_SIZE, size - 1, stack->pfile) != (size - 1)) {
fprintf(stderr, "Temporary file write failed\n");
exit(EXIT_FAILURE);
};
rewind(stack->pfile);
break;
};
}
/*
Stacks resorting.
Stacks [1..(num_stacks - 1)] are assumed to be sorted.
Find the place to insert the first stack, and do insert.
*/void resort(OnDiskStack* stacks, ITER_T num_stacks) {
OnDiskStack buf_stack;
switch (num_stacks) {
case 2:
if (compare_s(&stacks[0], &stacks[1]) == 1) {
buf_stack = stacks[0];
stacks[0] = stacks[1];
stacks[1] = buf_stack;
};
case 0:
;
case 1:
break;
default:
if (compare_s(&stacks[0], &stacks[1]) == 1) {
ITER_T beg = 1;
ITER_T end = num_stacks;
ITER_T mid;
buf_stack = stacks[0];
/*
Search the place to insert stacks[0] in
range [beg, end)
*/do {
mid = (end + beg) / 2;
switch (compare_s(&buf_stack, &stacks[mid])) {
case 0:
beg = end = mid;
break;
case 1:
beg = mid;
break;
case -1:
end = mid;
break;
default:
/* We should never go here */
assert(0);
break;
};
} while ((end - beg) > 1);
memmove(stacks, &stacks[1], beg * ODS_SIZE);
stacks[beg] = buf_stack;
};
break;
};
}
/* Application entry point */int main(int argc, char* argv[]) {
/* On-disk stacks to store sorted portions of input data */static OnDiskStack stacks[NUM_TMP_FILES_MAX];
/* Actual number of temporary storages used */
ITER_T num_stacks;
/*
Read input data to buffer, sort buffer
and put to on-disk stack when buffer is full or
when stdin is terminated
*/
{
/* Input data buffer */static VAL_T buf[BUF_SIZE];
/* Actual number of items stored in buffer*/
ITER_T num_bufd = num_stacks = 0;
int scan_ok;
do {
scan_ok = (scanf(VAL_FMT, &buf[num_bufd]) == 1);
if (scan_ok) {
if (!buf[num_bufd]) {
fprintf(stderr, "Input data s.b. > 0\n");
exit(EXIT_FAILURE);
};
++num_bufd;
};
if ((BUF_SIZE == num_bufd) || (!scan_ok)) {
if (NUM_TMP_FILES_MAX == num_stacks) {
fprintf(stderr, "Input data limit exceeded\n");
exit(EXIT_FAILURE);
};
sortnflush(buf, num_bufd, &stacks[num_stacks]);
num_stacks++;
num_bufd = 0;
};
} while (scan_ok);
};
/*
Merge all data in stacks, write data to stdout
*/if (!num_stacks)
return EXIT_SUCCESS;
{
qsort(stacks, num_stacks, ODS_SIZE, compare_s);
while (stacks[0].top) {
printf(VAL_FMT, stacks[0].top);
if (!stacks[0].pfile) {
stacks[0].top = 0;
} else if (fread(&stacks[0].top, VAL_SIZE, 1, stacks[0].pfile) != 1) {
if (feof(stacks[0].pfile)) {
stacks[0].pfile = NULL;
stacks[0].top = 0;
} else {
fprintf(stderr, "Temporary file read failed\n");
exit(EXIT_FAILURE);
};
};
resort(stacks, num_stacks);
};
};
return EXIT_SUCCESS;
}
Заранее спасибо
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
_>
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
_>...и все это должно быть написано на С (не путать с C++) и работать под Linux. В общем, переизобрести GNU "sort -n". Ладно, я, покурив, сочинил овнокод, отослал, никакого ответа, ессно — как модно нынче стало выражаться, "слив засчитан". Но все равно интересно, чтобы коллеги посмотрели со стороны и высказали замечания — на будущее.
_>Собственно, вот оно: _>
_>...
_>
Я так думаю, что они все еще изучают твой очень длинный-длинный код...
А нельзя было сделать так: выделить 4Кб памяти, по биту на каждое возможное значение, далее пробежать по файлу и отметить те значения, которые там встретились, а дальше просто пробежать по 4Кб памяти и вывести те числа, которые в файле были:
char iwasthere[32768 / 8] = {}; // 4Кбwhile (ifil >> num)
iwasthere[num / 8] |= (1 << (num % 8));
for (int i = 0; i < 32768; ++i)
if (iwasthere[i / 8] & (1 << (i % 8)))
ofil << i << endl;
Здравствуйте, Sergey, Вы писали:
S>На будущее я посоветовал бы знакомиться в таких случаях с творчеством Д. Кнута. Конкретно для этого случая рулит раздел 5.4 третьего тома — "Внешняя сортировка".
Achtung, ОФФТОП:
Да, только для таких случаев труд Кнута "Искусство программирования в сферическом вакууме" и нужен. Я серьезно — кроме как для решения тестовых задач мне ни разу не пришлось его листать. Увы, но то, что пишет дедушка Кнут, на практике мало востребованно (во всяком случае в моей практике) — гораздо чаще приходилось листать Майерса, книгу "Банды Четырех", Гласса, Бека. Ах да, раз мне потребовалось при решении задачи допускового анализа почитать то, что пишет Кнут про обход кортежей. Все здорово, но его супероптимизированный алгоритм считал минут двадцать, а мой некошерный управился за несколько секунд. И это не в упрек Кнуту будь сказано, просто его алгоритм обхода был рассчитан на некий абстрактный процедурно-ориентированный язык программирования, и все было организовано при помощи циклов, а я писал под Octave, и реализовал алгоритм в терминах операций с матрицами — дальнейшие пояснения, думаю, излишни. В конкретных условиях работы "неправильный" алгоритм оказался лучше "правильного". Поэтому я и хулигански приписал про "сферический вакуум"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, Sergey, Вы писали:
S>>На будущее я посоветовал бы знакомиться в таких случаях с творчеством Д. Кнута. Конкретно для этого случая рулит раздел 5.4 третьего тома — "Внешняя сортировка".
_>Achtung, ОФФТОП:
_>Да, только для таких случаев труд Кнута "Искусство программирования в сферическом вакууме" и нужен. Я серьезно — кроме как для решения тестовых задач мне ни разу не пришлось его листать. Увы, но то, что пишет дедушка Кнут, на практике мало востребованно (во всяком случае в моей практике) — гораздо чаще приходилось листать Майерса, книгу "Банды Четырех", Гласса, Бека. Ах да, раз мне потребовалось при решении задачи допускового анализа почитать то, что пишет Кнут про обход кортежей. Все здорово, но его супероптимизированный алгоритм считал минут двадцать, а мой некошерный управился за несколько секунд. И это не в упрек Кнуту будь сказано, просто его алгоритм обхода был рассчитан на некий абстрактный процедурно-ориентированный язык программирования, и все было организовано при помощи циклов, а я писал под Octave, и реализовал алгоритм в терминах операций с матрицами — дальнейшие пояснения, думаю, излишни. В конкретных условиях работы "неправильный" алгоритм оказался лучше "правильного". Поэтому я и хулигански приписал про "сферический вакуум"
Зря ты так. Я думаю ты очень часто пользуешься трудами дедушки Кнута, даже не подозревая об этом...
_>Да, только для таких случаев труд Кнута "Искусство программирования в сферическом вакууме" и нужен. Я серьезно — кроме как для решения тестовых задач мне ни разу не пришлось его листать.
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
_>...и все это должно быть написано на С (не путать с C++) и работать под Linux. В общем, переизобрести GNU "sort -n". Ладно, я, покурив, сочинил овнокод, отослал, никакого ответа, ессно — как модно нынче стало выражаться, "слив засчитан". Но все равно интересно, чтобы коллеги посмотрели со стороны и высказали замечания — на будущее.
На будущее я посоветовал бы знакомиться в таких случаях с творчеством Д. Кнута. Конкретно для этого случая рулит раздел 5.4 третьего тома — "Внешняя сортировка".
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, superdeveloper, Вы писали:
S>Что считать памятью? S>Регистры тоже память? S>Если нет, то всё можно сделать через регистры.
S>Вообще в Windows есть разные виды ведения памяти, commit/reserve и .т.д.
Я ж говорил:
и работать под Linux
S>Можно ли выделять временные файлы, можно ли несколько раз считать исходный файл и т.п.?
ИМХО, ты все усложняешь. Тестовое задание по определению представляет собой этюд, а не "взрослый" проект.
S>Надо поставить более точную задачу.
Вспоминается известная история, как Ходжа Насреддин спорил с джинном, кто дальше зашвырнет скалу (вариация на тему Пушкинской сказки о Балде). Джинн кинул огромную скалу на сто шагов. Насреддин же подошел к скале и стал вслух рассуждать: "Если я кину ее на восток — она будет мешать солнцу восходить, и мир погрузится во мрак, если кину на запад — будет мешать солнцу заходить, и весь мир изжарится, кину на север — перестанет дуть ветер, и весь мир протухнет..." ну ит.д. Короче, застращал джинна до того, что тот стал упрашивать Насреддина не трогать скалу.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
_>Как ни странно, мне эта мысль тоже приходила в голову. И насчет неопределенности формата данных в файле тоже мелькало, но я забил. А условие и в самом деле точно такое. А вот до решения с хранением флагов я как-то не допер... Тут надо или действительно быстро соображать, или просто знать готовое решение — задача из серии "Постройте 4 треугольника из 6-ти спичек", "Сколько раз надо спуститься в подвал, чтобы проверить какой выключатель какую лампочку контролирует", "Как измерить высоту дома при помощи барометра" ит.д. (список можно продолжать). Буду знать...
Вот в этой книжке "Жемчужины программирования" описана твоя задача. Книга эта не так объёмна, как тот же Кнут, но там рассматриваются всякие интересные задачи на пределах возможности машины. А вообще, согласен, что это тестовое задание на знание алгоритмов. Сходу, не прочитав определённую литературу, правильное решение не получится.
Здравствуйте, vadimcher, Вы писали:
V>А нельзя было сделать так: выделить 4Кб памяти, по биту на каждое возможное значение, далее пробежать по файлу и отметить те значения, которые там встретились, а дальше просто пробежать по 4Кб памяти и вывести те числа, которые в файле были
Кстати, да. Осталась одна надежда — гордо заявить, что мой код лучше, потому что будет быстрее работать (ну мало ли, бывают же чудеса, хе-хе). Надо попробовать написать твой вариант и прогнать под gprof-ом.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
А условие точно такое? Если заменить в первой фразе написать "Есть файл, который содержит 32768 целых" задача как раз для собеседования
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Здравствуйте, slava_phirsov, Вы писали:
СМ>А условие точно такое? Если заменить в первой фразе написать "Есть файл, который содержит 32768 целых" задача как раз для собеседования
Как ни странно, мне эта мысль тоже приходила в голову. И насчет неопределенности формата данных в файле тоже мелькало, но я забил. А условие и в самом деле точно такое. А вот до решения с хранением флагов я как-то не допер... Тут надо или действительно быстро соображать, или просто знать готовое решение — задача из серии "Постройте 4 треугольника из 6-ти спичек", "Сколько раз надо спуститься в подвал, чтобы проверить какой выключатель какую лампочку контролирует", "Как измерить высоту дома при помощи барометра" ит.д. (список можно продолжать). Буду знать...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
S>На будущее я посоветовал бы знакомиться в таких случаях с творчеством Д. Кнута. Конкретно для этого случая рулит раздел 5.4 третьего тома — "Внешняя сортировка".
в данном случае нужен Бентли с его "жемчужинами программирования" — у него такой пример был и решение было таким же, как тут предложили (vadimcher). как раз нафиг кнут не нужен тут.
Of course, the code must be complete enough to compile and link.
V>Я так думаю, что они все еще изучают твой очень длинный-длинный код... V>А нельзя было сделать так: выделить 4Кб памяти, по биту на каждое возможное значение, далее пробежать по файлу и отметить те значения, которые там встретились, а дальше просто пробежать по 4Кб памяти и вывести те числа, которые в файле были:
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, vadimcher, Вы писали:
V>>А нельзя было сделать так: выделить 4Кб памяти, по биту на каждое возможное значение, далее пробежать по файлу и отметить те значения, которые там встретились, а дальше просто пробежать по 4Кб памяти и вывести те числа, которые в файле были
_>Кстати, да. Осталась одна надежда — гордо заявить, что мой код лучше, потому что будет быстрее работать (ну мало ли, бывают же чудеса, хе-хе). Надо попробовать написать твой вариант и прогнать под gprof-ом.
Здесь только два нюанса. Чтобы побыстрее работало массив лучше делать интами типа int[32768 / 8 / sizeof(int)], и, кроме того, у тебя входящие числа не от 0 до 32767, а от 1 до 32768, будь осторожен!
Здравствуйте, Тролль-323, Вы писали:
_>>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768.
Т3>Так… Что-то я туплю на ночь глядя…
Т3>Это как бы шутка такая? На внимательность?
Т3>Тогда ведь получается, что он содержит каждое число от 1 до 32768 по одному разу.
Т3>Сортированный файл просто будет содержать тупо числа от 1 до 32768?
Здравствуйте, Sashaka, Вы писали:
S>Здравствуйте, Тролль-323, Вы писали:
_>>>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768.
Т3>>Так… Что-то я туплю на ночь глядя… Т3>>Это как бы шутка такая? На внимательность? Т3>>Тогда ведь получается, что он содержит каждое число от 1 до 32768 по одному разу. Т3>>Сортированный файл просто будет содержать тупо числа от 1 до 32768?
S>может содержать 32768, а может и меньше ? =)
А может и больше. А может вообще содержать не числа, а строки
В общем, аккуратнее надо с формулировками...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
Read data from stdin, sort it
(RAM usage is strongly limited)
and write data to stdout.
Data values are integers in range 1..32768
(NB! Zero value is excluded, each input value
in sequence is unique, non-repetitive).
Algorithm doesn't store input data in buffer,
it stores one bit for each possible value, indicating
whether this particular value was encountered or not.
*/
/* RAM buffer size (in bytes)*/#define BUF_SIZE 4096
/* Value type */typedef unsigned VAL_T;
/* Iterator type */typedef unsigned ITER_T;
/* Single data value format string, is used by printf/scanf */#define VAL_FMT "%u\n"/* Get particular bit in particular byte value*/#define GET_BIT (buf[byte_pos] & (MASKS[bit_pos]))
/* Application entry point */int main(int argc, char* argv[]) {
/* Masks used to select single bit from the byte */static unsigned char MASKS[8] = {1, 2, 4, 8, 16, 32, 64, 128};
/* Input data buffer */static unsigned char buf[BUF_SIZE];
memset(buf, 0, BUF_SIZE);
/*
Read input data from stdin and accumulate it into buffer
in compressed form
*/
{
VAL_T value;
while (scanf(VAL_FMT, &value) == 1) {
ITER_T byte_pos, bit_pos;
if (!value) {
fprintf(stderr, "Input data s.b. > 0\n");
exit(EXIT_FAILURE);
};
--value;
byte_pos = value / 8;
bit_pos = (value % 8);
if (GET_BIT) {
fprintf(stderr, "Input data is duplicated\n");
exit(EXIT_FAILURE);
};
buf[byte_pos] |= MASKS[bit_pos];
};
};
/*
Write accumulated data to stdout
*/
{
ITER_T byte_pos, bit_pos;
for (byte_pos = 0; byte_pos < BUF_SIZE; ++byte_pos)
for (bit_pos = 0; bit_pos < 8; ++bit_pos)
if (GET_BIT)
printf(VAL_FMT, byte_pos * 8 + bit_pos + 1);
};
return EXIT_SUCCESS;
}
Как и следовало ожидать, gprof не сумел замерить скорость выполнения — выдал 0.0
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
_>>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
СМ>А условие точно такое? Если заменить в первой фразе написать "Есть файл, который содержит 32768 целых" задача как раз для собеседования
А блин реально задача на внимательность
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, vadimcher, Вы писали:
V>Зря ты так. Я думаю ты очень часто пользуешься трудами дедушки Кнута, даже не подозревая об этом...
И даже подозревая — когда работаю в Texmaker'е А когда я втыкаю комп в розетку, я получаю электроэнергию от ЛАЭС, т.е., сам того не подозревая, пользуюсь трудами Курчатова, вернее, Бора, вернее Эйнштейна — ну, короче, идея ясна. Пафос был не в том, что Кнут — не крут, а в том, что в повседневности его книга, вот лично мне, как среднему (во всех смыслах) кодеру не очень-то и нужна... Ты (и другие знатоки трудов Кнута) можешь меня поправить, но вот темы, которые реально мне нужны каждый (или почти каждый) день, и о которых Кнут не пишет:
Разработка архитектуры на макро или на микро-уровне (можно тут еще вспомнить недавние споры в этой же ветке форума на предмет — когда надо кидать исключение, когда возвращать код ошибки, а когда использовать assert)
Объектно-ориентированное программирование (паттерны)
Функциональное программирование
Организация работы программиста
Тестирование
Вот эту повседневность я у Кнута не нашел (может быть, плохо искал? ОК, ткните носом, плиз). Пример с Октавой я привел выше — Кнут ведь писал "вообще", а в каждом конкретном случае общее решение как правило проигрывает (хороший философский принцип, примером его могут служить и две реализации лисапеда, породившего данный топик — "общая", которая "по-честному" собирает данные во временные файлы и "чисто конкретная", которая хранит только флаги).
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
в предложенных решениях сразу нарушается условие задачи — 4кб т.к. выделяется место под массив и есть хотябы одна переменная )
также не учтено потребление памяти при загрузке программы
Здравствуйте, Sashaka, Вы писали:
Т3>>Тогда ведь получается, что он содержит каждое число от 1 до 32768 по одному разу.
Т3>>Сортированный файл просто будет содержать тупо числа от 1 до 32768?
S>может содержать 32768, а может и меньше ? =)
Вначале выделить массив целых из 32768. Затем обнулить его.
Затем заполнять.
При выводе игнорировать нули.
Минусы: неэффективно при маленьком размере файла.
Если немного подумать, то оказывается, что значение массива будет зависеть от индекса.
Поэтому массив целых можно заменить на массив булевых констант.
Если надо оптимизировать по памяти, то можно сжать булевы константы до битовых флагов, как писал vadimcher.
Господа, задавшие задание, таки вышли на связь — ни вариант с "честной сортировкой", ни предложенный тут вариант с флагами их не устроили. Цитирую дословно(но по памяти): "У вас не было выполнено условие использовать не более 4 кбайт". Мне чисто теоретически стало интересно, но спросить как-то постеснялся — а что они тогда имели в виду? Что общий размер занимаемой приложением памяти не должен быть более 4 кбайт? У меня приложение "return 0" (не путать с "Hello, world!") заняло 4,2 кбайта, а приложение просто пишущее 10 байт во временный файл — так и все 5-6 кбайт. То есть даже с полным использованием внешней сортировки, когда все данные лежат только в файле (не будем уточнять, какие именно данные — "честно" отсортированные числа, или флаги) меньше 5 не получится. Есть конечно разные компиляторы, у них есть разные опции компиляции, есть наконец разные машины... Ну так по этой части в задании ничего сказано не было вообще. Короче то ли это у них дежурная отмазка такая, то ли я чего-то не просек, то ли господа сами не знают чего хотят.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Господа, задавшие задание, таки вышли на связь — ни вариант с "честной сортировкой", ни предложенный тут вариант с флагами их не устроили. Цитирую дословно(но по памяти): "У вас не было выполнено условие использовать не более 4 кбайт". Мне чисто теоретически стало интересно, но спросить как-то постеснялся — а что они тогда имели в виду? Что общий размер занимаемой приложением памяти не должен быть более 4 кбайт? У меня приложение "return 0"
Может они даже разбираться не стали, код читается очень тяжело если честно
А про флаги они видимо не поняли. Там как раз ровно 4кб нужно. (32кб/8)
Обычно по одной задачке не судят. Наверное "не понравилось" или программист не сильно срочно нужен.
Здравствуйте, slava_phirsov, Вы писали:
_>Спасибо, коллеги, за помощь, и все такое
_>Господа, задавшие задание, таки вышли на связь — ни вариант с "честной сортировкой", ни предложенный тут вариант с флагами их не устроили. Цитирую дословно(но по памяти): "У вас не было выполнено условие использовать не более 4 кбайт". Мне чисто теоретически стало интересно, но спросить как-то постеснялся — а что они тогда имели в виду? Что общий размер занимаемой приложением памяти не должен быть более 4 кбайт? У меня приложение "return 0" (не путать с "Hello, world!") заняло 4,2 кбайта, а приложение просто пишущее 10 байт во временный файл — так и все 5-6 кбайт. То есть даже с полным использованием внешней сортировки, когда все данные лежат только в файле (не будем уточнять, какие именно данные — "честно" отсортированные числа, или флаги) меньше 5 не получится. Есть конечно разные компиляторы, у них есть разные опции компиляции, есть наконец разные машины... Ну так по этой части в задании ничего сказано не было вообще. Короче то ли это у них дежурная отмазка такая, то ли я чего-то не просек, то ли господа сами не знают чего хотят.
И еще, пожалуй, на будущее я бы тебе посоветовал писать попроще. Во-первых, как правило, задачки проверяют один-два конкретных вопроса (как здесь, например, знаешь ли ты стандартные методы сортировки и можешь ли ты при этом уложиться в требуемую память). Поэтому решение должно быть по возможности коротким, ясным и решающим конкретную поставленную задачу наиболее простым, но эффективным способом. Из решения должно быть сразу ясно КАК ты решил задачу. Во-вторых, ты у них не один, и у них просто нет возможности, времени и желания "все это читать и разбираться". Никто не будет тратить больше двух минут на твое решение.
Здравствуйте, slava_phirsov, Вы писали:
_>Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
_>
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768.
Супер.
Сколько целых:32768
Какой диапазон:1...32768
Повторяются: Нет.
Следовательно, там будут только эти числа из диапазона, 1, 2, 3,..., 32768.
Других чисел не будет вообще.
Сортировать не надо.
Просто надо записать числа от 1 до 32768
Всё.
Примерно так
int main()
{
istream stream("file.txt");
for(int i=1;i<=32768;i++)
{
stream<<i<<" ";
}
//деструктор закроет файл
};
Здравствуйте, slava_phirsov, Вы писали:
_>Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
_>
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768.
Супер.
Сколько целых:32768
Какой диапазон:1...32768
Повторяются: Нет.
Следовательно, там будут только эти числа из диапазона, 1, 2, 3,..., 32768.
Других чисел не будет вообще.
Сортировать не надо.
Просто надо записать числа от 1 до 32768
Всё.
Примерно так
int main()
{
ostream stream("file.txt");
for(int i=1;i<=32768;i++)
{
stream<<i<<" ";
}
//деструктор закроет файл
};
Здравствуйте, superdeveloper, Вы писали:
S>Здравствуйте, slava_phirsov, Вы писали:
_>>Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
_>>
_>>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768.
Здравствуйте, x905, Вы писали:
X>ключевое слово "_может_", ага
Выделяем массив, равный 32768/8=4096 байт
каждому биту ставим в соответствие одно число 1...32768
число — номер бита (число-1)
соответственно биты будут 0...32767
Алгоритм:
1. Обнуляем массив
2. Проходимся по файлу, если это число есть, то включаем бит (число-1)
3. Проходим по всем битам, если бит включен, то заносим в выходной файл число (номер_бита+1)
4.конец агоритма.
Здравствуйте, superdeveloper, Вы писали:
S>Здравствуйте, x905, Вы писали:
X>>ключевое слово "_может_", ага
S>Выделяем массив, равный 32768/8=4096 байт S>каждому биту ставим в соответствие одно число 1...32768 S>число — номер бита (число-1) S>соответственно биты будут 0...32767
S>Алгоритм: S>1. Обнуляем массив S>2. Проходимся по файлу, если это число есть, то включаем бит (число-1) S>3. Проходим по всем битам, если бит включен, то заносим в выходной файл число (номер_бита+1) S>4.конец агоритма.
S>вот.
S>ps. Приём известный.
тред с начала читаем ? уже было, ага )
вопрос есть поинтересней: 4кб — это только на массив, а переменные?, а rintime?
даже helloworld вылезет за 4кб
и как все в 4кб всеже впихнуть?
Здравствуйте, slava_phirsov, Вы писали:
_>Доброго времени суток всем читающим! Дали мне в одной фирме тестовое задание:
_>
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
_>...и все это должно быть написано на С (не путать с C++) и работать под Linux. В общем, переизобрести GNU "sort -n". Ладно, я, покурив, сочинил овнокод, отослал, никакого ответа, ессно — как модно нынче стало выражаться, "слив засчитан". Но все равно интересно, чтобы коллеги посмотрели со стороны и высказали замечания — на будущее.
_>Собственно, вот оно:
_>
_>#include <stdio.h>
_>#include <stdlib.h>
_>#include <string.h>
_>#include <assert.h>
_>/*
_> Merge-sort algorithm implementation.
_> Application read data from stdin, sort it using merge-sort
_> with RAM usage limitation and write data to stdout.
_> Data values are integers in range 1..32768
_> (NB! Zero value is excluded).
_>*/
_>/* RAM buffer size (in bytes)*/
_>#define BUF_RAW_SIZE 4096
_>/* Input values total number limit */
_>#define NUM_VALUES_MAX 32768
_>/* Value type */
_>typedef unsigned VAL_T;
_>/* Iterator type */
_>typedef unsigned ITER_T;
_>/* Size of one input value (in bytes) */
_>#define VAL_SIZE sizeof(VAL_T)
_>/* RAM buffer size (in input values) */
_>#define BUF_SIZE (BUF_RAW_SIZE / VAL_SIZE)
_>/* Temporary files quantity limit */
_>#define NUM_TMP_FILES_MAX (NUM_VALUES_MAX / BUF_SIZE + 1)
_>/* Single data value format string, is used by printf/scanf */
_>#define VAL_FMT "%u\n"
_>/* Cast some arbitrary pointer to (VAL_T*) and dereference it */
_>#define VAL_DEREF(Z) (*((const VAL_T*)Z))
_>/* Struct represents data stored in the temporary file */
_>typedef struct {
_> /*
_> Temporary file associated with data,
_> NULL if file is not created or was exhausted
_> */
_> FILE* pfile;
_> /*
_> Stack top value, is 0 if no data is present;
_> NB! We rely, that each data value is >= 1
_> */
_> VAL_T top;
_>} OnDiskStack;
_>/* Size of one OnDiskStack instance */
_>#define ODS_SIZE (sizeof(OnDiskStack))
_>/* Get the top value of OnDiskStack instance given by pointer to. */
_>#define ODSREF_TOP(Z) (((const OnDiskStack*)Z)->top)
_>/*
_> Predicate, compares two VAL_T values,
_> is needed by std library qsort algorithm.
_> See qsort info for detailes.
_>*/
_>int compare(const void* a, const void* b) {
_> return (VAL_DEREF(a) > VAL_DEREF(b)) - (VAL_DEREF(a) < VAL_DEREF(b));
_>}
_>/*
_> Predicate, compares two OnDiskStack instances by
_> top values. Is used by std library qsort algorithm
_> and also at stacks resorting (see main(), resort() functions).
_> Truth table:
a->>top | b->top | compare_s(a, b)
_> ------------+-----------+-----------------
_> 0 | 0 | 0
_> ------------+-----------+-----------------
_> S!=0 | 0 | -1
_> ------------+-----------+-----------------
_> 0 | S!=0 | 1
_> ------------+-----------+-----------------
_> S1 | S2 | compare(S1, S2)
_>*/
_>int compare_s(const void* a, const void* b) {
_> return
_> compare(&ODSREF_TOP(a), &ODSREF_TOP(b)) *
_> ((ODSREF_TOP(a) && ODSREF_TOP(b)) ? 1: -1);
_>}
_>/*
_> Sort data in ascending order by std library qsort
_> and place to OnDiskStack (i.e. to temporary file);
_> Arguments:
_> buf - pointer to data buffer;
_> size - buffer size;
_> stack - pointer to destination stack (
_> stack s.b. empty, with no temporary file associated,
_> stack data would be lost otherwise
_> ).
_>*/
_>void sortnflush (
_> VAL_T* buf, ITER_T size,
_> OnDiskStack* stack
_>) {
_> stack->pfile = NULL;
_> stack->top = 0;
_> switch (size) {
_> case 1:
_> stack->top = buf[0];
_> case 0:
_> break;
_> default:
_> if (!(stack->pfile = tmpfile())) {
_> fprintf(stderr, "Temporary file creation failed\n");
_> exit(EXIT_FAILURE);
_> };
_> qsort(buf, size, VAL_SIZE, compare);
_> stack->top = buf[0];
_> /*
_> HACK! Write operation may be interrupted
_> in some cicrcuimstances, but we treat
_> such situation as an error
_> */
_> if (fwrite(&buf[1], VAL_SIZE, size - 1, stack->pfile) != (size - 1)) {
_> fprintf(stderr, "Temporary file write failed\n");
_> exit(EXIT_FAILURE);
_> };
_> rewind(stack->pfile);
_> break;
_> };
_>}
_>/*
_> Stacks resorting.
_> Stacks [1..(num_stacks - 1)] are assumed to be sorted.
_> Find the place to insert the first stack, and do insert.
_>*/
_>void resort(OnDiskStack* stacks, ITER_T num_stacks) {
_> OnDiskStack buf_stack;
_> switch (num_stacks) {
_> case 2:
_> if (compare_s(&stacks[0], &stacks[1]) == 1) {
_> buf_stack = stacks[0];
_> stacks[0] = stacks[1];
_> stacks[1] = buf_stack;
_> };
_> case 0:
_> ;
_> case 1:
_> break;
_> default:
_> if (compare_s(&stacks[0], &stacks[1]) == 1) {
_> ITER_T beg = 1;
_> ITER_T end = num_stacks;
_> ITER_T mid;
_> buf_stack = stacks[0];
_> /*
_> Search the place to insert stacks[0] in
_> range [beg, end)
_> */
_> do {
_> mid = (end + beg) / 2;
_> switch (compare_s(&buf_stack, &stacks[mid])) {
_> case 0:
_> beg = end = mid;
_> break;
_> case 1:
_> beg = mid;
_> break;
_> case -1:
_> end = mid;
_> break;
_> default:
_> /* We should never go here */
_> assert(0);
_> break;
_> };
_> } while ((end - beg) > 1);
_> memmove(stacks, &stacks[1], beg * ODS_SIZE);
_> stacks[beg] = buf_stack;
_> };
_> break;
_> };
_>}
_>/* Application entry point */
_>int main(int argc, char* argv[]) {
_> /* On-disk stacks to store sorted portions of input data */
_> static OnDiskStack stacks[NUM_TMP_FILES_MAX];
_> /* Actual number of temporary storages used */
_> ITER_T num_stacks;
_> /*
_> Read input data to buffer, sort buffer
_> and put to on-disk stack when buffer is full or
_> when stdin is terminated
_> */
_> {
_> /* Input data buffer */
_> static VAL_T buf[BUF_SIZE];
_> /* Actual number of items stored in buffer*/
_> ITER_T num_bufd = num_stacks = 0;
_> int scan_ok;
_> do {
_> scan_ok = (scanf(VAL_FMT, &buf[num_bufd]) == 1);
_> if (scan_ok) {
_> if (!buf[num_bufd]) {
_> fprintf(stderr, "Input data s.b. > 0\n");
_> exit(EXIT_FAILURE);
_> };
_> ++num_bufd;
_> };
_> if ((BUF_SIZE == num_bufd) || (!scan_ok)) {
_> if (NUM_TMP_FILES_MAX == num_stacks) {
_> fprintf(stderr, "Input data limit exceeded\n");
_> exit(EXIT_FAILURE);
_> };
_> sortnflush(buf, num_bufd, &stacks[num_stacks]);
_> num_stacks++;
_> num_bufd = 0;
_> };
_> } while (scan_ok);
_> };
_> /*
_> Merge all data in stacks, write data to stdout
_> */
_> if (!num_stacks)
_> return EXIT_SUCCESS;
_> {
_> qsort(stacks, num_stacks, ODS_SIZE, compare_s);
_> while (stacks[0].top) {
_> printf(VAL_FMT, stacks[0].top);
_> if (!stacks[0].pfile) {
_> stacks[0].top = 0;
_> } else if (fread(&stacks[0].top, VAL_SIZE, 1, stacks[0].pfile) != 1) {
_> if (feof(stacks[0].pfile)) {
_> stacks[0].pfile = NULL;
_> stacks[0].top = 0;
_> } else {
_> fprintf(stderr, "Temporary file read failed\n");
_> exit(EXIT_FAILURE);
_> };
_> };
_> resort(stacks, num_stacks);
_> };
_> };
_> return EXIT_SUCCESS;
_>}
_>
Здравствуйте, x905, Вы писали:
X>Здравствуйте, superdeveloper, Вы писали:
S>>Здравствуйте, x905, Вы писали:
X>>>ключевое слово "_может_", ага
S>>Выделяем массив, равный 32768/8=4096 байт S>>каждому биту ставим в соответствие одно число 1...32768 S>>число — номер бита (число-1) S>>соответственно биты будут 0...32767
S>>Алгоритм: S>>1. Обнуляем массив S>>2. Проходимся по файлу, если это число есть, то включаем бит (число-1) S>>3. Проходим по всем битам, если бит включен, то заносим в выходной файл число (номер_бита+1) S>>4.конец агоритма.
S>>вот.
S>>ps. Приём известный.
X>тред с начала читаем ? уже было, ага )
X>вопрос есть поинтересней: 4кб — это только на массив, а переменные?, а rintime? X>даже helloworld вылезет за 4кб X>и как все в 4кб всеже впихнуть?
Была в свое время игрушка, называлась livingstone. Ходилка такая, бродилка. Там бегать, прыгать, стрелять. Графика, насколько я помню, была типа 16-цветная, или даже 4х-цветная, но зато игрушка была достаточно продолжительной и интересной. Так вот, автор поставил себе цель написать ее полностью на ассемблере, и вместить в 64Кб. Вместил. Она даже была .com, а не .exe.
Здравствуйте, x905, Вы писали:
X>вопрос есть поинтересней: 4кб — это только на массив, а переменные?, а rintime? X>даже helloworld вылезет за 4кб X>и как все в 4кб всеже впихнуть?
Что считать памятью?
Регистры тоже память?
Если нет, то всё можно сделать через регистры.
Вообще в Windows есть разные виды ведения памяти, commit/reserve и .т.д.
Можно ли выделять временные файлы, можно ли несколько раз считать исходный файл и т.п.?
Надо поставить более точную задачу.
Здравствуйте, superdeveloper, Вы писали:
S>Что считать памятью? S>Регистры тоже память? S>Если нет, то всё можно сделать через регистры. S>Вообще в Windows есть разные виды ведения памяти, commit/reserve и .т.д.
причем тут венда?
S>Надо поставить более точную задачу.
согласен, сам хотел бы ответ услышать — интересно ведь
но видно не судьба
Вопрос с памятью не понятен, я бы решил, что это о выделяемой памяти через new. хотя может и стековые переменные. код программы в такой обьем не влезет.
от 0 до 32767 чисел. от 1 до 32768 значений.
2Кб на память, 2 кб на массив битов.
Вообще можно не ограничиваться 32К значений. а проходить по 16К (-> 2кб), хоть до 4Гб.
* тогда в первый раз разделить 4Гб на, скажем 512 частей (32 байта по 1 биту на часть. наличие цифр из этого диапазона).
каждую часть разделив еще на 512 получим наши 16кб.
правда файл может быть прочитан(от начала до конца) невероятное число раз.
в случае с 0 до 32767 значений — максимум 4 раза.
в случае с 1 до 32768 значений — максимум 5 раз.
Для 4Гб задачка на мой взгляд интересней, решение универсальней. (хотя да, лишние 2 чтения)
Римское правило. Тот, кто говорит, что Это не может быть сделано, никогда не должен мешать тому, кто Это делает.
Осень, ну вы поняли.
Зачем еще один код? А человек?
M>Может они даже разбираться не стали, код читается очень тяжело если честно
Хм, а это уже, ИМХО, субъективное. Вроде форматирование нормальное (если не считать расстановки фигурных скобок), хотя , со стороны такие вещи всегда виднее.
M>Обычно по одной задачке не судят. Наверное "не понравилось" или программист не сильно срочно нужен.
Задним числом узнал, что их вакансия висит на разных сайтах уже второй год
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, x905, Вы писали:
X>в предложенных решениях сразу нарушается условие задачи — 4кб т.к. выделяется место под массив и есть хотябы одна переменная ) X>также не учтено потребление памяти при загрузке программы
Полностью согласен. Если посмотреть top'ом, то программка отжирает мегабайты. Впрочем, и мега-программа "return 0" ведет себя не намного лучше. Предлагай выход? Писать все на ассемблере, вручную распределять память ит.п.?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
MSV>Вопрос с памятью не понятен, я бы решил, что это о выделяемой памяти через new. хотя может и стековые переменные. код программы в такой обьем не влезет.
Пацаны, не страдайте фигней! Говоря о том что ограничение по памяти 4 Кб, имеется в виду что максимальный размер используемого в програме массива может быть 4 Кб. Ни о какой памяти под рантайм речь не идет и никто вас расстреливать не будет если заведете пару вспомогательных переменных.
ЗЫ. 4 Кб была подсказка что именно хотел увидеть задавший задачу. Без нее, решение в лоб — использовать массив 32768 bool-ов или int-ов если нужно посчитать сколько раз встречается число и не замарачиваться с битовыми операциями. В принципе можно было сообразить даже если и не знаешь решения.
_>Есть файл, который может содержать 32768 целых, неповторяющихся чисел в диапазоне от 1 до 32768. Числа в файле расположены случайным образом. Написать программу, осуществляющую сортировку данного файла, результатом которой будет файл с отсортированными по возрастанию числами. Для осуществления сортировки разрешено использовать максимально 4Кб оперативной памяти.
Гы. Это шутка ? Если строго соответствовать указанному условию, то требуемая программа будет иметь вид