А вот ещё одно решение. Его идея в том, чтобы учитывать сравнения, сделанные на предыдущих шагах.
Для простоты, сделал на мутабельных списках, из которых изымаются головные значения. Переделать на неизменяемые векторы — дело нехитрое, просто потребуется ещё и индексы/итераторы хранить.
Скрытый текст
#include <vector>
#include <list>
#include <iostream>
using namespace std;
typedef int Value;
std::vector< std::list<Value> > sources; // ровно 3 элемента
// конечный автоматtypedef Value (*TakeFun)();
TakeFun taker = nullptr;
// такт конечного автомата:template<int A, int B, int C> // номера источников, в порядке возрастания голов
Value take()
{
cout << "take " << A << "," << B << "," << C << endl;
// взять самый маленький из самого маленького
Value v = sources[A].front();
sources[A].pop_front();
// действие для следующего тактаif(sources[A].empty()) // источник кончился - выпихнем его в хвост
taker = take<B,C,-1>;
else if(B < 0 || sources[A].front() <= sources[B].front()) // первый источник по-прежнему лучший (или второго уже нет)
taker = take<A,B,C>;
else if(C < 0 || sources[A].front() <= sources[C].front()) // первый источник - между вторым и третьим (или третьего нет)
taker = take<B,A,C>;
else// первый источник - позади третьего
taker = take<B,C,A>;
return v;
}
// инициализируем конечный автомат, сортировка пузырькомtemplate<int A, int B, int C> void setup()
{
cout << "setup " << A << "," << B << "," << C << endl;
if(A < 0)
taker = take<-1,-1,-1>;
else if(B < 0)
taker = take<A,-1,-1>;
else if(sources[A].front() > sources[B].front())
setup<B,A,C>();
else if(C < 0)
taker = take<A,B,-1>;
else if(sources[B].front() > sources[C].front())
setup<A,C,B>();
else
taker = take<A,B,C>;
}
int main()
{
sources = vector< list<int> > {
list<int> { 1, 5, 12, 28, 31 },
list<int> { 6, 9, 13 },
list<int> { 3, 7, 10, 15 },
};
setup<0,1,2>();
cout << "------" << endl;
while(taker != &take<-1,-1,-1>)
{
Value v = taker();
cout << v << endl;
}
}
Здравствуйте, Ororo1, Вы писали:
O>Условие: O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя! O>Примеры списков: O>[List]: 1, 5, 12, 28, 31 O>[List]: 6, 9, 13 O>[List]: 3, 7, 10, 15 O>Вывод в этом случае должен быть таким: O>GetNext << 1; O>GetNext << 3; O>GetNext << 5; O>GetNext << 6; O>GetNext << 7; O>GetNext << 9; O>и т.д.
O>Шутка!:
Здравствуйте, alzt, Вы писали:
EP>>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector. A>Можешь ответить поподробнее? Судя по рейтингу ты знаешь о чём пишешь, а не глупость написал.
watchmaker выше хорошо описал, попробую дополнить.
A>Это как-то связано с попаданием в кеш процессора? A>Но в этом случае тоже на мой взгляд сложно получить разницу на порядок. Список также легко влезет в кеш. Разница только в том, что надо хранить указатель на сл. элемент, но это не так много.
При определённом количестве элементов, в кэш не влезают ни массив ни связанный список, и при новом проходе нужно заново прочитать все значения из памяти в обоих случаях.
Важно понимать, что кэш это не просто хранилище уже считанных значений, точнее в нём хранятся не только они.
Из памяти приходят не отдельные байты, а целые куски размером с cache-line — по 64 байта (естественно зависит от конкретной архитектуры железа).
То есть считав с произвольного места в памяти 4 байта, будет происходить полноценная перекачка из памяти в кэш аж 64 байт.
Если удачно подобрать структуру данных и алгоритм обхода, то все те "лишние" 60 байт которые пришли в кэш при первом запросе, будут использоваться следующими итерациями, а значит КПД использования memory bandwidth будет 100%. То есть, например, требуется обработать гигабайт, и перекачиваться из памяти в кэш будет именно гигабайт.
Примером этого является линейных обход массива — все элементы лежат плотно рядом с друг другом. Прочитав первый элемент с границы в 64 байта — мы также получим следующие лежащие за ним, и они не окажутся невостребованными.
При работе же со списком, в общем случае, запросив из random'ного места в памяти узел с int'ом — всё что придёт в кэш помимо самого узла, скорей всего окажется невостребованным (точнее оно может понадобится, когда уже переедет в следующий уровнеь кэша, или вообще в память).
Вот и считай — КПД использования memory bandwidth примерно 4/64*100% = 6.25%, или в 16 раз меньше чем у массива.
Если же payload больше 4 байт, то и КПД для списка улучшится.
Но, КПД использования пришедших cache-line — это не единственная проблема списков, там ещё много других проблем, например ещё нужно учитывать prefetcher
(помогающий скрыть latency при предсказуемых паттернах доступа (типа обхода массива), и бесполезный для списков, так как пока не пришёл новый узел — неизвестно что prefetch'ить).
Да, и главное, задачи вида "пройтись по всем элементам, и сделать не слишком долгое вычисление" — упираются в скорость работы памяти. То есть время непосредственно вычислений намного меньше чем время пересылки данных из памяти.
С другой стороны, если для каждого элемента делается очень много работы — то особо не важно массив там, или список.
class std::vector<int,class std::allocator<int> >: 0.022010s wall, 0.015600s user + 0.000000s system = 0.015600s CPU (70.9%)
class std::list<int,class std::allocator<int> >: 4.811541s wall, 4.820431s user + 0.000000s system = 4.820431s CPU (100.2%)
class std::vector<int,class std::allocator<int> >: 0.023067s wall, 0.031200s user + 0.000000s system = 0.031200s CPU (135.3%)
class std::list<int,class std::allocator<int> >: 4.812521s wall, 4.820431s user + 0.000000s system = 4.820431s CPU (100.2%)
Замечу что на MSVC2010 никакого автовекторизатора нет (он появился он только в следующей версии компилятора).
4.812521s / 0.023067s ~ 208.6x
ASM:
; vector
$LL61@test:
add ebx, DWORD PTR [rax]
add rax, 4
cmp rax, r11
jne SHORT $LL61@test
; list
$LL82@test@2:
add ebx, DWORD PTR [rax+16]
mov rax, QWORD PTR [rax]
cmp rax, r11
jne SHORT $LL82@test@2
P.S. Это относится не только к спискам. Например, если есть массив указателей на объекты (чем грешат многие "true-OO" языки), особенно перетасованных/пересортированных, то мы получим подобный эффект — каждое чтение имеет малое КПД, да ещё и из произвольного места.
Здравствуйте, alzt, Вы писали:
К>>>- сделать не на векторах, а по-честному, на списках К>>>- на изменяемых списках это даже проще делается EP>>А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента. EP>>Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой. A>А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.
Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
Здравствуйте, smeeld, Вы писали:
EP>> не лишает обход перетасованного списка его random'ной сущности S>Мы говорим о реальных задачах, и существующих оптимальных механизмах S>их решения, или о вакуумном, строго последовательном расположении S>объектов и таком же доступе к ним? Понятно, что аккумулировать на S>непрерывный массив c paddd/addpd быстрее, но что если у нас, скажем, S>протокол обмена с форматом сообщений и с полнсотью рандомной их обработкой? S>Тогда наоборот, доступ и управление данными по принципу непрерывного S>массива-это качели.
Не пойму что ты пытаешься сказать. То что у списка есть своя область применения? Так с этим никто и не спорил
_>Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи
Есть там всё, что есть в STL, но это не введенно в стандарт самого языка, что правильно.
E>Ну там вроде бы никто такой задачи перед собой не ставил.
Там эта задача решена, на уровне синтаксиса языка.
Что вообще такое всё, что есть в STL? Вот есть в perl
хеши %h()-ассоциативные массивы, они же словари в питоне,
удобно. В C++ нет, хотя этот язык претендует на высокоуровневый,
с возможностью низкоуровневых оптимизаций, но с средствами для упрощения
и ускорения разработки прикладных приложений, прежде всего, и для этого
обязанный содержать развитые всокоуровневые структуры данных. Так же
про остальные: списки, массивы-всё это в упомянутых языках
на уровне синтаксиса с реализацией на уровне интерпретатора.
В погоне за "не отстать и перегнать" появляются костыли, вроде
STL, которые вводятся в стандарт. ИМХО получилось неуклюже.
Здравствуйте, Erop, Вы писали:
EP>>У контейнеров size_type — это implementation-defined беззнаковое целое. E>А есть гарантия, что беззнаковое?
Да: "Table 96 — Container requirements ... X::size_type unsigned integer type"
E>Тоесть, если я хочу свой конетейнер сделать для стандартных алгоритмов, то я тоже обязан беззнаковое использовать для инндекса, например?
Не помню есть ли алгоритмы STL принимающие контейнер, а не итераторы. Вспоминается только boost'вский remove_erase и подобные.
А так да, если бы были — то да, формально должно быть беззнаковое.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
>>>3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев. PM>>Аналог Boost.Range это https://github.com/ericniebler/range-v3 ?
EP>Я не следил за предложениями, может там и другие proposals были.
Я тоже не слежу за предложениями, просто читаю http://ericniebler.com, Eric там пишет о своих успехах
PM>>Блин, 15 лет прошло со времен плясок вокруг Visual C++ 6.0 в попытках заставить его компилировать шаблоны. Как будто ничего и не поменялось
EP>Какое-то движение всё же есть. Они хоть и отстают, но уже стараются догонять. EP>Кстати, ЕМНИП Саттер (или Стефан) не так давно говорил, что они вот-вот начнут внутри компилятора использовать AST
Прогресс есть, реально в VS 2013 не хватет только constexpr из-за отсутствия которого самые современные библиотеки не поддерживаются. Я с Boost.Hana так пролетел, вот и ворчу
EP>P.S. Помимо Boost.Range ещё есть и аналог в Adobe.ASL
Да, только давненько оно не обновлялось.
Кстати недавно Eric победил проблему с производительностью counted ranges на которую указывал Sean Parent (я так понимаю он автор ranges в ASL). Результатом и стало предложение D4128 Ranges for the Standard Library: Revision 1. В нем помимо изменений в стандартной библиотеке предлагается немного поменять семантику range-based for loop чтобы begin и end могли быть разными типами. Так что в Visual C++ 2020 может быть увидим что-нибудь
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, watchmaker, Вы писали:
W>>Но, разумеется, скорость работы меряется не числом инструкций. Поэтому тест нужно ещё запустить. Возьмём миллион чисел, положим их в контейнер.
EP>accumulate шёл сразу после заполнения? Если так, то многие из элементов могут быть уже в L3. Попробуй миллионов 100 — чтобы гарантированно вымыть весь кэш.
На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор :)
А относительная скорость осталась почти такой же.
Здравствуйте, BulatZiganshin, Вы писали:
W>>На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор W>>А относительная скорость осталась почти такой же.
BZ>а не в 3 — int vs int+int*?
x64 (и не забываем про alignment)
односвязный список: Node*+int32, sizeof = 16, то есть 4x
двусвязный список: 2*Node*+int32, sizeof = 24, то есть 6x
Здравствуйте, PM, Вы писали:
PM>Прогресс есть, реально в VS 2013 не хватет только constexpr из-за отсутствия которого самые современные библиотеки не поддерживаются. Я с Boost.Hana так пролетел, вот и ворчу
Спасибо за ссылку.
Но ведь не побелил же! Его концепция CountedRange выглядит как хак, который к тому же жертвует производительностью. Если бы не было потерь производительности, то можно было бы и пережить, а с потерями — ну уж нет.
ИМХО, не нужно боятся вводить новые концепции Range'ей — если эти концепции чётко улавливают суть, позволяя писать эффективный код (в абсолютном смысле, а не "в этом вот примере всего 5%" разница). Не хаки-адапторы к старым алгоритмам, а полноценные range со своими оптимизированными алгоритмами, как раз наподобие partition_point_n.
PM>на которую указывал Sean Parent (я так понимаю он автор ranges в ASL).
Он, кстати, раньше работал со Степановым.
Кстати, вот в этом моменте Степанов рассказывает про Counted Range.
А вот тут про двумерные итераторы (Eric упоминал их как segmented).
Оценка — на 4-
Улучшать можно по-всякому:
— разнести типы значений и индексов
— сделать не на векторах, а по-честному, на списках
— на изменяемых списках это даже проще делается
— at() не нужен, у нас в штатном режиме исключений выхода за границы не возникает — достаточно operator[]
— диагностику конца последовательности можно и без исключений сделать, а, например, отдельной функцией
— для трёх списков можно захардкодить, хотя обобщение на произвольное количество списков — в общем-то, правильный подход
Вместо вектора векторов и вектора индексов логичней использовать 1 вектор итераторов
Хотя я бы сделал проксю на 3 ячейки, которая хранит в себе копии текущих елементов (вместе с итераторами/индексами ессно): выдал наименьший из списка, взял из списка следующий, чтобы лишний раз в память не лазить (хотя это преждевременная оптимизация)
ps/
Впрочем, вру, конечно же, все равно надо еще хвосты запоминать, так-что векторов и с итераторами надо 2 :p
Здравствуйте, slava_phirsov, Вы писали:
O>>не принципиально
_> Вообще-то такое должно на автомате писаться. Ну я так думаю
+1
O>>Индексы вон тоже бы unsigned не помешало сделать
_> Основоположники, ЕМНИП, учили использовать unsigned там и только там, где требуются операции с битами.
-1
емнип это авторы Java оправдывались почему у них нет беззнаковых целых. Последние удобно использовать в качестве индексов, т.к. для проверки индекса в массиве требуется одно сравнение вместо двух:
bool is_valid_index(int i, int size)
{
return i >= 0 && i < size;
}
bool is_valid_index(unsigned i, unsigned size)
{
return i < size;
}
А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t (с которым кстати в теории можно адресовать в 2 раза больше элементов чем с int), то с is_valid_index(int, int) получим на ровном месте предупреждение компилятора о преобразовании unsigned в int. Которое придется душить явным преобразованием к int
Задолбали такие функции:
void process_string(char const* str, int len);
// приходится их использовать так:
std::string str = "fgfg";
process_string(str.data(), static_cast<int>(str.size()));
Здравствуйте, slava_phirsov, Вы писали:
EP>>Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно ровняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
. _>Ну вот некоторое время назад был холивар как раз по поводу STL, и как она ужасна, и что "написал он ее в больнице, и не иначе, как то была больница Степанова-Скворцова ит.п.".
STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая
Что конкретно "ужасного" там?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>То решение которое он предлагает в D крайне спорное. Точнее оно отлично подходит для InpuntRange, а вот начиная с ForwardRange начинаются серьёзные проблемы. Всё подробно описано в этом топике
Что до меня, то только то, что, в STL, к примеру, для копирования требуется не забыть переразмерить контейнер-приемник (вариант — не забыть использовать std::inserter), что std::remove на самом деле не удаляет, а просто перетасовывает элементы, и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Ororo1, Вы писали:
O>Условие: O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
Что значит нельзя объединят?
Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков.
Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
_>Намекаешь на то, что элементы вектора помещаются в памяти последовательно и компактно, а элементы списка — как повезет?
Даже если разместить узлы списка в памяти последовательно, то всё равно его обход будет в разы медленнее вектора. Неприятно, но не катастрофа.
Но вот если действительно узлы списка раскидать по памяти, то одних кеш-промахов уже вполне будет достаточно для той самой просадки на порядки.
Ещё тут стоит учесть, что элементы списка расходуют память менее экономно чем вектор. Что опять же не лучшим образом сказывается на использовании кеша.
_>:???: Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента.
Чтение адреса следующего элемента — это плохо. Плохо потому что это ещё одно чтение из памяти, и плохо потому что программа как правило не может продвинутся дальше до завершения чтения из-за зависимости по данным. Ведь в типичной итерации по списку сначала читается адрес следующего элемента, а затем он обрабатывается. Суперскалярный процессор может делать эти операции почти параллельно для вектора, но лишь последовательно для списка.
Ну и прочие эффекты, вроде автоматической векторизации и распараллеливания алгоритмов могут играть свою роль. Для вектора компилятор во многих случаях умеет делать это автоматически, для списка же шансов практически нет.
Здравствуйте, smeeld, Вы писали:
S>Объясните на этом примере где тут итерация по массиву обходится дешевле чем по списку.
Какой-то странный пример. Такое ощущение, что выключена оптимизация. Приведи исходный код, настройки компилятора. И нужно больше контекста. И лучше сравнивать более-менее ясные задачи, а не совсем уж абстрактные строчки кода вроде d=mas[i].
Ведь можно написать одну функцию из одной строки:
template<T> inc(T::iterator& i) { ++i; }
и потом сравнивать код для вектора или списка. Вот только это не особо интересно, абстрактная функция создаст другой код, которого не будет в более реальных программах, так как там этот же инкремент в контексте сопутствующих операций может породить сильно отличающийся код.
В общем, я на твоём примере пока объяснять не буду. Лучше приведу свой:
template <typename C> traverse_test(const C& c) {
volatile auto x = accumulate(c.begin(), c.end(), 0);
}
Компилируем на x86-64 c g++ 4.8.2 с выключенной векторизацией и с выключенным распараллеливанием.
Если в качестве контейнера C взять std::list<int> или std::forward_list<int>, то внутренний цикл будет выглядеть так:
.L5:
addl 8(%rax), %edx // сложили в аккумулятор
movq (%rax), %rax // взяли адрес следующего элемента
testq %rax, %rax // проверили, что список не закончился
jne .L5 // и начали обрабатывать следующий элемент
Если качестве контейнера C взять std::vector<int>, то цикл получится таким:
.L5:
addl (%rax), %edx // сложили в аккумулятор
addq $4, %rax // взяли адрес следующего элемента
cmpq %rax, %rcx // проверили, что список не закончился
jne .L5 // и начали обрабатывать следующий элемент
Если разрешить автоматическую векторизацию (точнее не запрещать, так как она включена по умолчанию), то код для std::list<int> или std::forward_list<int> не изменится, а для std::vector<int> основный цикл будет таким:
.L15:
addq $1, %rdx // обновили счётчик
paddd (%rsi), %xmm0 // сложили в аккумулятор
addq $16, %rsi // взяли адрес следующего элемента
cmpq %rdx, %r9 // проверили счётчик
ja .L15 // и начали обрабатывать следующий элементы
Как видишь, во всех случаях код довольно простой и вполне естественный. Во всяком случае, вариант для вектора ну никак не выглядит сложнее варианта для списка.
Но, разумеется, скорость работы меряется не числом инструкций. Поэтому тест нужно ещё запустить. Возьмём миллион чисел, положим их в контейнер. Убедимся что все элементы идут в памяти плотно и по порядку (в реальной программе, такие гарантии получить можно не всегда для списков, но пусть будет так). И запустим traverse_test раз 200. У меня не видно существенных отличий между std::list<int> и std::forward_list<int> в этом тесте, а в остальном время работы получается таким:
list 590 ms // c и без автовекторизации
vector 103 ms // без автовекторизации
vector 43 ms // c автовекторизацией
С тестированием forward_list возникла правда сложность, так как там есть только push_front, но не push_back, то есть элементы идут как бы в обратном порядке. И если это не исправлять, то скорость работы :forward_list оказывается даже ниже list и тест завершается за 650 ms. Если аналогично "ухудшить" ситуацию и для вектора, использовав rbegin вместо begin, то замедление также заметно ухудшает автовекторизированный вариант на 4-5 мс, не изменяя, впрочем, существенно вариант без векторизации.
В общем, из этого теста вполне можно заключить, что фраза «vector на порядок быстрее list в задаче пробега по всем элементам (и подсчёта их суммы)» не так уж далека от истины.
S>Что до промахов в кеше из-за раскиданности нод списка, во всех нормальных S>системах, ноды для списков выделяются из некоторого непрерывного массива, S>кеша специализированного аллокатора. Так что никакой раскиданности.
Это верно разве что только после первичного заполнения списка. Как только начинаются его модификации, то сразу всё идёт кувырком.
Да вот, например, тут
предложено собрать один список из трёх, видимо переставляя только указатели, не перенося сами элементы. Но после этого многие связи между элементами будут совсем нелокальными, даже если изначально все элемента каждого списка шли подряд. Конечно, в данной задаче с тремя списками это не так критично — отслеживать три потока prefetch современные x86 процессоры умеют, — но в чуть-чуть более сложной задаче всё будет куда печальнее. Удали, например, из списка элемент, потом добавь к нему новый в начало. Либо просто вставь новый элемент в середину. Где будет для него память выделена? Будет ли она в той же или в соседней кеш-линии, что и память его соседей по списку? Да совсем не обязательно. И никакой специализированный аллокатор это не поборет.
А если вставки или удаления в середине не нужны, то зачем тем более выбирать список, а не массив?
Это — не объединение. Это три независимых списка, к которым сделан доступ по индексу.
Легко убедиться, заменив вектор списков на вектор указателей на списки, как у топикстартера.
Здравствуйте, smeeld, Вы писали:
S>Здравствуйте, watchmaker, Вы писали:
W>>list 590 ms // c и без автовекторизации W>>vector 103 ms // без автовекторизации W>>vector 43 ms // c автовекторизацией W>>[/code]
S>То, что std::list делает allocate if N+1> CURRENT_SIZE then CURRENT_SIZE=CURRENT_SIZE+1 allocate(1) для каждой добавляемой ноды, S>а std::vector по закону if N+1> CURRENT_SIZE then CURRENT_SIZE=CURRENT_SIZE*2 allocate(CURRENT_SIZE*2 ), тем самым уменьшая количество S>brk в ядро.
И что?
Во-первых, на время теста это не влияет, ведь std::accumulate не делает аллокаций.
Во-вторых, стратегия роста (+1) вместо (×2) для списка не так страшна как для вектора, ведь ему никогда не нужно переносить уже существующие элементы.
В-третьих, brk или mmap в ядро уйдут только когда уже выделенный буфер заполнится, до этого выделения памяти будет происходить лишь внутри аллокатора программы, что совсем не так страшно. А памяти после brk вернётся не мало — хватит на много элементов списка.
Но, да, если же замерять время с аллокациями, то картина для списка может стать ещё печальнее.
W>>Это верно разве что только после первичного заполнения списка. S>Просто просмотрите как устроены slab аллокаторы в linux/solaris. А так тема долгая для этого форума.
Да, я исходил именно из тех допущений, что аллокатор устроен на аналогичном принципе. И именно в этом случае при использовании slab-подобных подходов у редактируемого списка возникают проблемы с локальностью. Впрочем эти проблемы возникнут у изменяемого списка в любом случае.
W>>А если вставки или удаления в середине не нужны, то зачем тем более выбирать список, а не массив? S>Списки нужны как раз когда есть необходимость тасовать объекты.
Ну так именно об этом я и говорю: если нужно тасовать объекты, то о каком попадании в кеш идёт речь? После хорошей перетасовки каждый следующий элемент списка будет находится чуть ли не в случайной области памяти. А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора.
Здравствуйте, watchmaker, Вы писали:
W>А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора.
Все элементы находятся в пределах одного непрерывного массива, список обеспечивает лишь
политику доступа и управления элементами, которые в реальности распологаются в виде массива.
О каких промахах, идёт речь, которых не будет при политике доступа и управления объектами
по принципу непрерывного последовательного массива?
Здравствуйте, Sni4ok, Вы писали:
S>ну и в целом всё убого- скорость O(N^2) вместо напрашивающихся O(n * log(n))
Вроде бы O(N) оба раза, если N -- cуммарная длина списков, например...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Не холивара ради — приведи пару пунктов от себя.
Заведи топик, где это не офтопик -- приведу
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, B0FEE664, Вы писали:
К><> К>Хороший подход, но энергичный. К>В случае, если списки длинные (и упорядоченные), то можно запустить столько потоков, сколько есть списков, и засыпать там К>- либо к заданным моментам времени — sleep_until, естественно, отсчитывая от момента старта программы К>- либо на время разности между смежными значениями (это хуже, т.к. набежит ошибка)
Нене, это всё излишне усложняет. Преждевременная оптимизация, так можно и до одного потока докатиться...
Здравствуйте, Erop, Вы писали:
E>Вообще речь идёт об альтернативе чему? Главная проблема STL в том, что это библиотека не понятно для чего...
Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Условие:
Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
Примеры списков:
[List]: 1, 5, 12, 28, 31
[List]: 6, 9, 13
[List]: 3, 7, 10, 15
Вывод в этом случае должен быть таким:
GetNext << 1;
GetNext << 3;
GetNext << 5;
GetNext << 6;
GetNext << 7;
GetNext << 9;
и т.д.
Решение:
....
vector<vector<int>*> vectors;//тут хранятся наши списки
vector<int> vec1;
vector<int> vec2;
vector<int> vec3;//сами списки
....
vectors.push_back(&vec1);
vectors.push_back(&vec2);
vectors.push_back(&vec3);
vector<int> indexes(vectors.size(),0);//тут храним индекс элемента, который последний раз брали для каждого из списков
GetNext(indexes);
....
bool isExist(vector<int> &vec, int index)
{
return index < vec.size();
}
int GetNext( vector<int> &indexes)
{
int min = 0;
int index = -1;
for(int i=0;i<vectors.size();++i)
{
if(isExist(*vectors.at(i),indexes.at(i)))
{
min = vectors.at(i)->at(indexes.at(i));
index = i;
break;
}
}
if(index == -1)
throw exception();
for(int i=0;i<vectors.size();++i)
{
if(isExist(*vectors.at(i),indexes.at(i)))
{
if(vectors.at(i)->at(indexes.at(i))<min)
{
min = vectors.at(i)->at(indexes.at(i));
index = i;
}
}
}
indexes.at(index)++;
return vectors.at(index)->at(indexes.at(index)-1);
}
Здравствуйте, Кодт, Вы писали:
К>- сделать не на векторах, а по-честному, на списках К>- на изменяемых списках это даже проще делается
А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента.
Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой.
Фактически все равно это слияние отсортированных списков, только результат возвращается из GetNext. А как кстати GetNext должен сигнализировать о завершении всех списков? Я видел в коде throw exception.
Комментарии в коде решения
O>Решение: O>
O>vector<vector<int>*> vectors;//тут хранятся наши списки
// можно хранить списки по значению в векторе, но я бы ограничился массивом
// vector<int> vectors[3];unsigned const NUM_LISTS = 3;
typedef vector<int> numbers;
numbers lists[NUM_LISTS];
O>vector<int> indexes(vectors.size(),0);//тут храним индекс элемента, который последний раз брали для каждого из списков
// можно вместо индексов хранить const_iterator
// для используемого в качестве списка vector<int> это не принципиально,
// но для другого типа контейнеров (например list<int>) будет критично
numbers::const_iterator heads[NUM_LISTS];
heads[0] = lists[0].begin();
heads[1] = lists[1].begin();
heads[2] = lists[2].begin();
// GetNext станет такой: ищем минимальный из heads, возвращаем его, продвигаем итератор вперед int GetNext()
{
int min_head = 0;
for (int i = 0; i < NUM_LISTS; ++i)
{
if (heads[i] == lists[i].end()) continue; // пропускаем пройденный списокif (*heads[i] < *heads[min_head]) min_head = i;
}
if (heads[min_head] == lists[min_head].end())
throw exception(); // end of listsint min = *heads[min_head];
++heads[min_head];
return min;
}
O>}
O>
// почему не const& ? У среднестатистического программиста,
// который увидел передачу не-const ссылки, сразу возникает мысль,
// что передаваемый объект будет меняться внутри функции.
O>bool isExist(vector<int> &vec, int index)
O>
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, Ororo1, Вы писали:
O>>
_>// почему не const& ? У среднестатистического программиста,
_>// который увидел передачу не-const ссылки, сразу возникает мысль,
_>// что передаваемый объект будет меняться внутри функции.
O>>bool isExist(vector<int> &vec, int index)
O>>
Да, в реальном проекте конечно же желательно const дописать, а в данном случае не принципиально. Индексы вон тоже бы unsigned не помешало сделать.
_>
_> int min = *heads[min_head];
_> ++heads[min_head];
_> return min;
_>
_> return *(heads[min_head]++);
_>
Не хочу ломать голову в приоритете операций, но похоже у тебя сначала идет инкремент итератора и затем его разыменование. И вот тут оно может свалиться. Нужного эффекта можно добиться с
return *heads[min_head]++;
, но постинкременту я предпочитаю явный порядок следования. Кто-то будет это читать и тоже ломать голову. Олдскульных сишников очень мало и они тоже могут ошибаться.
[offtoppic]
Вот не припомню, когда реально без постинкремента было невозможно обойтись. Вот фича, которую надо бы сделать deprecated в С++17 вместо триграфов
[/offtopic]
PM>Не хочу ломать голову в приоритете операций, но похоже у тебя сначала идет инкремент итератора и затем его разыменование. И вот тут оно может свалиться.
С чего бы это? Постфиксный инкремент возвращает предыдущее значение итератора, так что, все честно.
PM>Вот не припомню, когда реально без постинкремента было невозможно обойтись. Вот фича, которую надо бы сделать deprecated в С++17 вместо триграфов
И не надейся! Вангую: deprecated будут объявлены optional и лямбды
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, PM, Вы писали:
PM>С чего бы, если строкой выше есть проверка с комментарием "пропускаем пройденный список" PM>Я код проверял на тестовых данных топикстартера, всё отработало.
Ну, это еще ничего не доказывает
Смотри:
PM>int GetNext()
PM>{
PM> int min_head = 0; // Пусть, первый список уже пуст. Тогда heads[min_head] == lists[min_head].end()
PM> for (int i = 0; i < NUM_LISTS; ++i)
PM> {
PM> if (heads[i] == lists[i].end()) continue; // сработает при i == 0
// здесь мы оказываемся при i == 1, и разыменовываем heads[min_head], т.е. heads[0], т.е. lists[0].end()
// всё пропало!
PM> if (*heads[i] < *heads[min_head]) min_head = i;
PM> }
PM> if (heads[min_head] == lists[min_head].end())
PM> throw exception(); // end of lists
PM> int min = *heads[min_head];
PM> ++heads[min_head];
PM> return min;
PM>}
O>>}
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, PM, Вы писали:
PM>емнип это авторы Java оправдывались почему у них нет беззнаковых целых
А мне наоборот припоминается, что это Страуструп в своем талмуде писал такое. Вот нет под рукой, дойду до него — дам точную ссылку
PM>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t (с которым кстати в теории можно адресовать в 2 раза больше элементов чем с int)
Ну вот, да, а в Qt наоборот сплошь и рядом int. Люди не смогли договориться о порядке следования байт, где уж тут знаковость индексов уединообразить. Только диктатура спасет человечество, ИМХО
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Ну, это еще ничего не доказывает
Это да, надо тестировать на крайних случаях
Возможный фикс:
int GetNext()
{
int min_head = -1;
for (int i = 0; i < NUM_LISTS; ++i)
{
if (heads[i] == lists[i].end()) continue;
if (min_head == -1 || *heads[i] < *heads[min_head]) min_head = i;
}
if (min_head == -1)
throw exception();
int min = *heads[min_head];
++heads[min_head];
return min;
}
Здравствуйте, slava_phirsov, Вы писали:
PM>>емнип это авторы Java оправдывались почему у них нет беззнаковых целых _>А мне наоборот припоминается, что это Страуструп в своем талмуде писал такое. Вот нет под рукой, дойду до него — дам точную ссылку
Страуструп да, писал и говорил нечто подобное.
Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно равняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
.
Использование знаковых целых, там где по смыслу подходят беззаконные (да ещё и имеют большую "ёмкость") — это что-то из области babysitting'а, причём не особо удачного. Если уж нужна нянька — то тогда уже брать safe int и им подобные.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно ровняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
Ну вот некоторое время назад был холивар как раз по поводу STL, и как она ужасна, и что "написал он ее в больнице, и не иначе, как то была больница Степанова-Скворцова ит.п.". Тут можно до бесконечности кидаться ссылками, конечно...
И меряться авторитетами основоположников тоже можно долго
EP>Использование знаковых целых, там где по смыслу подходят беззаконные (да ещё и имеют большую "ёмкость") — это что-то из области babysitting'а, причём не особо удачного. Если уж нужна нянька — то тогда уже брать safe int и им подобные.
Одна беда: если ты пишешь под фреймворк, где используются знаковые, то у тебя особого выбора нет. В чужой монастырь со своим уставом (даже не со своим, а со степановским)... Я бы вот предпочел использовать везде size_t, вроде для того оно и предназначалось, но, увы...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая EP>Что конкретно "ужасного" там?
Не буду цитировать холиварщиков, тем более, что я не разделяю многие из их аргументов, гугл в помощь, если интересно. Вроде читал даже на КЫВТе сенсационный пост типа "Александреску опускает итераторы"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
EP>>STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая EP>>Что конкретно "ужасного" там? _>Не буду цитировать холиварщиков, тем более, что я не разделяю многие из их аргументов, гугл в помощь, если интересно. Вроде читал даже на КЫВТе сенсационный пост типа "Александреску опускает итераторы"
То решение которое он предлагает в D крайне спорное. Точнее оно отлично подходит для InpuntRange, а вот начиная с ForwardRange начинаются серьёзные проблемы. Всё подробно описано в этом топике
И, кстати, на эту тему недавно был доклад — там также отмечается, что хоть в некоторых крайне специфичных случаях D Range это удобно, но в общем случае менее мощное средство чем итераторы.
Здравствуйте, slava_phirsov, Вы писали:
_>Что до меня, то только то, что, в STL, к примеру, для копирования требуется не забыть переразмерить контейнер-приемник (вариант — не забыть использовать std::inserter)
Для подобных вещей есть assign, insert, и те же *inserter'ы.
И что именно значит "не забыть"? Видимо "не забыть" означает "нужно знать что делает используемый инструмент, а не использовать его наугад" — тогда да, нужно
_>что std::remove на самом деле не удаляет, а просто перетасовывает элементы
Ничего удивительного — std::remove работает с range'ами, а не с контейнерами. Принял range, и возвратил range (точнее два).
_>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.
однако нигде его не перехватываете.
ну и в целом всё убого- скорость O(N^2) вместо напрашивающихся O(n * log(n))
O>Условие: O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя! O>Примеры списков: O>[List]: 1, 5, 12, 28, 31 O>[List]: 6, 9, 13 O>[List]: 3, 7, 10, 15 O>Вывод в этом случае должен быть таким: O>GetNext << 1; O>GetNext << 3; O>GetNext << 5; O>GetNext << 6; O>GetNext << 7; O>GetNext << 9; O>и т.д.
O>Решение: O>
O>....
O>vector<vector<int>*> vectors;//тут хранятся наши списки
O>vector<int> vec1;
O>vector<int> vec2;
O>vector<int> vec3;//сами списки
O>....
O>vectors.push_back(&vec1);
O>vectors.push_back(&vec2);
O>vectors.push_back(&vec3);
O>vector<int> indexes(vectors.size(),0);//тут храним индекс элемента, который последний раз брали для каждого из списков
O>GetNext(indexes);
O>....
O>bool isExist(vector<int> &vec, int index)
O>{
O> return index < vec.size();
O>}
O>int GetNext( vector<int> &indexes)
O>{
O> int min = 0;
O> int index = -1;
O> for(int i=0;i<vectors.size();++i)
O> {
O> if(isExist(*vectors.at(i),indexes.at(i)))
O> {
O> min = vectors.at(i)->at(indexes.at(i));
O> index = i;
O> break;
O> }
O> }
O> if(index == -1)
O> throw exception();
O> for(int i=0;i<vectors.size();++i)
O> {
O> if(isExist(*vectors.at(i),indexes.at(i)))
O> {
O> if(vectors.at(i)->at(indexes.at(i))<min)
O> {
O> min = vectors.at(i)->at(indexes.at(i));
O> index = i;
O> }
O> }
O> }
O> indexes.at(index)++;
O> return vectors.at(index)->at(indexes.at(index)-1);
O>}
O>
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>И что именно значит "не забыть"? и пр.
Это означает, что оно не интуитивно понятно трудно для понимания начинающему. И есть мнение, что все это можно было сделать проще и безопаснее за счет незначительных накладных расходов.
_>>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.
EP>Что значит "кучку однотипных аргументов"?
Здравствуйте, slava_phirsov, Вы писали:
_>И есть мнение, что все это можно было сделать проще и безопаснее за счет незначительных накладных расходов.
Например.
_>>>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства. EP>>Что значит "кучку однотипных аргументов"? _>
_>4 аргумента. И можно перепутать аргументы местами, и это скомпилируется без проблем. Да, это один из видов "bad smell" — слишком много аргументов.
1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов.
2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно.
3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев.
EP>1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов.
Одна беда — поскольку типы итераторов являются параметрами шаблона, то типизация не помешает перепутать эти пары местами.
Что касается конкретно std::search, то ИМХО в большинстве случаев все четыре итератора будут одного типа.
EP>2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно.
Однако новички путаются. И даже — страшно сказать — забывают, надо ли ставить первым в паре begin или end. Да, со временем привыкают. Ну так о том и речь, что ножик-то слишком острый, чтобы намазывать им масло на хлеб, мог бы быть и потупее.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
EP>>1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов. _>Одна беда — поскольку типы итераторов являются параметрами шаблона, то типизация не помешает перепутать эти пары местами.
_>Что касается конкретно std::search, то ИМХО в большинстве случаев все четыре итератора будут одного типа.
Согласен.
EP>>2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно. _>Однако новички путаются. И даже — страшно сказать — забывают, надо ли ставить первым в паре begin или end. Да, со временем привыкают.
Код-то тестировать (или в крайнем случае проверять на нескольких входных данных вручную) нужно в любом случае, там всё и вылезет. Плюс есть же всякие debug итераторы и алгоритмы.
_>Ну так о том и речь, что ножик-то слишком острый, чтобы намазывать им масло на хлеб, мог бы быть и потупее.
Скоро будет в стандарте. А в библиотеках уже больше 10 лет.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
>3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев.
Аналог Boost.Range это https://github.com/ericniebler/range-v3 ? Выглядит многообещающе, еще бы попробовать их где-нибудь, но Visual C++ опять не поддерживается.
Блин, 15 лет прошло со времен плясок вокруг Visual C++ 6.0 в попытках заставить его компилировать шаблоны. Как будто ничего и не поменялось
Здравствуйте, PM, Вы писали:
>>3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев. PM>Аналог Boost.Range это https://github.com/ericniebler/range-v3 ?
Я не следил за предложениями, может там и другие proposals были.
PM>Блин, 15 лет прошло со времен плясок вокруг Visual C++ 6.0 в попытках заставить его компилировать шаблоны. Как будто ничего и не поменялось
Какое-то движение всё же есть. Они хоть и отстают, но уже стараются догонять.
Кстати, ЕМНИП Саттер (или Стефан) не так давно говорил, что они вот-вот начнут внутри компилятора использовать AST
P.S. Помимо Boost.Range ещё есть и аналог в Adobe.ASL
Здравствуйте, Evgeny.Panasyuk, Вы писали:
К>>- сделать не на векторах, а по-честному, на списках К>>- на изменяемых списках это даже проще делается
EP>А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента. EP>Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой.
А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.
Здравствуйте, slava_phirsov, Вы писали:
_> Основоположники, ЕМНИП, учили использовать unsigned там и только там, где требуются операции с битами.
Беззнаковые надо использовать там где нет и не может быть отрицательных индексов и смешений-перемешений.
И это очень важно, и к этому следует привыкать и стремится.
То есть знаковые только там где арифметика, и отрицательный офсет НУЖЕН. Везде где индексы только "size_t".
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента. Намекаешь на то, что элементы вектора помещаются в памяти последовательно и компактно, а элементы списка — как повезет?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А что Hana даёт по сравнению с Boost.Fusion? Только скорость компиляции?
Не знаю , хотел просто посмотреть на Hana. Мне нужно было compile-time reflection для структур с наследованием, с Boost.Fusion не получилось: BOOST_FUSION_ADAPT_STRUCT для базового класса, решил посмотреть что еще есть.
PM>>Кстати недавно Eric победил проблему с производительностью counted ranges
EP>Спасибо за ссылку. EP>Но ведь не побелил же! Его концепция CountedRange выглядит как хак, который к тому же жертвует производительностью. Если бы не было потерь производительности, то можно было бы и пережить, а с потерями — ну уж нет. EP>ИМХО, не нужно боятся вводить новые концепции Range'ей — если эти концепции чётко улавливают суть, позволяя писать эффективный код (в абсолютном смысле, а не "в этом вот примере всего 5%" разница). Не хаки-адапторы к старым алгоритмам, а полноценные range со своими оптимизированными алгоритмами, как раз наподобие partition_point_n.
Да не победил, но сильно не ухудшил, что уже хорошо. Вообще, насколько я помню, основной трудностью у него было то, что в перспективе маячили параллельные иерархии bounded, counted and infinite ranges и для каждой иерархии нужно было реализовывать весь набор алгоритмов из стандартной библиотеки.
А с такими адаптерами можно использовать библиотечные реализации, что гораздо надежнее. И оптимизированные реализации для разных типов ranges со временем будут добавляться.
Здравствуйте, Andrew.W Worobow, Вы писали:
O>>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
AWW>Что значит нельзя объединят?
Очевидно же: нельзя создавать копию данных.
AWW>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков. AWW>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.
Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.
O>>>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
AWW>>Что значит нельзя объединят? К>Очевидно же: нельзя создавать копию данных.
Надо тогда в задаче четко написать — стоимость копирования данных равна 100*О.
AWW>>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков. AWW>>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.
К>Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.
Ну да, кстати вот это типичный пример того как человек который "не студент вчерашний", имея такое вот задание может переносить его на реальный опыт, и оказаться "в дураках".
Вообще дерево можно сделать и без копирования, ну ссылки там, указатели.
Тут то еще вопрос — "А где написано что копировать нельзя?"
Здравствуйте, watchmaker, Вы писали:
_>> Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента.
Объясните на этом примере где тут итерация по массиву обходится дешевле чем по списку.
Более того, итерация по списку-дешевле.
Что до промахов в кеше из-за раскиданности нод списка, во всех нормальных
системах, ноды для списков выделяются из некоторого непрерывного массива,
кеша специализированного аллокатора. Так что никакой раскиданности.
Здравствуйте, Andrew.W Worobow, Вы писали:
AWW>>>Что значит нельзя объединят? К>>Очевидно же: нельзя создавать копию данных. AWW>Надо тогда в задаче четко написать — стоимость копирования данных равна 100*О.
У меня телепалка лучше работает
AWW>>>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков. AWW>>>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.
К>>Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.
AWW> Ну да, кстати вот это типичный пример того как человек который "не студент вчерашний", имея такое вот задание может переносить его на реальный опыт, и оказаться "в дураках".
AWW>Вообще дерево можно сделать и без копирования, ну ссылки там, указатели. AWW>Тут то еще вопрос — "А где написано что копировать нельзя?"
Включаю телепалку: ты хочешь сделать не просто "единое дерево", а "непросто" — а именно, приоритетную очередь.
Дерево — это потроха реализации приоритетной очереди. Причём обычная, std::priority_queue, вообще лишь подразумевает деревянную структуру, а размещается в обычном векторе.
Я тут для прикола наколбасил очередь на конечном автомате с шаблонной функцией перехода. Можно ведь и так.
Здравствуйте, smeeld, Вы писали:
S>Что до промахов в кеше из-за раскиданности нод списка, во всех нормальных S>системах, ноды для списков выделяются из некоторого непрерывного массива, S>кеша специализированного аллокатора. Так что никакой раскиданности.
Вот мне тоже так думается, но у wathcmaker-а был еще один козырь — зависимость по данным. Компилятор может (хотя и не должен) развернуть цикл обхода массива и (в суперскалярной арх-ре) практически параллельно выполнять операции над группой элементов массива. Отдельные коллеги, (напр. Касперски в одной из своих книг) уверяют, что это дает существенную разницу.
P.S. Кроме того, для связного списка нет никакой гарантии, что последовательные элементы списка располагаются в памяти последовательно, что как бы снижает эффект выделения памяти для узлов списка из одного пула.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, xobotik, Вы писали:
К>Ну так ты энергично объединил списки в set. К>А в условиях задачи было сказано этого не делать.
Все таки в задачи, наверно имелось ввиду нельзя сделать один список из трех и отсортировать его в порядке возрастания. Тем самым получить нужный результат.
P.S. Если уж точно следовать условию задачи, то записи вида std::vector<std::list<int32_t>> (и похожее) даже в виде входных параметров для функции GetNext не допустимы.
Условие:
Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы.
Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
<>
Хороший подход, но энергичный.
В случае, если списки длинные (и упорядоченные), то можно запустить столько потоков, сколько есть списков, и засыпать там
— либо к заданным моментам времени — sleep_until, естественно, отсчитывая от момента старта программы
— либо на время разности между смежными значениями (это хуже, т.к. набежит ошибка)
Здравствуйте, watchmaker, Вы писали:
W>list 590 ms // c и без автовекторизации W>vector 103 ms // без автовекторизации
для сумирования списка критический путь — зависимые load из L1 cache, каждый требует 4 тактов на intel. для сумирования массива — выполняется один add каждый цикл. если вести чуть более сложны вычисления, то загрузка адреса след. элемента будет происходить параллельно с вычислениями, а вот от проверки на null на каждой итерации избавиться не удастся
Здравствуйте, watchmaker, Вы писали:
W>list 590 ms // c и без автовекторизации W>vector 103 ms // без автовекторизации W>vector 43 ms // c автовекторизацией W>[/code]
То, что std::list делает allocate if N+1> CURRENT_SIZE then CURRENT_SIZE=CURRENT_SIZE+1 allocate(1) для каждой добавляемой ноды,
а std::vector по закону if N+1> CURRENT_SIZE then CURRENT_SIZE=CURRENT_SIZE*2 allocate(CURRENT_SIZE*2 ), тем самым уменьшая количество
brk в ядро.
W>Это верно разве что только после первичного заполнения списка.
Просто просмотрите как устроены slab аллокаторы в linux/solaris. А так тема долгая для этого форума.
W>А если вставки или удаления в середине не нужны, то зачем тем более выбирать список, а не массив?
Списки нужны как раз когда есть необходимость тасовать объекты. Непрерывный массив подходит
только для последовательных хранилищ, неизменяемых со временем элементов.
Здравствуйте, watchmaker, Вы писали:
W>Но, разумеется, скорость работы меряется не числом инструкций. Поэтому тест нужно ещё запустить. Возьмём миллион чисел, положим их в контейнер.
accumulate шёл сразу после заполнения? Если так, то многие из элементов могут быть уже в L3. Попробуй миллионов 100 — чтобы гарантированно вымыть весь кэш.
Здравствуйте, sokel, Вы писали:
EP>>Легко приводится к интерфейсу GetNext. S>ага, как то так наверное:
Да, так.
Как вариант можно на std::priority_queue. Но с ручным вызовом pop_heap/push_heap/make_heap мне даже чуть больше нравится — не нужно доставать элемент, а потом класть обратно (точнее нужно, но тут чуть эффективней получается — так как он меньше двигается).
Здравствуйте, smeeld, Вы писали:
S>>>Списки нужны как раз когда есть необходимость тасовать объекты. W>>Ну так именно об этом я и говорю: если нужно тасовать объекты, то о каком попадании в кеш идёт речь? После хорошей перетасовки каждый следующий элемент списка будет находится чуть ли не в случайной области памяти. А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора. S>Все элементы находятся в пределах одного непрерывного массива, список обеспечивает лишь S>политику доступа и управления элементами, которые в реальности распологаются в виде массива. S>О каких промахах, идёт речь, которых не будет при политике доступа и управления объектами S>по принципу непрерывного последовательного массива?
От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности
если доступ будет идти последовательно по адресам n,n+8,n+16... то это будет так же предсказуемо, как доступ к массиву по адресам n,n+4,n+8...
Здравствуйте, watchmaker, Вы писали:
W>На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор W>А относительная скорость осталась почти такой же.
а не в 3 — int vs int+int*? во-второрых, вариант с padd ещё быстрее, так что дело не в памяти
EP> не лишает обход перетасованного списка его random'ной сущности
Мы говорим о реальных задачах, и существующих оптимальных механизмах
их решения, или о вакуумном, строго последовательном расположении
объектов и таком же доступе к ним? Понятно, что аккумулировать на
непрерывный массив c paddd/addpd быстрее, но что если у нас, скажем,
протокол обмена с форматом сообщений и с полнсотью рандомной их обработкой?
Тогда наоборот, доступ и управление данными по принципу непрерывного
массива-это качели.
Здравствуйте, BulatZiganshin, Вы писали:
EP>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности BZ>если доступ будет идти последовательно по адресам n,n+8,n+16... то это будет так же предсказуемо, как доступ к массиву по адресам n,n+4,n+8...
Я же не зря подчеркнул и выделил жирным: S>>>>Списки нужны как раз когда есть необходимость тасовать объекты.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности EP>Я же не зря подчеркнул и выделил жирным: S>>>>>Списки нужны как раз когда есть необходимость тасовать объекты.
так написал-то ты прямо противоположное! если элементы лежат последовательно, то и доступ будет последователен как в массиве. чи ни то ни
Здравствуйте, BulatZiganshin, Вы писали:
EP>>>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает
обход перетасованного списка
его random'ной сущности EP>>Я же не зря подчеркнул и выделил жирным: S>>>>>>Списки нужны как раз когда есть необходимость тасовать объекты. BZ>так написал-то ты прямо противоположное! если элементы лежат последовательно, то и доступ будет последователен как в массиве. чи ни то ни
Написал я про обход перетасованного списка.
Список который всегда последовательно обходит элементы заданного массива — избыточен (разве что нужен для совместимости с каким-то API, принимающим только список)
PM>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t
Разве это гарантируется?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, slava_phirsov, Вы писали:
_>Ну вот, да, а в Qt наоборот сплошь и рядом int. Люди не смогли договориться о порядке следования байт, где уж тут знаковость индексов уединообразить. Только диктатура спасет человечество, ИМХО
А исчо некоторые ызврошэнци трактуют отрицательные индексы, как индексы с конца массива...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
PM>>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t E>Разве это гарантируется?
У аллокаторов ::size_type — это беззнаковое целое.
У std::allocator, который является аллокатором по-умолчанию для контейнеров, size_type это size_t, но контейнеры не обязаны его использовать как свой size_type.
У контейнеров size_type — это implementation-defined беззнаковое целое.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>3. Какая альтернатива-то?
Ну линк, например...
Вообще речь идёт об альтернативе чему? Главная проблема STL в том, что это библиотека не понятно для чего...
Но правда, лучше в отдельную тему завести или занекропостить, если есть что новое сказать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
Одно из уродств STL в том, что в ней по умолчанию лучше брать list...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Andrew.W Worobow, Вы писали:
AWW>Беззнаковые надо использовать там где нет и не может быть отрицательных индексов и смешений-перемешений. AWW>И это очень важно, и к этому следует привыкать и стремится. AWW>То есть знаковые только там где арифметика, и отрицательный офсет НУЖЕН. Везде где индексы только "size_t".
От этого сообщения исходит опыт разгребания непонятных крешей и странных поведений.
Не сразу втыришь, что индекс прохода по массиву на int, и, по стечению
некоторых обстоятельств, уходит в отрицательные значения.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>У контейнеров size_type — это implementation-defined беззнаковое целое.
А есть гарантия, что беззнаковое?
Тоесть, если я хочу свой конетейнер сделать для стандартных алгоритмов, то я тоже обязан беззнаковое использовать для инндекса, например?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>45 страниц
Это его ещё попилили на части, так там боьше было
EP>могу же сорваться и ответить там на что-нибудь
Оно вроде бы заперто, сначала надо наголосовать на отпирание.
Но можно просто новый топик созжать со ссылками и цитатами из того флейма и понесётся
'а, и то так и не разрешили...
EP>Вроде же добавили поддержку stateful allocators в C++11
Ну прикрути неракообразно...
Кстати, ещё чего вектору не хватает, так это то, что он не может спросить у аллокатора, скока тому удобно памяти дать...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
EP>>3. Какая альтернатива-то? Естественно без потери мощности в общем случае E>Ну линк, например...
Так там же мощность теряется. Практически после любой операции — получаем single pass, и излишнее копирование как следствие.
Это конечно не всегда является проблемой, но мы же обсуждаем часть стандартной библиотеки C++, а не Python'а там или C#'а.
E>Вообще речь идёт об альтернативе чему?
Здравствуйте, Erop, Вы писали:
EP>>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector. E>Одно из уродств STL в том, что в ней по умолчанию лучше брать list...
Почему? В стандарте даже явно сказано обратное:
23.2.3 Sequence containers
The sequence containers offer the programmer different complexity trade-offs and should be used accordingly. vector or array is the type of sequence container that should be used by default. list or forward_list should be used when there are frequent insertions and deletions from the middle of the sequence. deque is the data structure of choice when most insertions and deletions take place at the beginning or at the end of
the sequence.
EP>>std::string это не STL. E>Это уже терминологический спор, IMHO.
Да это не спор. Это просто другая тема.
Я обсуждаю STL, а конкретно библиотеку Степанова и её производные, а не стандартную библиотеку C++ целиком. Ни потоки ввода-вывода, ни строки в STL не входят.
E>Кстати, ещё чего вектору не хватает, так это то, что он не может спросить у аллокатора, скока тому удобно памяти дать...
Да, не хватает. А ещё нехватает возможности расширения in-place, а-ля realloc. Кстати, у Александреску, есть выступление, где он рассказывает про их вектор способный тесно взаимодействовать с аллокатором.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
E>>Вообще речь идёт об альтернативе чему? EP>алгоритмы + контейнеры
Это вообще не является задачей или группой задач.
Правда нужна другая тема.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, smeeld, Вы писали:
S>size_t стандартом С++ ABI x86 и sparc определены как беззнаковое.
Так не интересно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Почему?
По разным замерам, насколько я помню...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Я обсуждаю STL, а конкретно библиотеку Степанова и её производные, а не стандартную библиотеку C++ целиком. Ни потоки ввода-вывода, ни строки в STL не входят.
Библиотека Степанова довольно сильно отличается от того даже, что в С++03 было, и уж поадвно от ныненшних красот. Вообще у Степанова был, в лучшем случае концепт некий.
но лучше отдельную тему.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>По каким таким замерам?
Ну читай тот флейм, например
или заведи другую тему.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
EP>>По каким таким замерам? E>Ну читай тот флейм, например
Посмотрю.
Но ты же не просто запомнил что "такие-то замеры показали что по-умолчанию лучше брать list -> теперь так и поступаю, и другим советую", а наверное ещё и что там были за замеры, что они примерно измеряли, почему они показали такой реазультат, и в какой задаче
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Но ты же не просто запомнил что "такие-то замеры показали что по-умолчанию лучше брать list -> теперь так и поступаю, и другим советую", а наверное ещё и что там были за замеры, что они примерно измеряли, почему они показали такой реазультат, и в какой задаче
Ну там какие-о можельные алгоритмы брали работы с коллекцией. Типа пополняли, итерировали сортировали. Что-то такое.
Короче, без мув-семантики вектор вообще сливал, теперь не знаю, если честно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>C++ таки? Логичнее же у какого-то объекта GetNext звать? E>Для трёх кучу делать странно, IMHO и min_elenemt'а за глаза:
Да тут не суть важно, главное алгоритм, ведь где три, там и тридцать три. А массивы вообще на диске могут лежать.
Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.
Здравствуйте, PM, Вы писали:
PM>Да не победил, но сильно не ухудшил, что уже хорошо.
Но он же ещё и поменял интерфейс алгоритмов, пусть изменение и незначительное, но нельзя просто взять старый алгоритм (необязательно из STL) и использовать с его итераторами.
И, кстати, не факт что это вообще будет работать для произвольного алгоритма — может он first и last хранит в одной переменной по очереди.
PM>Вообще, насколько я помню, основной трудностью у него было то, что в перспективе маячили параллельные иерархии bounded, counted and infinite ranges и для каждой иерархии нужно было реализовывать весь набор алгоритмов из стандартной библиотеки.
Это преувеличение.
В текущем STL плохо поддерживаются signle pass range с предикатом, типа sentinel. Его подход позволяет использовать эти single pass с предикатом, со старым кодом, чуть чуть его изменив.
Но, в STL не так много алгоритмов работающих только с single pass. Причём многие из них, являются всего лишь обвёртками вокруг других алгоритмов. Например [find_if, find, find_if_not, ...]
Другой пример приводил Sean: вот есть partition_point и partition_point_n. Первый работает с Bounded Range, а второй с Counted Range.
Казалось бы, использовав идею Eric'а — можно избавится от реализации одного из них. Но, если посмотреть реализацию partition_point — то она всего лишь делает:
То есть опять таки — алгоритм по сути всего лишь один, а partition_point это обвёртка.
И, кстати, это не только partition_point — но и lower_bound_n, lower_bound, upper_bound_n, upper_bound, binary_search_n, binary_search — всё обвёртки.
Да, можно сэкономить на написании нескольких мини-обёрток, но — это достигается 1) за счёт потери производительности 2) изменения интерфейса всех алгоритмов (со старым кодом сразу не заработает) 3) сама концепция выглядит как хак. Стоит ли оно того?
Здравствуйте, sokel, Вы писали:
S>Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.
while(!q.empty())
{
auto range = heap.top();
q.pop();
if(!q.empty())
{
auto ceiling = *begin(heap.top());
auto last = upper_bound(range, ceiling); // can be linear or binary search
copy(begin(range), last, out)
range = make_iterator_range(last, end(range));
if(!empty(range))
q.push(range);
}
else
{
copy(range, out);
}
}
Здравствуйте, sokel, Вы писали:
S>Да тут не суть важно, главное алгоритм, ведь где три, там и тридцать три. А массивы вообще на диске могут лежать.
Ну для трёх просто min_element может оказаться и быстрее кучи или сортированного массива...
S>Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.
Это всё не имеет смысла. Вставлять что бы всё равно придётся двигать половину массива в среднем, так что можно просто одним прогоном пузырька сортировать на самом деле...
А куча, кстати, log n на pop и log n на push, а откуда третий?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
EP>25.4.6.1 push_heap
EP>Complexity: At most log(last — first) comparisons.
EP>25.4.6.2 pop_heap
EP>Complexity: At most 2 * log(last — first) comparisons.
IMHO, в этой задаче время будет тратится на перемещения элементов, а не на сравнения...
Так что эти оценки вообще не о чём.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>IMHO, в этой задаче время будет тратится на перемещения элементов, а не на сравнения... E>Так что эти оценки вообще не о чём.
IMNSHO, стоимость перемещения пары указателей (или даже диапазона при вставке) вполне известна, а вот стоимость сравнения в общем случае нет.
К тому же сортировка в отличие от кучи даёт хорошую оптимизацию — части списков могут быть пройдены вообще без всяких перемещений.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
A>>А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.
EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
Можешь ответить поподробнее? Судя по рейтингу ты знаешь о чём пишешь, а не глупость написал.
Это как-то связано с попаданием в кеш процессора?
Но в этом случае тоже на мой взгляд сложно получить разницу на порядок. Список также легко влезет в кеш. Разница только в том, что надо хранить указатель на сл. элемент, но это не так много.
Здравствуйте, slava_phirsov, Вы писали:
_>Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи
Ну там вроде бы никто такой задачи перед собой не ставил.
IMHO, фишка в том, что С++ -- это такой язык-конструктор языков. Типа для каждого проекта его допиливают создав библу, инструкцию программиста и т. д...
Ну и соответственно его стандартная библиотека тоже не фреймворк для решения каких-то понятных задач, а фреймворк для создания фреймворков.
Задача сложнее и решение сложнее, не лишённое некоторой красоты.
Только это всё при реальной разработке обычно нифига не нужно. А приходится тащить буст, так как голый стл ни для чего, кроме разработки аналога буста не годен. Без какой-то дополнительной библиотеки на голом стл писать малореально. Увы, но он так спроектирован. Это некий расширяемый пример как писать бесшовно расширяемы фреймворк, но он ни под что конкретно не доделан. В Перле тоже есть такая библа, только она в сиходниках интерпретатора спрятана, а тут кишки наружу.
Это всё моё IMHO, но я ещё раз призываю задавать вопросы в ОТДЕЛЬНОЙ ТЕМЕ. Не надо портит оценку решения всякой философией!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Ororo1, Вы писали:
O>Условие: O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
Это называется merge sorting...
один этап -- merge.