Re: Оцените решение задачи
От: Кодт Россия  
Дата: 16.10.14 13:03
Оценка: 39 (3) +1
Здравствуйте, Ororo1, Вы писали:

А вот ещё одно решение. Его идея в том, чтобы учитывать сравнения, сделанные на предыдущих шагах.
Для простоты, сделал на мутабельных списках, из которых изымаются головные значения. Переделать на неизменяемые векторы — дело нехитрое, просто потребуется ещё и индексы/итераторы хранить.
  Скрытый текст
#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;
    }
}
Перекуём баги на фичи!
Re: Оцените старую шутку
От: B0FEE664  
Дата: 16.10.14 15:32
Оценка: 9 (1) :))
Здравствуйте, 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>Шутка!:

#include <iostream>
#include <vector>
#include <thread>
#include <chrono>

void GetNext(int n)
{
  std::this_thread::sleep_for(std::chrono::milliseconds(n*500));
  std::cout << "GetNext << " << n << std::endl;
}


void Start(int* p, size_t sz)
{
    for(size_t i = 0; i < sz; i++)
      std::thread(GetNext, p[i]);
}


int main(int argc, char * argv[])
{
    int vec1[] = {1, 5, 12, 28, 31};
    int vec2[] = {6, 9, 13};
    int vec3[] = {3, 7, 10, 15};

    Start(vec1, sizeof(vec1)/sizeof(int));
    Start(vec2, sizeof(vec2)/sizeof(int));
    Start(vec3, sizeof(vec3)/sizeof(int));
  
    std::this_thread::sleep_for(std::chrono::seconds(30));
    return 0;
}


И отсортированность списков не нужна!
И каждый день — без права на ошибку...
Re[6]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 17.10.14 21:45
Оценка: 4 (1) +1
Здравствуйте, 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
Автор: koodeer
Дата: 06.04.14
(помогающий скрыть latency при предсказуемых паттернах доступа (типа обхода массива), и бесполезный для списков, так как пока не пришёл новый узел — неизвестно что prefetch'ить).

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



Тест

#define _HAS_ITERATOR_DEBUGGING 0
#define _SECURE_SCL 0

#include <boost/exception/detail/type_info.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/numeric.hpp>
#include <boost/timer/timer.hpp>
#include <exception>
#include <iostream>
#include <iterator>
#include <vector>
#include <list>

#if defined(__GNUC__)
    #define NOINLINE __attribute__ ((noinline))
#elif defined(_MSC_VER)
    #define NOINLINE __declspec(noinline)
#else
    #define NOINLINE
#endif

template<int> NOINLINE void ASM_MARKER() { volatile int x = 0; (void)x; }

/******************************************************************************/
using namespace std;

template<typename Range>
void test(Range &r)
{
    cout << boost::type_name<Range>() << ": ";

    boost::timer::auto_cpu_timer t;

    ASM_MARKER<111>();
    volatile int result = boost::accumulate(r, 0);
    ASM_MARKER<222>();

    (void)result;
}

int main() try
{
    vector<int> v(40000000);
    boost::iota(v, 0);
    boost::random_shuffle(v);

    list<int> l(begin(v), end(v));

    l.sort();
    boost::sort(v);

    for(unsigned i=0; i!=2; ++i)
    {
        test(v);
        test(l);
    }
}
catch(const exception &e)
{
    cout << e.what() << endl;
}
Результат (MSVC2010 x64 Release):
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


LIVE DEMO on Coliru — тут уменьшенно N, чтобы вписаться в лимит времени.



P.S. Это относится не только к спискам. Например, если есть массив указателей на объекты (чем грешат многие "true-OO" языки), особенно перетасованных/пересортированных, то мы получим подобный эффект — каждое чтение имеет малое КПД, да ещё и из произвольного места.
Отредактировано 17.10.2014 22:02 Evgeny.Panasyuk . Предыдущая версия .
Re[4]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 19:16
Оценка: +1 -1
Здравствуйте, alzt, Вы писали:

К>>>- сделать не на векторах, а по-честному, на списках

К>>>- на изменяемых списках это даже проще делается
EP>>А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента.
EP>>Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой.
A>А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.

Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.
Re[14]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 18:55
Оценка: +2
Здравствуйте, smeeld, Вы писали:

EP>> не лишает обход перетасованного списка его random'ной сущности

S>Мы говорим о реальных задачах, и существующих оптимальных механизмах
S>их решения, или о вакуумном, строго последовательном расположении
S>объектов и таком же доступе к ним? Понятно, что аккумулировать на
S>непрерывный массив c paddd/addpd быстрее, но что если у нас, скажем,
S>протокол обмена с форматом сообщений и с полнсотью рандомной их обработкой?
S>Тогда наоборот, доступ и управление данными по принципу непрерывного
S>массива-это качели.

Не пойму что ты пытаешься сказать. То что у списка есть своя область применения? Так с этим никто и не спорил
Re[17]: Оцените решение задачи
От: smeeld  
Дата: 17.10.14 17:31
Оценка: -2
Здравствуйте, slava_phirsov, Вы писали:


_>Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи


Есть там всё, что есть в STL, но это не введенно в стандарт самого языка, что правильно.
Re[18]: Оцените решение задачи
От: smeeld  
Дата: 18.10.14 08:32
Оценка: :))
Здравствуйте, Erop, Вы писали:


E>Ну там вроде бы никто такой задачи перед собой не ставил.


Там эта задача решена, на уровне синтаксиса языка.
Что вообще такое всё, что есть в STL? Вот есть в perl
хеши %h()-ассоциативные массивы, они же словари в питоне,
удобно. В C++ нет, хотя этот язык претендует на высокоуровневый,
с возможностью низкоуровневых оптимизаций, но с средствами для упрощения
и ускорения разработки прикладных приложений, прежде всего, и для этого
обязанный содержать развитые всокоуровневые структуры данных. Так же
про остальные: списки, массивы-всё это в упомянутых языках
на уровне синтаксиса с реализацией на уровне интерпретатора.
В погоне за "не отстать и перегнать" появляются костыли, вроде
STL, которые вводятся в стандарт. ИМХО получилось неуклюже.
Re[8]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:38
Оценка: 10 (1)
Здравствуйте, Erop, Вы писали:

EP>>У контейнеров size_type — это implementation-defined беззнаковое целое.

E>А есть гарантия, что беззнаковое?

Да: "Table 96 — Container requirements ... X::size_type unsigned integer type"

E>Тоесть, если я хочу свой конетейнер сделать для стандартных алгоритмов, то я тоже обязан беззнаковое использовать для инндекса, например?


Не помню есть ли алгоритмы STL принимающие контейнер, а не итераторы. Вспоминается только boost'вский remove_erase и подобные.
А так да, если бы были — то да, формально должно быть беззнаковое.
Re[17]: Оцените решение задачи
От: PM  
Дата: 15.10.14 19:00
Оценка: 9 (1)
Здравствуйте, 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 может быть увидим что-нибудь
Re[10]: Оцените решение задачи
От: watchmaker  
Дата: 16.10.14 18:01
Оценка: 9 (1)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, watchmaker, Вы писали:


W>>Но, разумеется, скорость работы меряется не числом инструкций. Поэтому тест нужно ещё запустить. Возьмём миллион чисел, положим их в контейнер.


EP>accumulate шёл сразу после заполнения? Если так, то многие из элементов могут быть уже в L3. Попробуй миллионов 100 — чтобы гарантированно вымыть весь кэш.

На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор :)
А относительная скорость осталась почти такой же.
Re[12]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 18:43
Оценка: 8 (1)
Здравствуйте, BulatZiganshin, Вы писали:

W>>На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор

W>>А относительная скорость осталась почти такой же.

BZ>а не в 3 — int vs int+int*?


x64 (и не забываем про alignment)
односвязный список: Node*+int32, sizeof = 16, то есть 4x
двусвязный список: 2*Node*+int32, sizeof = 24, то есть 6x
Отредактировано 16.10.2014 18:48 Evgeny.Panasyuk . Предыдущая версия .
Re[18]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 20:02
Оценка: 6 (1)
Здравствуйте, PM, Вы писали:

PM>Прогресс есть, реально в VS 2013 не хватет только constexpr из-за отсутствия которого самые современные библиотеки не поддерживаются. Я с Boost.Hana так пролетел, вот и ворчу


А что Hana даёт по сравнению с Boost.Fusion? Только скорость компиляции?

PM>Кстати недавно Eric победил проблему с производительностью counted ranges


Спасибо за ссылку.
Но ведь не побелил же! Его концепция CountedRange выглядит как хак, который к тому же жертвует производительностью. Если бы не было потерь производительности, то можно было бы и пережить, а с потерями — ну уж нет.
ИМХО, не нужно боятся вводить новые концепции Range'ей — если эти концепции чётко улавливают суть, позволяя писать эффективный код (в абсолютном смысле, а не "в этом вот примере всего 5%" разница). Не хаки-адапторы к старым алгоритмам, а полноценные range со своими оптимизированными алгоритмами, как раз наподобие partition_point_n.

PM>на которую указывал Sean Parent (я так понимаю он автор ranges в ASL).


Он, кстати, раньше работал со Степановым.

Кстати, вот в этом моменте Степанов рассказывает про Counted Range.
А вот тут про двумерные итераторы (Eric упоминал их как segmented).
Re: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 13:10
Оценка: 1 (1)
Вот тут
Автор: Lazin
Дата: 10.07.14
было обсуждение подобной задачи.
В частности, простое решение выглядит вот так:
while(!q.empty())
{
    auto range = heap.top();  
    q.pop();

    //assert(!empty(range));
    *out++ = *begin(range);
    range = make_iterator_range(next(begin(range)), end(range));
    if(!empty(range))
        q.push(range);
}

Легко приводится к интерфейсу GetNext.
Re: Оцените решение задачи
От: Кодт Россия  
Дата: 15.10.14 12:47
Оценка: +1
Здравствуйте, Ororo1, Вы писали:

O>Решение:


Оценка — на 4-
Улучшать можно по-всякому:
— разнести типы значений и индексов
— сделать не на векторах, а по-честному, на списках
— на изменяемых списках это даже проще делается
— at() не нужен, у нас в штатном режиме исключений выхода за границы не возникает — достаточно operator[]
— диагностику конца последовательности можно и без исключений сделать, а, например, отдельной функцией
— для трёх списков можно захардкодить, хотя обобщение на произвольное количество списков — в общем-то, правильный подход
Перекуём баги на фичи!
Re: Оцените решение задачи
От: dead0k  
Дата: 15.10.14 12:59
Оценка: +1
Здравствуйте, Ororo1, Вы писали:

Вместо вектора векторов и вектора индексов логичней использовать 1 вектор итераторов
Хотя я бы сделал проксю на 3 ячейки, которая хранит в себе копии текущих елементов (вместе с итераторами/индексами ессно): выдал наименьший из списка, взял из списка следующий, чтобы лишний раз в память не лазить (хотя это преждевременная оптимизация)

ps/
Впрочем, вру, конечно же, все равно надо еще хвосты запоминать, так-что векторов и с итераторами надо 2 :p
Отредактировано 15.10.2014 13:08 dead0k . Предыдущая версия .
Re[4]: Оцените решение задачи
От: PM  
Дата: 15.10.14 15:30
Оценка: -1
Здравствуйте, 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()));



И это блин в 21-м веке в С++ коде: https://github.com/v8/v8/blob/master/include/v8.h#L1986
Re[8]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 16:14
Оценка: :)
Здравствуйте, slava_phirsov, Вы писали:

EP>>Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно ровняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
Автор: andyp
Дата: 17.07.13
.

_>Ну вот некоторое время назад был холивар как раз по поводу STL, и как она ужасна, и что "написал он ее в больнице, и не иначе, как то была больница Степанова-Скворцова ит.п.".

STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая
Что конкретно "ужасного" там?
Re[11]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 16:42
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>То решение которое он предлагает в D крайне спорное. Точнее оно отлично подходит для InpuntRange, а вот начиная с ForwardRange начинаются серьёзные проблемы. Всё подробно описано в этом топике
Автор: matumba
Дата: 21.10.13
.


Что до меня, то только то, что, в STL, к примеру, для копирования требуется не забыть переразмерить контейнер-приемник (вариант — не забыть использовать std::inserter), что std::remove на самом деле не удаляет, а просто перетасовывает элементы, и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[2]: Оцените решение задачи
От: sokel Россия  
Дата: 16.10.14 07:00
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Вот тут
Автор: Lazin
Дата: 10.07.14
было обсуждение подобной задачи.

EP>В частности, простое решение выглядит вот так:
EP>
EP>while(!q.empty())
EP>{
EP>    auto range = heap.top();  
EP>    q.pop();

EP>    //assert(!empty(range));
EP>    *out++ = *begin(range);
EP>    range = make_iterator_range(next(begin(range)), end(range));
EP>    if(!empty(range))
EP>        q.push(range);
EP>}
EP>

EP>Легко приводится к интерфейсу GetNext.

ага, как то так наверное:
#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

typedef vector<int> list_type;

void print(const vector<list_type>& in) {
    using list_iter = pair < list_type::const_iterator, list_type::const_iterator >;
    using iter_state = vector < list_iter >;
    iter_state iters;
    iters.reserve(in.size());
    for(auto& l : in)
        if(!l.empty())
            iters.push_back({ l.begin(), l.end() });

    auto iter_compare = [](const list_iter& i1, const list_iter& i2) {
        return *i1.first > *i2.first;
    };
    make_heap(iters.begin(), iters.end(), iter_compare);
    while(!iters.empty()) {
        pop_heap(iters.begin(), iters.end(), iter_compare);
        list_iter& min = iters.back();
        cout << *min.first << endl;
        ++min.first;
        if(min.first == min.second)
            iters.pop_back();
        else
            push_heap(iters.begin(), iters.end(), iter_compare);
    }
}

int main(int argc, char* argv[])
{
    vector<list_type> lists = {
            { 1, 5, 12, 28, 31 },
            { 6, 9, 13 },
            { 3, 7, 10, 15 }
    };
    print(lists);
    return 0;
}
Re: Оцените решение задачи
От: Andrew.W Worobow https://github.com/Worobow
Дата: 16.10.14 07:04
Оценка: -1
Здравствуйте, Ororo1, Вы писали:

O>Условие:

O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!

Что значит нельзя объединят?
Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков.
Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.
Не все кто уехал, предал Россию.
Re[6]: Оцените решение задачи
От: watchmaker  
Дата: 16.10.14 09:36
Оценка: +1
Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.


_>Намекаешь на то, что элементы вектора помещаются в памяти последовательно и компактно, а элементы списка — как повезет?

Даже если разместить узлы списка в памяти последовательно, то всё равно его обход будет в разы медленнее вектора. Неприятно, но не катастрофа.
Но вот если действительно узлы списка раскидать по памяти, то одних кеш-промахов уже вполне будет достаточно для той самой просадки на порядки.
Ещё тут стоит учесть, что элементы списка расходуют память менее экономно чем вектор. Что опять же не лучшим образом сказывается на использовании кеша.

_>:???: Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента.

Чтение адреса следующего элемента — это плохо. Плохо потому что это ещё одно чтение из памяти, и плохо потому что программа как правило не может продвинутся дальше до завершения чтения из-за зависимости по данным. Ведь в типичной итерации по списку сначала читается адрес следующего элемента, а затем он обрабатывается. Суперскалярный процессор может делать эти операции почти параллельно для вектора, но лишь последовательно для списка.

Ну и прочие эффекты, вроде автоматической векторизации и распараллеливания алгоритмов могут играть свою роль. Для вектора компилятор во многих случаях умеет делать это автоматически, для списка же шансов практически нет.
Re[8]: Оцените решение задачи
От: watchmaker  
Дата: 16.10.14 15:49
Оценка: +1
Здравствуйте, 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>кеша специализированного аллокатора. Так что никакой раскиданности.
Это верно разве что только после первичного заполнения списка. Как только начинаются его модификации, то сразу всё идёт кувырком.
Да вот, например, тут
Автор: Evgeny.Panasyuk
Дата: 15.10.14
предложено собрать один список из трёх, видимо переставляя только указатели, не перенося сами элементы. Но после этого многие связи между элементами будут совсем нелокальными, даже если изначально все элемента каждого списка шли подряд. Конечно, в данной задаче с тремя списками это не так критично — отслеживать три потока prefetch современные x86 процессоры умеют, — но в чуть-чуть более сложной задаче всё будет куда печальнее. Удали, например, из списка элемент, потом добавь к нему новый в начало. Либо просто вставь новый элемент в середину. Где будет для него память выделена? Будет ли она в той же или в соседней кеш-линии, что и память его соседей по списку? Да совсем не обязательно. И никакой специализированный аллокатор это не поборет.
А если вставки или удаления в середине не нужны, то зачем тем более выбирать список, а не массив?
Re[3]: Оцените решение задачи
От: Кодт Россия  
Дата: 16.10.14 16:01
Оценка: +1
Здравствуйте, xobotik, Вы писали:

X>Здравствуйте, Кодт, Вы писали:

К>>int main()
К>>{
К>>    sources = vector< list<int> > {
К>>        list<int> { 1, 5, 12, 28, 31 },
К>>        list<int> { 6, 9, 13 },
К>>        list<int> { 3, 7, 10, 15 },
К>>    };
К>>}

X>Это не объединение ?)

Это — не объединение. Это три независимых списка, к которым сделан доступ по индексу.
Легко убедиться, заменив вектор списков на вектор указателей на списки, как у топикстартера.
Перекуём баги на фичи!
Re[10]: Оцените решение задачи
От: watchmaker  
Дата: 16.10.14 17:27
Оценка: +1
Здравствуйте, 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>Списки нужны как раз когда есть необходимость тасовать объекты.
Ну так именно об этом я и говорю: если нужно тасовать объекты, то о каком попадании в кеш идёт речь? После хорошей перетасовки каждый следующий элемент списка будет находится чуть ли не в случайной области памяти. А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора.
Re[11]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 18:10
Оценка: :)
Здравствуйте, watchmaker, Вы писали:

W>А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора.


Все элементы находятся в пределах одного непрерывного массива, список обеспечивает лишь
политику доступа и управления элементами, которые в реальности распологаются в виде массива.
О каких промахах, идёт речь, которых не будет при политике доступа и управления объектами
по принципу непрерывного последовательного массива?
Re[2]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:13
Оценка: +1
Здравствуйте, Sni4ok, Вы писали:

S>ну и в целом всё убого- скорость O(N^2) вместо напрашивающихся O(n * log(n))


Вроде бы O(N) оба раза, если N -- cуммарная длина списков, например...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:23
Оценка: :)
Здравствуйте, Erop, Вы писали:

EP>>Что конкретно "ужасного" там?

E>http://rsdn.ru/forum/cpp/2072440.flat
Автор: gid_vvp
Дата: 23.08.06


45 страниц могу же сорваться и ответить там на что-нибудь

E>http://rsdn.ru/forum/cpp/2073293.flat
Автор: Mazay
Дата: 23.08.06


Потоки ввода-вывода это не STL.

E>http://rsdn.ru/forum/cpp/2074310.flat
Автор: asdfghjkl
Дата: 24.08.06


std::string это не STL.

E>и т. д...


Не холивара ради — приведи пару пунктов от себя.

E>Но даже вот прикручивать к массиву аллокатор вроде CAutoBuffer
Автор: Erop
Дата: 27.02.07
'а, и то так и не разрешили...


Вроде же добавили поддержку stateful allocators в C++11
Re[11]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:38
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Не холивара ради — приведи пару пунктов от себя.

Заведи топик, где это не офтопик -- приведу
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Оцените старую шутку
От: sokel Россия  
Дата: 16.10.14 22:16
Оценка: :)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, B0FEE664, Вы писали:


К><>

К>Хороший подход, но энергичный.
К>В случае, если списки длинные (и упорядоченные), то можно запустить столько потоков, сколько есть списков, и засыпать там
К>- либо к заданным моментам времени — sleep_until, естественно, отсчитывая от момента старта программы
К>- либо на время разности между смежными значениями (это хуже, т.к. набежит ошибка)

Нене, это всё излишне усложняет. Преждевременная оптимизация, так можно и до одного потока докатиться...
Re[16]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 17.10.14 15:48
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Вообще речь идёт об альтернативе чему? Главная проблема STL в том, что это библиотека не понятно для чего...


Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 17.10.2014 16:38 slava_phirsov . Предыдущая версия .
Оцените решение задачи
От: Ororo1  
Дата: 15.10.14 12:29
Оценка:
Условие:
Даны 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);
    
}
c++ задачи
Re[2]: Оцените решение задачи
От: Ororo1  
Дата: 15.10.14 13:07
Оценка:
Здравствуйте, Кодт, Вы писали:

К>- сделать не на векторах, а по-честному, на списках

К>- на изменяемых списках это даже проще делается

Я забыл указать, что по условию можно любой контейнер использовать. Спасибо за коммент, учту на будущее.
Re[2]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 13:15
Оценка:
Здравствуйте, Кодт, Вы писали:

К>- сделать не на векторах, а по-честному, на списках

К>- на изменяемых списках это даже проще делается

А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента.
Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой.
Отредактировано 15.10.2014 13:19 Evgeny.Panasyuk . Предыдущая версия .
Re: Оцените решение задачи
От: PM  
Дата: 15.10.14 13:46
Оценка:
Здравствуйте, Ororo1, Вы писали:

Фактически все равно это слияние отсортированных списков, только результат возвращается из 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 lists

    int min = *heads[min_head];
    ++heads[min_head];
    return min; 
}
O>}
O>
Re: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 14:02
Оценка:
Здравствуйте, Ororo1, Вы писали:

O>

// почему не const& ? У среднестатистического программиста,
// который увидел передачу не-const ссылки, сразу возникает мысль,
// что передаваемый объект будет меняться внутри функции.
O>bool isExist(vector<int> &vec, int index)

O>
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 14:04 slava_phirsov . Предыдущая версия . Еще …
Отредактировано 15.10.2014 14:03 slava_phirsov . Предыдущая версия .
Re[2]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 14:15
Оценка:
PM>int GetNext()
PM>{
PM>    int min_head = 0;
PM>    for (int i = 0; i < NUM_LISTS; ++i)
PM>    {
PM>        if (heads[i] == lists[i].end()) continue;
PM>        if (*heads[i] < *heads[min_head]) min_head = i; // свалится, когда heads[0] == lists[0].end()
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>>}
O>>
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[2]: Оцените решение задачи
От: Ororo1  
Дата: 15.10.14 14:31
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, Ororo1, Вы писали:


O>>

_>// почему не const& ? У среднестатистического программиста,
_>// который увидел передачу не-const ссылки, сразу возникает мысль,
_>// что передаваемый объект будет меняться внутри функции.
O>>bool isExist(vector<int> &vec, int index)

O>>


Да, в реальном проекте конечно же желательно const дописать, а в данном случае не принципиально. Индексы вон тоже бы unsigned не помешало сделать.
Re[2]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 14:34
Оценка:
Здравствуйте, PM, Вы писали:


 
    int min = *heads[min_head];
    ++heads[min_head];
    return min;

    return *(heads[min_head]++);
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 14:37
Оценка:
Здравствуйте, Ororo1, Вы писали:


O>не принципиально


Вообще-то такое должно на автомате писаться. Ну я так думаю


O>Индексы вон тоже бы unsigned не помешало сделать


Основоположники, ЕМНИП, учили использовать unsigned там и только там, где требуются операции с битами.


P.S.

Наташенька, ну вы же сами просили сказать, что я думаю

Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 14:38 slava_phirsov . Предыдущая версия .
Re[3]: Оцените решение задачи
От: PM  
Дата: 15.10.14 15:05
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

PM>>        if (heads[i] == lists[i].end()) continue;
PM>>        if (*heads[i] < *heads[min_head]) min_head = i; // свалится, когда heads[0] == lists[0].end()

С чего бы, если строкой выше есть проверка с комментарием "пропускаем пройденный список"

Я код проверял на тестовых данных топикстартера, всё отработало.
Re[3]: Оцените решение задачи
От: PM  
Дата: 15.10.14 15:13
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>
_> 
_>    int min = *heads[min_head];
_>    ++heads[min_head];
_>    return min;
_>
_>    return *(heads[min_head]++);
_>


Не хочу ломать голову в приоритете операций, но похоже у тебя сначала идет инкремент итератора и затем его разыменование. И вот тут оно может свалиться. Нужного эффекта можно добиться с
return *heads[min_head]++;
, но постинкременту я предпочитаю явный порядок следования. Кто-то будет это читать и тоже ломать голову. Олдскульных сишников очень мало и они тоже могут ошибаться.

[offtoppic]
Вот не припомню, когда реально без постинкремента было невозможно обойтись. Вот фича, которую надо бы сделать deprecated в С++17 вместо триграфов
[/offtopic]
Re[4]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 15:24
Оценка:
Здравствуйте, PM, Вы писали:

_>>
_>>    return *(heads[min_head]++);
_>>


PM>Не хочу ломать голову в приоритете операций, но похоже у тебя сначала идет инкремент итератора и затем его разыменование. И вот тут оно может свалиться.


С чего бы это? Постфиксный инкремент возвращает предыдущее значение итератора, так что, все честно.

PM>Вот не припомню, когда реально без постинкремента было невозможно обойтись. Вот фича, которую надо бы сделать deprecated в С++17 вместо триграфов


И не надейся! Вангую: deprecated будут объявлены optional и лямбды
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 15:28
Оценка:
Здравствуйте, 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>>}
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[5]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 15:35
Оценка:
Здравствуйте, PM, Вы писали:

PM>емнип это авторы Java оправдывались почему у них нет беззнаковых целых


А мне наоборот припоминается, что это Страуструп в своем талмуде писал такое. Вот нет под рукой, дойду до него — дам точную ссылку


PM>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t (с которым кстати в теории можно адресовать в 2 раза больше элементов чем с int)


Ну вот, да, а в Qt наоборот сплошь и рядом int. Люди не смогли договориться о порядке следования байт, где уж тут знаковость индексов уединообразить. Только диктатура спасет человечество, ИМХО
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[5]: Оцените решение задачи
От: PM  
Дата: 15.10.14 15:51
Оценка:
Здравствуйте, 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;
}
Re[6]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 15:59
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

PM>>емнип это авторы Java оправдывались почему у них нет беззнаковых целых

_>А мне наоборот припоминается, что это Страуструп в своем талмуде писал такое. Вот нет под рукой, дойду до него — дам точную ссылку

Страуструп да, писал и говорил нечто подобное.
Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно равняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
Автор: andyp
Дата: 17.07.13
.
Использование знаковых целых, там где по смыслу подходят беззаконные (да ещё и имеют большую "ёмкость") — это что-то из области babysitting'а, причём не особо удачного. Если уж нужна нянька — то тогда уже брать safe int и им подобные.
Отредактировано 15.10.2014 16:01 Evgeny.Panasyuk . Предыдущая версия .
Re[7]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 16:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Но если уж и апеллировать к авторитетам, то в области алгоритмов C++ нужно ровняться на Степанова — он спокойно использует беззнаковые, и ЕМНИП даже рекомендовал в одной из лекций
Автор: andyp
Дата: 17.07.13
.


Ну вот некоторое время назад был холивар как раз по поводу STL, и как она ужасна, и что "написал он ее в больнице, и не иначе, как то была больница Степанова-Скворцова ит.п.". Тут можно до бесконечности кидаться ссылками, конечно...

И меряться авторитетами основоположников тоже можно долго


EP>Использование знаковых целых, там где по смыслу подходят беззаконные (да ещё и имеют большую "ёмкость") — это что-то из области babysitting'а, причём не особо удачного. Если уж нужна нянька — то тогда уже брать safe int и им подобные.


Одна беда: если ты пишешь под фреймворк, где используются знаковые, то у тебя особого выбора нет. В чужой монастырь со своим уставом (даже не со своим, а со степановским)... Я бы вот предпочел использовать везде size_t, вроде для того оно и предназначалось, но, увы...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 16:06 slava_phirsov . Предыдущая версия .
Re[9]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 16:26
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая

EP>Что конкретно "ужасного" там?

Не буду цитировать холиварщиков, тем более, что я не разделяю многие из их аргументов, гугл в помощь, если интересно. Вроде читал даже на КЫВТе сенсационный пост типа "Александреску опускает итераторы"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[10]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 16:36
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

EP>>STL одна из лучших библиотек которые я видел, я бы сказал даже лучшая

EP>>Что конкретно "ужасного" там?
_>Не буду цитировать холиварщиков, тем более, что я не разделяю многие из их аргументов, гугл в помощь, если интересно. Вроде читал даже на КЫВТе сенсационный пост типа "Александреску опускает итераторы"

То решение которое он предлагает в D крайне спорное. Точнее оно отлично подходит для InpuntRange, а вот начиная с ForwardRange начинаются серьёзные проблемы. Всё подробно описано в этом топике
Автор: matumba
Дата: 21.10.13
.

И, кстати, на эту тему недавно был доклад — там также отмечается, что хоть в некоторых крайне специфичных случаях D Range это удобно, но в общем случае менее мощное средство чем итераторы.
Re[12]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 17:14
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Что до меня, то только то, что, в STL, к примеру, для копирования требуется не забыть переразмерить контейнер-приемник (вариант — не забыть использовать std::inserter)


Для подобных вещей есть assign, insert, и те же *inserter'ы.
И что именно значит "не забыть"? Видимо "не забыть" означает "нужно знать что делает используемый инструмент, а не использовать его наугад" — тогда да, нужно

_>что std::remove на самом деле не удаляет, а просто перетасовывает элементы


Ничего удивительного — std::remove работает с range'ами, а не с контейнерами. Принял range, и возвратил range (точнее два).

_>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.


Что значит "кучку однотипных аргументов"?
Отредактировано 15.10.2014 17:14 Evgeny.Panasyuk . Предыдущая версия .
Re: Оцените решение задачи
От: Sni4ok  
Дата: 15.10.14 17:17
Оценка:
Здравствуйте, Ororo1, Вы писали:

решение ужасно, вы ожидаете выброс исключения:
vectors.at(i)

однако нигде его не перехватываете.
ну и в целом всё убого- скорость 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>
Re[2]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 17:23
Оценка:
Здравствуйте, Sni4ok, Вы писали:

S>ну и в целом всё убого- скорость O(N^2) вместо напрашивающихся O(n * log(n))


Откуда там квадратичная сложность?
Re[13]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 17:24
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>И что именно значит "не забыть"? и пр.


Это означает, что оно не интуитивно понятно трудно для понимания начинающему. И есть мнение, что все это можно было сделать проще и безопаснее за счет незначительных накладных расходов.


_>>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.


EP>Что значит "кучку однотипных аргументов"?


template <class ForwardIterator1, class ForwardIterator2>
   ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1,
                            ForwardIterator2 first2, ForwardIterator2 last2)


4 аргумента. И можно перепутать аргументы местами, и это скомпилируется без проблем. Да, это один из видов "bad smell" — слишком много аргументов.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 17:27 slava_phirsov . Предыдущая версия .
Re[3]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 17:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

Не, главное, с чего там O(n * log2(n))?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 17:33 slava_phirsov . Предыдущая версия .
Re[14]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 17:34
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>И есть мнение, что все это можно было сделать проще и безопаснее за счет незначительных накладных расходов.


Например.

_>>>и то, что большинство библиотечных алгоритмов принимают кучку однотипных аргументов (подходя формально — "bad smell" в чистом виде) — это все не есть достоинства.

EP>>Что значит "кучку однотипных аргументов"?
_>
_>template <class ForwardIterator1, class ForwardIterator2>
_>   ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1,
_>                            ForwardIterator2 first2, ForwardIterator2 last2)
_>

_>4 аргумента. И можно перепутать аргументы местами, и это скомпилируется без проблем. Да, это один из видов "bad smell" — слишком много аргументов.

1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов.
2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно.
3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев.
Re[15]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 15.10.14 17:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов.


Одна беда — поскольку типы итераторов являются параметрами шаблона, то типизация не помешает перепутать эти пары местами.

Что касается конкретно std::search, то ИМХО в большинстве случаев все четыре итератора будут одного типа.


EP>2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно.


Однако новички путаются. И даже — страшно сказать — забывают, надо ли ставить первым в паре begin или end. Да, со временем привыкают. Ну так о том и речь, что ножик-то слишком острый, чтобы намазывать им масло на хлеб, мог бы быть и потупее.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 15.10.2014 17:42 slava_phirsov . Предыдущая версия .
Re[16]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 18:02
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

EP>>1. Четыре одинаковых итератора тут только в частном случае, а в общем — две пары разных итераторов.

_>Одна беда — поскольку типы итераторов являются параметрами шаблона, то типизация не помешает перепутать эти пары местами.

В таком случае даже Range версия не спасёт
template<class ForwardRange1, class ForwardRange2>
typename range_iterator<ForwardRange1>::type
search(ForwardRange1& rng1, const ForwardRange2& rng2);


_>Что касается конкретно std::search, то ИМХО в большинстве случаев все четыре итератора будут одного типа.


Согласен.

EP>>2. Во всей библиотеке итераторы из одного range принимаются по порядку, запутаться трудно.

_>Однако новички путаются. И даже — страшно сказать — забывают, надо ли ставить первым в паре begin или end. Да, со временем привыкают.

Код-то тестировать (или в крайнем случае проверять на нескольких входных данных вручную) нужно в любом случае, там всё и вылезет. Плюс есть же всякие debug итераторы и алгоритмы.

_>Ну так о том и речь, что ножик-то слишком острый, чтобы намазывать им масло на хлеб, мог бы быть и потупее.


Скоро будет в стандарте. А в библиотеках уже больше 10 лет.
Re[15]: Оцените решение задачи
От: PM  
Дата: 15.10.14 18:11
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

>3. Какая альтернатива-то? Естественно без потери мощности в общем случае. Есть Boost.Range (или его аналог который войдёт в стандарт) — но это всего лишь оптимизация некоторых частных случаев.


Аналог Boost.Range это https://github.com/ericniebler/range-v3 ? Выглядит многообещающе, еще бы попробовать их где-нибудь, но Visual C++ опять не поддерживается.

Блин, 15 лет прошло со времен плясок вокруг Visual C++ 6.0 в попытках заставить его компилировать шаблоны. Как будто ничего и не поменялось
Re[16]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 15.10.14 18:32
Оценка:
Здравствуйте, 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
Re[3]: Оцените решение задачи
От: alzt  
Дата: 15.10.14 19:09
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

К>>- сделать не на векторах, а по-честному, на списках

К>>- на изменяемых списках это даже проще делается

EP>А зачем? Тормозить ведь будет. Это же не merge списков в один большой, а просто вывод по отдельности каждого элемента.

EP>Вот если бы в результате нужен был список — то другое дело, действительно проще и быстрее собрать из трёх связанных списков один большой.

А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.
Re[4]: Оцените решение задачи
От: Andrew.W Worobow https://github.com/Worobow
Дата: 16.10.14 07:14
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_> Основоположники, ЕМНИП, учили использовать unsigned там и только там, где требуются операции с битами.


Беззнаковые надо использовать там где нет и не может быть отрицательных индексов и смешений-перемешений.
И это очень важно, и к этому следует привыкать и стремится.
То есть знаковые только там где арифметика, и отрицательный офсет НУЖЕН. Везде где индексы только "size_t".
Не все кто уехал, предал Россию.
Re[5]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 16.10.14 08:12
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.


Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента. Намекаешь на то, что элементы вектора помещаются в памяти последовательно и компактно, а элементы списка — как повезет?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[19]: Оцените решение задачи
От: PM  
Дата: 16.10.14 09:11
Оценка:
Здравствуйте, 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 со временем будут добавляться.
Re: Оцените решение задачи
От: Кодт Россия  
Дата: 16.10.14 10:01
Оценка:
Здравствуйте, Ororo1, Вы писали:


Есть ещё одно решение, лежащее в стороне.
Это использование приоритетной очереди.
  по-быстрому накидал
#include <vector>
#include <queue>
#include <algorithm>
#include <iostream>
#include <iterator>

// исходные данные
typedef int Value;
typedef std::vector<Value> Source;

typedef std::vector<Source> Sources;
Sources sources;

// состояние вектора можно представить как диапазон итераторов
typedef Source::iterator SourceIter;
typedef std::pair<SourceIter,SourceIter> SourceRange;

SourceRange full_range(Source& s) { return SourceRange(s.begin(), s.end()); }
bool is_empty(SourceRange const& sr) { return sr.first == sr.second; }
Value peek(SourceRange const& sr) { return *sr.first; }
Value shift(SourceRange& sr) { return *sr.first++; }

std::ostream& operator<< (std::ostream& ost, SourceRange const& sr)
{
    ost << "[";
    std::copy(sr.first, sr.second, std::ostream_iterator<Value>(ost,","));
    ost << "]";
    return ost;
}

// очередь

struct Comparator // над непустыми диапазонами
{
    bool operator()(SourceRange const& l, SourceRange const& r) const
    {
        std::cout << "   -- comparing " << l << " to " << r << std::endl;
        return peek(l) > peek(r);
    }
};

typedef std::priority_queue<SourceRange, std::vector<SourceRange>, Comparator> Queue; // очередь из непустых диапазонов
Queue queue;

void add_to_queue(SourceRange const& sr)
{
    std::cout << " -- adding " << sr << std::endl;
    if(!is_empty(sr))
        queue.push(sr);
    std::cout << std::endl;
}

void add_to_queue_full_range(Source& s)
{
    add_to_queue(full_range(s));
}

// собственно, наша задача

void setup()
{
    for(Source& s : sources)
        add_to_queue_full_range(s);
}

bool done()
{
    return queue.empty();
}

Value next()
{
    SourceRange sr = queue.top();
    std::cout << " -- taking " << sr << std::endl;

    queue.pop();
    std::cout << std::endl;

    Value v = shift(sr);
    add_to_queue(sr);
    return v;
}

// проверяем

int main()
{
    sources = Sources {
        Source { 1, 5, 12, 28, 31 },
        Source { 6, 9, 13 },
        Source { 3, 7, 10, 15 },
    };

    setup();

    std::cout << "--------" << std::endl;
    while(!done())
    {
        Value v = next();
        std::cout << v << std::endl;
    }
}
Перекуём баги на фичи!
Re[2]: Оцените решение задачи
От: Кодт Россия  
Дата: 16.10.14 12:18
Оценка:
Здравствуйте, Andrew.W Worobow, Вы писали:

O>>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!


AWW>Что значит нельзя объединят?

Очевидно же: нельзя создавать копию данных.

AWW>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков.

AWW>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.

Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.
Перекуём баги на фичи!
Re[3]: Оцените решение задачи
От: Andrew.W Worobow https://github.com/Worobow
Дата: 16.10.14 12:32
Оценка:
Здравствуйте, Кодт, Вы писали:


O>>>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!


AWW>>Что значит нельзя объединят?

К>Очевидно же: нельзя создавать копию данных.

Надо тогда в задаче четко написать — стоимость копирования данных равна 100*О.

AWW>>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков.

AWW>>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.

К>Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.


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

Вообще дерево можно сделать и без копирования, ну ссылки там, указатели.
Тут то еще вопрос — "А где написано что копировать нельзя?"
Не все кто уехал, предал Россию.
Re[7]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 12:55
Оценка:
Здравствуйте, watchmaker, Вы писали:

_>> Откуда порядки? Обход вектора — грубо говоря — выкомпилируется в инкремент адреса на константу. Обход связного списка — в чтение адреса следующего элемента.


Переход по списку p=p->next;

  4006fc:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400700:       48 8b 00                mov    (%rax),%rax
  400703:       48 89 45 f8             mov    %rax,-0x8(%rbp)


Переход по массиву ++i; d=mas[i]


  
  4006f9:       8b 45 fc                mov    -0x4(%rbp),%eax
  4006fc:       8d 50 01                lea    0x1(%rax),%edx
  4006ff:       89 55 fc                mov    %edx,-0x4(%rbp)
  400702:       48 98                   cltq   
  400704:       8b 84 85 60 fe ff ff    mov    -0x1a0(%rbp,%rax,4),%eax
  40070b:       89 45 f8                mov    %eax,-0x8(%rbp)


[/code]

Объясните на этом примере где тут итерация по массиву обходится дешевле чем по списку.
Более того, итерация по списку-дешевле.
Что до промахов в кеше из-за раскиданности нод списка, во всех нормальных
системах, ноды для списков выделяются из некоторого непрерывного массива,
кеша специализированного аллокатора. Так что никакой раскиданности.
Re: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 12:58
Оценка:
Здравствуйте, Ororo1, Вы писали:

А так?


template <typename T, int N, template <typename...> class U, template <typename...> class P >
 struct iter {
  typedef U<T> value_array;
  typedef P<value_array*> pointer_array;
  iter(const pointer_array& u): mas(u), msz(mas.size()),
                sz(0), current(0)
  {  size_t n=msz, m=0, tmp;
    max_sz=0;
      while(m<n)
       {
        tmp=mas[m]->size();
        index[m]=0;
        max_sz+=tmp;
        sz1[m++]=tmp;
       
       };
     
     };

  bool allow(size_t n){ return sz1[n] > index[n]; };
  const pointer_array& mas;
  size_t sz;
  size_t msz;
  size_t sz1[N];
  size_t index[N];
  size_t max_sz;
  size_t current;
 };
 

template <typename T, int N, template <typename...> class U, template <typename...>  class P>
T* GetNext(iter<T, N, U, P>& m){
  typedef typename iter<T, N, U, P>::value_array value_array;
  T* p=NULL;
  size_t  lim=m.msz, cnt=0, tmp=m.sz;
  if(tmp == m.max_sz) return NULL;
  tmp=m.current;
  if(!m.allow(tmp))
   {
     while(cnt< lim)
       {
    if(m.allow(cnt))
         {  
       tmp=cnt; m.current=tmp; break;
         };
    ++cnt;
       };
       if(cnt==lim) return NULL;
     }; 
  
  value_array& ref=*m.mas[tmp];
   p=const_cast<T*>(std::addressof(ref[m.index[tmp]]));
   cnt=0;
   while(cnt < lim){
     if(!m.allow(cnt)){ ++cnt; continue; }
     tmp=m.index[cnt];
     value_array& ref=*m.mas[cnt];
     if(p)
       {
     if(ref[tmp] < *p)
            {
             p=const_cast<T*>(std::addressof(ref[tmp]));
             m.current=cnt;         
        }
       };
     ++cnt;
       };
  ++m.sz;
  ++m.index[m.current];
  return p;
 };
Re: Оцените решение задачи
От: Pzz Россия https://github.com/alexpevzner
Дата: 16.10.14 13:00
Оценка:
Здравствуйте, Ororo1, Вы писали:

O>Решение:


Ужос.

Коду машинного нагенерируется раз в 20 больше, чем если не заворачивать все подряд в STL'ные контейнеры.

Не говоря уж о том, что нет никакой надобности проходиться по vectors два раза.
Re[4]: Оцените решение задачи
От: Кодт Россия  
Дата: 16.10.14 13:10
Оценка:
Здравствуйте, Andrew.W Worobow, Вы писали:

AWW>>>Что значит нельзя объединят?

К>>Очевидно же: нельзя создавать копию данных.
AWW>Надо тогда в задаче четко написать — стоимость копирования данных равна 100*О.

У меня телепалка лучше работает

AWW>>>Тут вообще просится дерево, единное дерево. И все. То есть все просто, дерево с узлами элементами этих списков.

AWW>>>Всего на вскидку строчек 20-30. Если использовать готовую реализацию дерева.

К>>Дерево — это читерство, потому что это копия данных. Только, если сделаешь ленивое дерево. Но на С++ ленивость в 20 строчек не уложится.


AWW> Ну да, кстати вот это типичный пример того как человек который "не студент вчерашний", имея такое вот задание может переносить его на реальный опыт, и оказаться "в дураках".


AWW>Вообще дерево можно сделать и без копирования, ну ссылки там, указатели.

AWW>Тут то еще вопрос — "А где написано что копировать нельзя?"

Включаю телепалку: ты хочешь сделать не просто "единое дерево", а "непросто" — а именно, приоритетную очередь.
Дерево — это потроха реализации приоритетной очереди. Причём обычная, std::priority_queue, вообще лишь подразумевает деревянную структуру, а размещается в обычном векторе.
Я тут для прикола наколбасил очередь на конечном автомате с шаблонной функцией перехода. Можно ведь и так.
Перекуём баги на фичи!
Re: Зачем все усложнять?
От: xobotik Россия  
Дата: 16.10.14 13:17
Оценка:
Здравствуйте, Ororo1, Вы писали:
#include <vector>
#include <list>
#include <set>
#include <iostream>
#include <stdint.h>

std::set<int32_t> GetNext(const std::vector<std::list<int32_t>> &source)
{
    if (source.empty()) return std::set<int32_t>();

    std::set<int32_t> result;
    for (uint32_t sourceIndex = 0; sourceIndex < source.size(); ++sourceIndex) {
        result.insert(source[sourceIndex].begin(), source[sourceIndex].end());
    }
    return result;
}
int main()
{
    std::list<int32_t> source1 = { 1, 5, 12, 28, 31 };
    std::list<int32_t> source2 = { 6, 9, 13 };
    std::list<int32_t> source3 = { 3, 7, 10, 15 };

    std::vector<std::list<int32_t>> sources;
    sources.push_back(source1);
    sources.push_back(source2);
    sources.push_back(source3);

    auto result = GetNext(sources);
    for (auto value : result) {
        std::cout << value << std::endl;
    }

    return 0;
}
С уважением!
Отредактировано 16.10.2014 14:18 xobotik . Предыдущая версия .
Re[8]: Оцените решение задачи
От: slava_phirsov Россия  
Дата: 16.10.14 13:19
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Что до промахов в кеше из-за раскиданности нод списка, во всех нормальных

S>системах, ноды для списков выделяются из некоторого непрерывного массива,
S>кеша специализированного аллокатора. Так что никакой раскиданности.

Вот мне тоже так думается, но у wathcmaker-а был еще один козырь — зависимость по данным. Компилятор может (хотя и не должен) развернуть цикл обхода массива и (в суперскалярной арх-ре) практически параллельно выполнять операции над группой элементов массива. Отдельные коллеги, (напр. Касперски в одной из своих книг) уверяют, что это дает существенную разницу.

P.S. Кроме того, для связного списка нет никакой гарантии, что последовательные элементы списка располагаются в памяти последовательно, что как бы снижает эффект выделения памяти для узлов списка из одного пула.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Отредактировано 16.10.2014 13:21 slava_phirsov . Предыдущая версия .
Re[2]: Зачем все усложнять?
От: Кодт Россия  
Дата: 16.10.14 13:45
Оценка:
Здравствуйте, xobotik, Вы писали:

Ну так ты энергично объединил списки в set.
А в условиях задачи было сказано этого не делать.
Перекуём баги на фичи!
Re[3]: Зачем все усложнять?
От: xobotik Россия  
Дата: 16.10.14 14:13
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, xobotik, Вы писали:


К>Ну так ты энергично объединил списки в set.

К>А в условиях задачи было сказано этого не делать.

Все таки в задачи, наверно имелось ввиду нельзя сделать один список из трех и отсортировать его в порядке возрастания. Тем самым получить нужный результат.

P.S. Если уж точно следовать условию задачи, то записи вида std::vector<std::list<int32_t>> (и похожее) даже в виде входных параметров для функции GetNext не допустимы.
С уважением!
Отредактировано 16.10.2014 14:35 xobotik . Предыдущая версия . Еще …
Отредактировано 16.10.2014 14:26 xobotik . Предыдущая версия .
Re[2]: Оцените решение задачи
От: xobotik Россия  
Дата: 16.10.14 14:41
Оценка:
Здравствуйте, Кодт, Вы писали:
К>int main()
К>{
К> sources = vector< list<int> > {
К> list<int> { 1, 5, 12, 28, 31 },
К> list<int> { 6, 9, 13 },
К> list<int> { 3, 7, 10, 15 },
К> };
К>}
К>[/c]
К>[/cut]
Это не объединение ?)

Условие:
Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы.
Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!
С уважением!
Re[2]: Оцените старую шутку
От: Кодт Россия  
Дата: 16.10.14 16:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

<>
Хороший подход, но энергичный.
В случае, если списки длинные (и упорядоченные), то можно запустить столько потоков, сколько есть списков, и засыпать там
— либо к заданным моментам времени — sleep_until, естественно, отсчитывая от момента старта программы
— либо на время разности между смежными значениями (это хуже, т.к. набежит ошибка)
Перекуём баги на фичи!
Re[9]: Оцените решение задачи
От: BulatZiganshin  
Дата: 16.10.14 16:08
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>list 590 ms // c и без автовекторизации

W>vector 103 ms // без автовекторизации

для сумирования списка критический путь — зависимые load из L1 cache, каждый требует 4 тактов на intel. для сумирования массива — выполняется один add каждый цикл. если вести чуть более сложны вычисления, то загрузка адреса след. элемента будет происходить параллельно с вычислениями, а вот от проверки на null на каждой итерации избавиться не удастся
Люди, я люблю вас! Будьте бдительны!!!
Re[9]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 17:03
Оценка:
Здравствуйте, 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>А если вставки или удаления в середине не нужны, то зачем тем более выбирать список, а не массив?


Списки нужны как раз когда есть необходимость тасовать объекты. Непрерывный массив подходит
только для последовательных хранилищ, неизменяемых со временем элементов.
Re[9]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 17:21
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Но, разумеется, скорость работы меряется не числом инструкций. Поэтому тест нужно ещё запустить. Возьмём миллион чисел, положим их в контейнер.


accumulate шёл сразу после заполнения? Если так, то многие из элементов могут быть уже в L3. Попробуй миллионов 100 — чтобы гарантированно вымыть весь кэш.
Re[3]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 17:54
Оценка:
Здравствуйте, sokel, Вы писали:

EP>>Легко приводится к интерфейсу GetNext.

S>ага, как то так наверное:

Да, так.
Как вариант можно на std::priority_queue. Но с ручным вызовом pop_heap/push_heap/make_heap мне даже чуть больше нравится — не нужно доставать элемент, а потом класть обратно (точнее нужно, но тут чуть эффективней получается — так как он меньше двигается).
Re[12]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 18:21
Оценка:
Здравствуйте, smeeld, Вы писали:

S>>>Списки нужны как раз когда есть необходимость тасовать объекты.

W>>Ну так именно об этом я и говорю: если нужно тасовать объекты, то о каком попадании в кеш идёт речь? После хорошей перетасовки каждый следующий элемент списка будет находится чуть ли не в случайной области памяти. А кеш-промахи — это намного-намного страшнее любой ерунды вроде чтения адреса следующего элемента вместо сдвига указателя у вектора.
S>Все элементы находятся в пределах одного непрерывного массива, список обеспечивает лишь
S>политику доступа и управления элементами, которые в реальности распологаются в виде массива.
S>О каких промахах, идёт речь, которых не будет при политике доступа и управления объектами
S>по принципу непрерывного последовательного массива?

От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности
Отредактировано 16.10.2014 18:22 Evgeny.Panasyuk . Предыдущая версия .
Re[13]: Оцените решение задачи
От: BulatZiganshin  
Дата: 16.10.14 18:29
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности


если доступ будет идти последовательно по адресам n,n+8,n+16... то это будет так же предсказуемо, как доступ к массиву по адресам n,n+4,n+8...
Люди, я люблю вас! Будьте бдительны!!!
Re[11]: Оцените решение задачи
От: BulatZiganshin  
Дата: 16.10.14 18:33
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>На таких объёмах уже привлекает внимание факт, что список занимает почти в 6 раз больше памяти чем вектор

W>А относительная скорость осталась почти такой же.

а не в 3 — int vs int+int*? во-второрых, вариант с padd ещё быстрее, так что дело не в памяти
Люди, я люблю вас! Будьте бдительны!!!
Re[13]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 18:41
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:



EP> не лишает обход перетасованного списка его random'ной сущности


Мы говорим о реальных задачах, и существующих оптимальных механизмах
их решения, или о вакуумном, строго последовательном расположении
объектов и таком же доступе к ним? Понятно, что аккумулировать на
непрерывный массив c paddd/addpd быстрее, но что если у нас, скажем,
протокол обмена с форматом сообщений и с полнсотью рандомной их обработкой?
Тогда наоборот, доступ и управление данными по принципу непрерывного
массива-это качели.
Re[14]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 18:46
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

EP>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности

BZ>если доступ будет идти последовательно по адресам n,n+8,n+16... то это будет так же предсказуемо, как доступ к массиву по адресам n,n+4,n+8...

Я же не зря подчеркнул и выделил жирным:
S>>>>Списки нужны как раз когда есть необходимость тасовать объекты.

P.S. ИМХО, затенение старых сообщений не нужно.
Re[15]: Оцените решение задачи
От: BulatZiganshin  
Дата: 16.10.14 18:49
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает обход перетасованного списка его random'ной сущности

EP>Я же не зря подчеркнул и выделил жирным:
S>>>>>Списки нужны как раз когда есть необходимость тасовать объекты.

так написал-то ты прямо противоположное! если элементы лежат последовательно, то и доступ будет последователен как в массиве. чи ни то ни
Люди, я люблю вас! Будьте бдительны!!!
Re[16]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 18:53
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

EP>>>>От того что у тебя все узлы(или только элементы) списка лежат в одном массиве последовательно, не лишает
обход перетасованного списка
его random'ной сущности

EP>>Я же не зря подчеркнул и выделил жирным:
S>>>>>>Списки нужны как раз когда есть необходимость тасовать объекты.
BZ>так написал-то ты прямо противоположное! если элементы лежат последовательно, то и доступ будет последователен как в массиве. чи ни то ни

Написал я про обход перетасованного списка.
Список который всегда последовательно обходит элементы заданного массива — избыточен (разве что нужен для совместимости с каким-то API, принимающим только список)
Re[5]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 18:57
Оценка:
Здравствуйте, PM, Вы писали:


PM>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t

Разве это гарантируется?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 18:59
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Ну вот, да, а в Qt наоборот сплошь и рядом int. Люди не смогли договориться о порядке следования байт, где уж тут знаковость индексов уединообразить. Только диктатура спасет человечество, ИМХО


А исчо некоторые ызврошэнци трактуют отрицательные индексы, как индексы с конца массива...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:07
Оценка:
Здравствуйте, Erop, Вы писали:

PM>>А учитывая, что в стандартной библиотеке для индексов и количества элементов в контейнере используется беззнаковый size_t

E>Разве это гарантируется?

У аллокаторов ::size_type — это беззнаковое целое.
У std::allocator, который является аллокатором по-умолчанию для контейнеров, size_type это size_t, но контейнеры не обязаны его использовать как свой size_type.
У контейнеров size_type — это implementation-defined беззнаковое целое.
Отредактировано 16.10.2014 19:07 Evgeny.Panasyuk . Предыдущая версия .
Re[9]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:10
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Что конкретно "ужасного" там?


http://rsdn.ru/forum/cpp/2072440.flat
Автор: gid_vvp
Дата: 23.08.06

http://rsdn.ru/forum/cpp/2073293.flat
Автор: Mazay
Дата: 23.08.06

http://rsdn.ru/forum/cpp/2074310.flat
Автор: asdfghjkl
Дата: 24.08.06

и т. д...

Кое-что, правда, подправили, несколько ракообразно, но хоть как-то.
Но даже вот прикручивать к массиву аллокатор вроде CAutoBuffer
Автор: Erop
Дата: 27.02.07
'а, и то так и не разрешили...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[15]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>3. Какая альтернатива-то?


Ну линк, например...

Вообще речь идёт об альтернативе чему? Главная проблема STL в том, что это библиотека не понятно для чего...
Но правда, лучше в отдельную тему завести или занекропостить, если есть что новое сказать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:17
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.


Одно из уродств STL в том, что в ней по умолчанию лучше брать list...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 19:18
Оценка:
Здравствуйте, Andrew.W Worobow, Вы писали:

AWW>Беззнаковые надо использовать там где нет и не может быть отрицательных индексов и смешений-перемешений.

AWW>И это очень важно, и к этому следует привыкать и стремится.
AWW>То есть знаковые только там где арифметика, и отрицательный офсет НУЖЕН. Везде где индексы только "size_t".

От этого сообщения исходит опыт разгребания непонятных крешей и странных поведений.
Не сразу втыришь, что индекс прохода по массиву на int, и, по стечению
некоторых обстоятельств, уходит в отрицательные значения.
Re[7]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:31
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>У контейнеров size_type — это implementation-defined беззнаковое целое.

А есть гарантия, что беззнаковое?
Тоесть, если я хочу свой конетейнер сделать для стандартных алгоритмов, то я тоже обязан беззнаковое использовать для инндекса, например?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 19:35
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>45 страниц

Это его ещё попилили на части, так там боьше было

EP>могу же сорваться и ответить там на что-нибудь

Оно вроде бы заперто, сначала надо наголосовать на отпирание.
Но можно просто новый топик созжать со ссылками и цитатами из того флейма и понесётся

но лучше не тут по-любому и сначала глянуть небыло ли это уже обсуждено

E>>http://rsdn.ru/forum/cpp/2073293.flat
Автор: Mazay
Дата: 23.08.06

EP>Потоки ввода-вывода это не STL.
Ну фиг его знает. Станадартная библа шаблонов же?

E>>http://rsdn.ru/forum/cpp/2074310.flat
Автор: asdfghjkl
Дата: 24.08.06

EP>std::string это не STL.
Это уже терминологический спор, IMHO.


E>>Но даже вот прикручивать к массиву аллокатор вроде CAutoBuffer
Автор: Erop
Дата: 27.02.07
'а, и то так и не разрешили...


EP>Вроде же добавили поддержку stateful allocators в C++11


Ну прикрути неракообразно...

Кстати, ещё чего вектору не хватает, так это то, что он не может спросить у аллокатора, скока тому удобно памяти дать...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[16]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:40
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>3. Какая альтернатива-то? Естественно без потери мощности в общем случае

E>Ну линк, например...

Так там же мощность теряется. Практически после любой операции — получаем single pass, и излишнее копирование как следствие.
Это конечно не всегда является проблемой, но мы же обсуждаем часть стандартной библиотеки C++, а не Python'а там или C#'а.

E>Вообще речь идёт об альтернативе чему?


алгоритмы + контейнеры
Re[8]: Оцените решение задачи
От: smeeld  
Дата: 16.10.14 19:41
Оценка:
Здравствуйте, Erop, Вы писали:

E>А есть гарантия, что беззнаковое?


template<>
  class allocator<void>
  {
  public:
  typedef size_t size_type;

size_t стандартом С++ ABI x86 и sparc определены как беззнаковое.
Re[6]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:43
Оценка:
Здравствуйте, 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.

Re[12]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 19:56
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>могу же сорваться и ответить там на что-нибудь

E>Оно вроде бы заперто, сначала надо наголосовать на отпирание.

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

E>>>http://rsdn.ru/forum/cpp/2073293.flat
Автор: Mazay
Дата: 23.08.06

EP>>Потоки ввода-вывода это не STL.
E>Ну фиг его знает. Станадартная библа шаблонов же?

Не, то просто standard library, даже несмотря на то что там тоже есть шаблоны.

E>>>http://rsdn.ru/forum/cpp/2074310.flat
Автор: asdfghjkl
Дата: 24.08.06

EP>>std::string это не STL.
E>Это уже терминологический спор, IMHO.

Да это не спор. Это просто другая тема.
Я обсуждаю STL, а конкретно библиотеку Степанова и её производные, а не стандартную библиотеку C++ целиком. Ни потоки ввода-вывода, ни строки в STL не входят.

E>Кстати, ещё чего вектору не хватает, так это то, что он не может спросить у аллокатора, скока тому удобно памяти дать...


Да, не хватает. А ещё нехватает возможности расширения in-place, а-ля realloc. Кстати, у Александреску, есть выступление, где он рассказывает про их вектор способный тесно взаимодействовать с аллокатором.
Re[3]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:16
Оценка:
Здравствуйте, sokel, Вы писали:

S>ага, как то так наверное:

S>
S>int main(int argc, char* argv[])
S>{
S>    vector<list_type> lists = {
S>            { 1, 5, 12, 28, 31 },
S>            { 6, 9, 13 },
S>            { 3, 7, 10, 15 }
S>    };
S>    print(lists);
S>    return 0;
S>}
S>


C++ таки? Логичнее же у какого-то объекта GetNext звать?

Для трёх кучу делать странно, IMHO и min_elenemt'а за глаза:
template<typename TIter, typename TElem = typename TIter::value_type>
class CSeqMerger {
public:
    template<typename TCont>
    CSeqMerger& operator << ( TCont& c ) 
    {
        add( c.begin(), c.end() );
        return *this;        
    } 

    bool IsEmpty() const
         { return data.empty(); }
    TElem GetNext() 
    {
        assert( !IsEmpty() );
        std::vector<rahge_t>::iterator m = min_element( data.begin(), data.end(), predicate );
        TElem res = *m->first++;
        if( m->first == m->second ) {
            data.erase( m );
        }
        return res;
    }
private:
    typedef std::pair<TIter, TIter> range_t;
    std::vector<range_t> data;
    static bool predicate( const range_t& a1, const range_t& a2 ) 
        { return *a1->first < *a2->first; }
    add( TIter beg, TIter end ) 
        { data.push_back( range_t( beg, end ) ); }
};

template<typename TCont> CSeqMerger<TCont::const_iterator> GetSeqMerger( const TCont c )
{
    CSeqMerger<TCont::const_iterator> res;
    res << c;
    return res;
}

template<typename TOut, typename TSeq> void print( TOut& o, TSeq seq )
{
    while( !seq.IsEmpty() )
        o << seq.GetNext() << " ";
}

int main(int argc, char* argv[])
{
    vector<int> 
        a = { 1, 5, 12, 28, 31 },
        b = { 6, 9, 13 },
        c = { 3, 7, 10, 15 }
    ;
    
    print( std::cout, GetSeqMerger( a ) << b << c );
    return 0;
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[17]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:17
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


E>>Вообще речь идёт об альтернативе чему?

EP>алгоритмы + контейнеры

Это вообще не является задачей или группой задач.
Правда нужна другая тема.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:18
Оценка:
Здравствуйте, smeeld, Вы писали:

S>size_t стандартом С++ ABI x86 и sparc определены как беззнаковое.


Так не интересно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:19
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Почему?

По разным замерам, насколько я помню...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:21
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Я обсуждаю STL, а конкретно библиотеку Степанова и её производные, а не стандартную библиотеку C++ целиком. Ни потоки ввода-вывода, ни строки в STL не входят.

Библиотека Степанова довольно сильно отличается от того даже, что в С++03 было, и уж поадвно от ныненшних красот. Вообще у Степанова был, в лучшем случае концепт некий.

но лучше отдельную тему.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 20:22
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Почему?

E>По разным замерам, насколько я помню...

По каким таким замерам?
Re[9]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:27
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>По каким таким замерам?

Ну читай тот флейм, например
или заведи другую тему.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 20:32
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>По каким таким замерам?

E>Ну читай тот флейм, например

Посмотрю.
Но ты же не просто запомнил что "такие-то замеры показали что по-умолчанию лучше брать list -> теперь так и поступаю, и другим советую", а наверное ещё и что там были за замеры, что они примерно измеряли, почему они показали такой реазультат, и в какой задаче
Re[11]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 20:35
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Но ты же не просто запомнил что "такие-то замеры показали что по-умолчанию лучше брать list -> теперь так и поступаю, и другим советую", а наверное ещё и что там были за замеры, что они примерно измеряли, почему они показали такой реазультат, и в какой задаче


Ну там какие-о можельные алгоритмы брали работы с коллекцией. Типа пополняли, итерировали сортировали. Что-то такое.
Короче, без мув-семантики вектор вообще сливал, теперь не знаю, если честно.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Оцените решение задачи
От: sokel Россия  
Дата: 16.10.14 21:56
Оценка:
Здравствуйте, Erop, Вы писали:

E>C++ таки? Логичнее же у какого-то объекта GetNext звать?

E>Для трёх кучу делать странно, IMHO и min_elenemt'а за глаза:

Да тут не суть важно, главное алгоритм, ведь где три, там и тридцать три. А массивы вообще на диске могут лежать.
Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.
Re[20]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 22:53
Оценка:
Здравствуйте, 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 — то она всего лишь делает:
return partition_point_n(first, distance(first, last), p);

То есть опять таки — алгоритм по сути всего лишь один, а partition_point это обвёртка.
И, кстати, это не только partition_point — но и lower_bound_n, lower_bound, upper_bound_n, upper_bound, binary_search_n, binary_search — всё обвёртки.
Да, можно сэкономить на написании нескольких мини-обёрток, но — это достигается 1) за счёт потери производительности 2) изменения интерфейса всех алгоритмов (со старым кодом сразу не заработает) 3) сама концепция выглядит как хак. Стоит ли оно того?

И я думаю что таких примеров много.
Отредактировано 16.10.2014 23:04 Evgeny.Panasyuk . Предыдущая версия .
Re[5]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 16.10.14 22:58
Оценка:
Здравствуйте, sokel, Вы писали:

S>Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.


Там можно ещё делать бинарный поиск в самих диапазонах (оправданно или нет — зависит от данных):
https://rsdn.ru/forum/cpp/5688512.1
Автор: Evgeny.Panasyuk
Дата: 14.07.14

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);
    }
}
Re[5]: Оцените решение задачи
От: Erop Россия  
Дата: 16.10.14 23:20
Оценка:
Здравствуйте, sokel, Вы писали:

S>Да тут не суть важно, главное алгоритм, ведь где три, там и тридцать три. А массивы вообще на диске могут лежать.

Ну для трёх просто min_element может оказаться и быстрее кучи или сортированного массива...

S>Куча все таки наверное не очень хороший вариант. Можно, например, отсортировать и походить по крайнему пока не нарушится сортировка. Потом через lower_bound найти куда переместить крайнего, ну и по новой. log n на один поиск при переходе вместо 3 log n на push-pop в бинарной куче.

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

А куча, кстати, log n на pop и log n на push, а откуда третий?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Оцените решение задачи
От: Evgeny.Panasyuk Россия  
Дата: 17.10.14 00:13
Оценка:
Здравствуйте, Erop, Вы писали:

E>А куча, кстати, log n на pop и log n на push, а откуда третий?


Это в асимптотике, а в операциях:

25.4.6.1 push_heap
Complexity: At most log(last — first) comparisons.

25.4.6.2 pop_heap
Complexity: At most 2 * log(last — first) comparisons.

Re[7]: Оцените решение задачи
От: Erop Россия  
Дата: 17.10.14 05:17
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Это в асимптотике, а в операциях:

EP>

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, в этой задаче время будет тратится на перемещения элементов, а не на сравнения...
Так что эти оценки вообще не о чём.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Оцените решение задачи
От: sokel Россия  
Дата: 17.10.14 05:42
Оценка:
Здравствуйте, Erop, Вы писали:

E>IMHO, в этой задаче время будет тратится на перемещения элементов, а не на сравнения...

E>Так что эти оценки вообще не о чём.

IMNSHO, стоимость перемещения пары указателей (или даже диапазона при вставке) вполне известна, а вот стоимость сравнения в общем случае нет.
К тому же сортировка в отличие от кучи даёт хорошую оптимизацию — части списков могут быть пройдены вообще без всяких перемещений.
Re: Оцените решение задачи
От: Andrusha  
Дата: 17.10.14 14:33
Оценка:
Здравствуйте, Ororo1, Вы писали:

Мой вариант:
#include "stdafx.h"

#include <iostream>

int a1[] = { 1, 5, 12, 28, 31 };
int a2[] = { 6, 9, 13 };
int a3[] = { 3, 7, 10, 15 };

int s[] = { sizeof(a1) / sizeof (int),
            sizeof(a2) / sizeof (int),
            sizeof(a3) / sizeof (int) };

int i[] = { 0, 0, 0 };
int * a[] = { a1, a2, a3 };

bool GetMin(int *a, int &n, int s, int &val)
{
    if (n < s && (val == 0 || a[n] < val))
    {
        val = a[n];
        return true;
    }
    return false;
}

bool GetNext(int &val)
{
    int ndx = -1;

    val = 0;

    for (int k = 0; k < 3; ++k)
    {
        if (i[k] >= 0 && GetMin(a[k], i[k], s[k], val) != false)
            ndx = k;
    }

    if (ndx >= 0)
    {
        if (i[ndx] >= s[ndx])
            i[ndx] = -1;
        else
            i[ndx] += 1;
        return true;
    }

    return false;
}

int main(int argc, char* argv[])
{
    int val = 0;
    while (GetNext(val))
    {
        std::cout << val << std::endl;
    }

    return 0;
}

Re[5]: Оцените решение задачи
От: alzt  
Дата: 17.10.14 20:04
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

A>>А с чего тормозить-то будет? Списки/векторы уже отсортированы, от них требуется только операция продвижения итератора вперёд.


EP>Линейный обход связного списка в общем случае на порядок(и) дороже чем соответствующий обход вектора. Если в задаче не указанно что нужен именно связный список, то по-умолчанию нужно брать vector.


Можешь ответить поподробнее? Судя по рейтингу ты знаешь о чём пишешь, а не глупость написал.
Это как-то связано с попаданием в кеш процессора?
Но в этом случае тоже на мой взгляд сложно получить разницу на порядок. Список также легко влезет в кеш. Разница только в том, что надо хранить указатель на сл. элемент, но это не так много.
Re[17]: Оцените решение задачи
От: Erop Россия  
Дата: 18.10.14 07:13
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Ну вот не холивара ради, никогда не понимал, почему в других языках (C, Perl, Python) как-то обходятся без монстров типа STL и Boost, и решают спокойно свои прикладные задачи


Ну там вроде бы никто такой задачи перед собой не ставил.
IMHO, фишка в том, что С++ -- это такой язык-конструктор языков. Типа для каждого проекта его допиливают создав библу, инструкцию программиста и т. д...
Ну и соответственно его стандартная библиотека тоже не фреймворк для решения каких-то понятных задач, а фреймворк для создания фреймворков.
Задача сложнее и решение сложнее, не лишённое некоторой красоты.
Только это всё при реальной разработке обычно нифига не нужно. А приходится тащить буст, так как голый стл ни для чего, кроме разработки аналога буста не годен. Без какой-то дополнительной библиотеки на голом стл писать малореально. Увы, но он так спроектирован. Это некий расширяемый пример как писать бесшовно расширяемы фреймворк, но он ни под что конкретно не доделан. В Перле тоже есть такая библа, только она в сиходниках интерпретатора спрятана, а тут кишки наружу.

Это всё моё IMHO, но я ещё раз призываю задавать вопросы в ОТДЕЛЬНОЙ ТЕМЕ. Не надо портит оценку решения всякой философией!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Оцените решение задачи
От: MasterZiv СССР  
Дата: 23.10.14 11:42
Оценка:
Здравствуйте, Ororo1, Вы писали:

O>Условие:

O>Даны 3 списка с числами. Числа в списках не повторяются. Сами списки отсортированы. Необходимо реализовать метод GetNext, который бы обходил все 3 списка и выдавал числа в порядке возрастания. Объединять списки нельзя!

Это называется merge sorting...
один этап -- merge.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.