1. C++ / Говнокод #17410

    +56

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    #include <iostream>
    #include <thread>
    #include <list>
    #include <functional>
    #include <chrono>
    using namespace std;
    
    void outputToSomeContainer(int val, list<int>& result){
       result.push_back(val);
    }
    
    class async{
      list<thread> a;
    public:
      async(){}
      async(async&& a): a(move(a.a))
      {}
      void addTask(function<void()>&&f){
        a.emplace_back(move(f));
      }
      void wait(){
        for(auto&& i: a)
          i.join();
      }
    };
    
    async async_O_n_Sort(const list<char>& unsorted, function<void(int)> outputToContainer){
      async a;
      for(int i: unsorted)//O(n)
        a.addTask([i, outputToContainer](){this_thread::sleep_for(chrono::milliseconds(5+i*10));outputToContainer(i);});
      return a;
    }
    
    int main() {
      list<char> unsorted {1, 0, 6, 3, 4};
      list<int> sorted;
      auto a = async_O_n_Sort(unsorted, bind(outputToSomeContainer, placeholders::_1, ref(sorted)));
      cout<<"А мы веселые пельменья, мы похоже на варенья"<<endl;
      a.wait();
      for(int i: sorted)
          cout<<i<<endl;
      return 0;
    }

    Тред:

    http://www.gamedev.ru/flame/forum/?id=196521
    http://coliru.stacked-crooked.com/a/c317bee4dbe183ab

    Запостил: laMer007, 05 Января 2015

    Комментарии (91) RSS

    • Не мог не помочь Тарасу с сортировкой за О(1) О(n)
      Ответить
      • А между прочим сортировка O(1) вполне осуществима. O(1) по памяти.
        Кстати какая самая недорогая по памяти сортировка с приемлимой (до квадратичной) сложностью, в треде как я понял советуют:
        том 3 Кнута упражение 18 раздел 5.2.4
        Ответить
        • > А между прочим сортировка O(1) вполне осуществима. O(1) по памяти.

          ага. конечно. с какой константой? небось 2^64 для 64-битных систем? :)

          давно когда-то читал бумажку чудака который O(1) структурами занимается. там все очень и очень нетривиально и константы в этом O(1) просто заоблачные. даже в случае если делать нативно в железе.
          Ответить
          • Вут? Какие нахер константы?
            Пузырёк же.
            Ответить
            • > Вут? Какие нахер константы?

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

              Но часто когда анализируются алгоритмы, народ так же записывает и константы (и другие асимптотически незначительные функции). Например: вместо O(n) пишут (например) O(5n + 10). что значит что у алгоритма, на каждый элемент есть избыточность 5, плюс 10 на инициализацию/деинициализацию.

              В частности в области O(1) алгоритмов, т.к. асимптотика заранее известна, народ соосредотачивается на константе, которая отражает избыточность алгоритма.

              Формального определения для юнита констант я не видел. Народ либо Кнутовским MIXом пользуется, либо количеством CPU циклов на каком-то настоящем проце.

              > Пузырёк же.

              где?
              Ответить
              • >> Пузырёк же.
                >где?
                Пример сортировки с честной O(1) по памяти без страшных констант и скрытых рекурсивных комиссий.
                Ответить
                • Самая простая сортировка - Bogosort.
                  Ответить
                  • С точки зрения понимания и краткости - безусловно.
                    while (!sorted(array)) shuffle(array);
                    Божественный one-liner.
                    Ответить
                    • Ну сортировка выбором и вставкой тоже однострочниками делаются.
                      Ответить
                      • Продемонстрируй.
                        Ответить
                        • for (size_t i = 1, n = array.size(); i < n; ++i) insert(array, i);
                          Ответить
                        • сортирует диапазон заданый двумя итераторами:
                          for (auto cur = begin; cur != end; ++cur) rotate(upper_bound(begin, cur, *cur), cur, cur+1);
                          
                          for (auto cur = begin; cur != end; ++cur) iter_swap(cur, min_element(cur, end));
                          Ответить
    • using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Threading;
      using System.Threading.Tasks;
      using System.Collections.Generic;
      using System.Linq;
      
      namespace Rextester
      {
          public class Program
          {
              static async Task outputToContainer(int i)
              {
                  await Task.Delay(50+i*100);
                  Console.WriteLine(i);
              }
              static Task SleepSort(List<int> l){
                  var lt = new List<Task>();
                  foreach(var i in l)
                      lt.Add(outputToContainer(i));
                  return Task.WhenAll(lt);
              }
              public static void Main(string[] args)
              {
                  var unsorted = new List<int>(){1, 4, 2, 5, 8, 4};
                  var a = SleepSort(unsorted);
                  Console.WriteLine("О эти мягкие булочки! Угадай о чём я думал?");
                  a.Wait();
              }
          }
      }
      Ответить
      • Спрячьте под спойлер пост выше.
        http://rextester.com/DRGN82512
        Интересно, а как с async \ await убрать потоки совсем? Внизу показывает 8. Слышал как-то можно кооперативную многозадачность через них организовать при использовании ExecutionContext и SynchronisationContext.
        Ну понятно что по хорошему должны быть таймеры, но это наверное не наш путь. Хотя может наш.
        И кстати с async лямбдой у меня что-то не получилось.
        Ответить
        • > Интересно, а как с async \ await убрать потоки совсем? Внизу показывает 8.

          Смотрю что оно там показывает. Вероятно, Process.GetCurrentProcess().Threads.Coun t.
          Минимум будет три потока. Плюс энное количество специальных: http://blogs.msdn.com/b/yunjin/archive/2005/07/05/435726.aspx

          Убрать совсем, чтоб UI замёрз?
          Ответить
          • Ух ты. Я подумал что кол-во потоков растет с числом элементов
            http://rextester.com/GODX99721
            Ну а с другой стороны не факт, что это число потоков в пуле. Тогда сортировка совсем поломается.
            Ответить
            • Хотя вот не ломается, странно:
              http://rextester.com/SQIHB19182
              Не знаю как тут C# творит чудеса
              Ответить
              • На C# код значительно чище и понятней.
                Ответить
                • Это да, в лаконичности асинку шарпа не откажешь.

                  Однако, читал где-то год назад на кывте один тред , где схлестнулись гуру c++ и дотнета, как раз по поводу async/await, так дотнетчики позорно слились (имхо).

                  Крестовики показали, что можно с любой функцией работать асинхронно, а в шарпике она должна быть специально для этого написана.
                  Ответить
                  • > Крестовики показали, что можно с любой функцией работать асинхронно, а в шарпике она должна быть специально для этого написана.

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

                      Да не может быть! Неужели макрософт это всеже смогла пропихнуть? Это в С++14? Или какое-то расширение в майкрософтском компиляторе?
                      Ответить
                    • Тфу. Выже про стд асинк. Он вообще неадекватен. Запускается только когда вейт вызвал или запускается в текущем потоке. Вообще незнаю кто его придумал. Надеюсь поправят к С++14.

                      А я попутал. Майкрософт свой чуда асинк пытался впихнуть в сишарповом смысле в стандарт 11. Слились. Линус послал всех лесом
                      Ответить
                    • На самом деле очень надеюсь, что к С++17 примут асинк авайт наконец

                      и резьюмейбл йелд
                      Ответить
                    • Там вообще речь шла о бусте и корутинах. http://rsdn.ru/forum/philosophy/5353890
                      Тред большой и там в разных подветках была битва.
                      Ответить
                      • Короче тебе суда:
                        Ищи ключевые слова, что я назвал:
                        http://meetingcpp.com/index.php/br/items/resumable-functions-async-and-await.html
                        Ответить
                      • async_stream
                        async_code
                        await_async
                        И я что-то не понял где асинхронный кол и последующий авайт в произвольном месте кода? А то await_async не позволяет в произвольном месте кода произвести авейт
                        Ответить
                      • Если честно единственный адекватный путь, что я вижу сейчас в С++ без асинк авейта:
                        task<std::string> read( std::string file, std::string suffix ) {
                           return open(file)
                           .then([=](std::istream fi) {
                              auto ret = std::make_shared<std::string>();
                              auto next = 
                                 std::make_shared<std::function<task()>>(
                              [=]{
                                 fi.read()
                                 .then([=](std::string chunk) {
                                    if( chunk.size() ) {
                                       *ret += chunk + suffix;
                                       return (*next)();
                                    }
                                    return *ret;
                                 });
                              });
                              return (*next)();
                           });
                        }
                        Других вариантов получить тот же функционал помоему нет
                        Ответить
                  • > а в шарпике она должна быть cпециально для этого написана.
                    Ну так вызов обычной функции завернуть в асинк функцию и вот тебе проблема решена.
                    Ответить
                    • В том-то и дело, что нужно вручную заворачивать.
                      Ответить
                      • А что плохого? Пару лишних букв, только и всего. А можно ничего не заворачивать, создавая лишнюю функцию, а юзать асинхронную лямбду.
                        Ответить
                        • Кстати в новом шарпе достаточно async написать перед вызовом функции и ничего заворачивать не нужно.
                          Ответить
                  • >Однако, читал где-то год назад на кывте один тред , где схлестнулись гуру c++ и дотнета, как раз по поводу async/await, так дотнетчики позорно слились (имхо).
                    Это потому что большинство дотнетчиков — тупые и ограниченные этим самым C#, кмк.
                    Хоть С++ и говно, но на ГК большая часть толковых пользователей промышляет именно крестоблядством.
                    Разгадка парадкоса мне видится в том что написание и понимание разных ебанутых крестокодов, а также непрерывная борьба с граблями неплохо развивает мышление и смекалку.
                    Ответить
                    • С этим полностью согласен.

                      Вот только проблема в том, что плюсовики добираются до буста, корутин, асинхронности и т. п. лишь через несколько лет обучения.

                      В то время как в современном дотнетике сходу предлагается использовать async/await. Поэтому даже кодомакаки его используют.
                      Ответить
                      • > лишь через несколько лет обучения
                        Благодаря чему они к этому морально готовы, а не бросаются пихать все это во все дыры.
                        Ответить
                        • давай ка. расскажи нам, как ты там морально готов писать асинхронку? небось кроме буст азии ничего не умеешь. зеленый ещё совсем
                          Ответить
                          • > как ты там морально готов
                            Стойко перенося боль и унижения от boost::asio.

                            > ничего не умеешь
                            Нук, насоветуй чего-нибудь интересного на почитать (кроме бустовских либ, само собой).
                            Ответить
                            • А в бустовских либах ничего и нет. Кроме PPL, TBB, C++\CX ничего и нет. И последний работает тока в вин8, хотя с авайтами, асунками и прочими анимешными девочками
                              Ответить
                              • > TBB
                                TarasB от нас что-то скрывает....

                                > И последний работает тока в вин8
                                Это же развитие C++/CLI с фреймворком и сборкой мусора?
                                Ответить
                                • ppl - библиотека тока из студии. говорят есть pplx швободная реализация, но сам понимаешь кто у кого сосет

                                  тбб - это интел тбб. я хз. может даже платить надо. не качал

                                  с++\сх для вин восемь - это как Swift для макоси.
                                  привязка целого языка. личный вин рантайм на ком объектах с рефлексией (вин рт) вместо фреймворка. счетчики ссылок вместо гц. притом местный счетчик ссылок boost::intrusive_ptr встроен в язык как ^ вместо *.
                                  Ответить
                                  • > тбб - это интел тбб. я хз. может даже платить надо. не качал

                                    tbb - очень даже неплохая либа. Есть опен-сорсная версия либы, она присутствует в дебиановской репе с незапамятных времён.
                                    Юзал из неё контейнеры, параллельные алгоритмы, pipeline.
                                    Ответить
              • Сори. В посте выше не та ссылка:
                http://rextester.com/SDYJI94409
                Добавил элементов и отреверсил, чтобы наименьшие элементы были в конце листа. Получается или число потоков совпадает с числом элементов в листе или все происходит в одном или н потоках притом в каждом из них запланированно к событий одновременно (По сути потоки переиспользуются кооперативно).

                Кстати сортировка очевидно работает на листах без random access
                Ответить
    • Насчёт рекурсий и стека при divide&conquer сортировках, помните что в них рекурсивный вызов производится джва раза:
      sort(left)
      sort(right)
      Потому никаких хвостовых рекурсий там не будет, будет дерево 2-4-8-16.
      Ответить
    • use permutation sort, luke
      Ответить
      • Сортировка Бога?
        Ответить
      • Точно. Создам тысячи и тысячи потоков и они будут асинхронно пермутировать, пока один не добьется успеха и не напишет роман Льва Толстого.
        Ответить
        • >Создам тысячи и тысячи потоков и они будут асинхронно пермутировать
          Поздравляю, еще чуть-чуть и вы изобретёте quantum bogosort.
          Ответить
    • timesort?
      Ответить
      • Кстати, а это ведь перекладывание сортировки на планировщик... Ему то придется все упорядочить.
        Ответить
        • А ведь в ядре ОС, этот планировщик скорее всего юзает очередь с приоритетами, которая собственно и дуплит когда и кому просыпаться.

          И сюрприз!, положить/достать из этой очереди 1 элемент можно за O(log(N)), а отсортировать надо N. Опять N*log(N)
          Ответить
      • Ну в треде на гейдеве это назвали sleepsort
        Ответить
    • http://habrahabr.ru/post/247053/
      Ответить
      • Я что-то не понял чем ему инплейс мердж сорт не угодил? O(k) дополнительной памяти только нужно. k есть константа, так что даже O(1)

        ps: А гейдев не только говнокод пытается захватить, он ещё и на хабр свои щупальца протянул.
        Ответить
        • >чем ему инплейс мердж сорт не угодил?
          Вроде в статье написано:
          Сортировка слияниями на месте (in-place merge sort) укладывается в ограничения 1 и 2 (есть даже ее stable вариант, но там жесть), но совершенно непонятно, как ее эффективно реализовать без произвольного доступа к памяти.
          Ответить
          • > произвольного доступа к памяти
            А зачем он? Он и не нужен, когда список сортируешь.
            Ответить
            • Если вопрос с тем как убрать рекурсию еще можно решить, то
              я тоже не совсем понимаю как этот инплейс (то есть на том же списке) мердж сорт будет сливать последние блоки данных, сравнивая их начала. Для этого ему надо будет постоянно бегать в средину списка.
              > Он и не нужен, когда список сортируешь.
              Любой произвольный доступ по индексу можно заменить последовательной итерацией N элементов.
              В первом случае мы получаем элемент за O(1), во втором за O(N).
              Ответить
              • не нужна никакая рекурсия
                поблочно копируешь из листа в вектор длиной к
                юзаешь любую сортировку на блоке.
                сливаешь обратно в лист

                ну а вообще в типичном квик сорте стек тоже юзается, так что там О(log n) на память внезапно.
                Ответить
                • > О(log n) на память внезапно
                  И тут мы подходим к вопросу - а так ли нужно это O(1) по памяти? Ведь их квиксорт с медианой гарантированно укладывается в O(log(N)) памяти, и для всех разумных применений это жалкие 32 слота, которые можно считать константой...
                  Ответить
                  • Все вопросы к Тарасу. Он помоему двинулся и ради константы готов удавить.

                    32 байта хватит всем
                    Ответить
                    • Мне интересно было, можно ли написать универсальный алгоритм сортировки любого диапазона. Можно, но смысла никакого.
                      Ответить
                    • На константе я реально двинулся, даже написал аллокатор, работающий за О(1), но с нихуёвым перерасходом (не настолько как в жабе, нет, мой умеет не жиреть неограниченно). И на самом деле, если упороться и приебаться к одной функции, то он работает за O(ln(ln(size)), если в компиляторе нет одного интринсика.
                      Ответить
                      • > работающий за О(1), но с нихуёвым перерасходом
                        Блоки с размером по степеням двойки поди? Забыл как называется этот метод ;(
                        Ответить
                        • Ага. Причём если ты 100500 раз выделишь блоки размера 1024, а потом один блок размером 2048 (и не будешь его удалять), то место, зарезервированное под блоки размером 1024, так и останется
                          Ответить
                          • > так и останется
                            Почему? Там же вроде свободные половинки приклеивались друг к другу? Или ради O(1) этой поклейкой пришлось пожертвовать?
                            Ответить
                            • У меня не бадди. Вообще в теме код смотри, я правда там дохрена выгреб, потому что два дебила стали кукарекать что "ты неправильно ищешь логарифм и в этом у тебя узкое место".
                              http://www.gamedev.ru/flame/forum/?id=196218
                              (не с 1 страницы)
                              Ответить
                              • Дык это ж просто кеш уже выделенных блоков... Я то думал, что там хитровыебанный тарасоаллокатор с изящными байтоёбствами... Или я не тот код читаю (на 4й странице)?
                                Ответить
                                • да, он
                                  тупо да?
                                  единственное что я допилил чуток освобождение последних пустых блоков
                                  Ответить
                                • >Я то думал, что там хитровыебанный тарасоаллокатор с изящными байтоёбствами...
                                  same shit
                                  Ответить
                                  • Я обломал вас брутальной тупостью своего лолокатора?
                                    А ещё я настолько ебанулся на алгоритмической сложности, что вместо вектора с его ужасными реаллокациями запилил треугольный буфер - это массив из указателей на блоки элементов, в первых двух блоках по 16 элементов, в каждом следующем вдвое больше, блоки заполняются последовательно. Все операции по сложности как у вектора, только константа в 10 раз больше XD, зато вставка нового элемента гарантированно (а не амортизированно) О(1).
                                    Ответить
                                    • недоделанный std::deque, но зачем?
                                      Ответить
                                      • причём тут нахуй дек?
                                        у него O(1) гарантия на вставку в конец?
                                        вы блин идеи знакомые услышали, ближайшее слово по смыслу нашли и типа опа оно
                                        Ответить
                                        • >у него O(1) гарантия на вставку в конец?

                                          O(k) в худшем случае. k конфигурируемо при компиляции (размер сегмента). Так что да, O(1), меняется только размер константы.
                                          Ответить
                                          • и где он хранит указатели на блоки? в векторе? он тоже реаллоцируется иногда
                                            Ответить
                                    • >А ещё я настолько ебанулся на алгоритмической сложности, что вместо вектора с его ужасными реаллокациями запилил треугольный буфер - это массив из указателей на блоки элементов, в первых двух блоках по 16 элементов, в каждом следующем вдвое больше
                                      Тю, я где-то похожее видел.
                                      И тоже пилил свой, но у меня проще: буфер прирастает блоками константного размера. Адресация — сегмент+смещение.
                                      Стандартный java arrayList — по мне говно. Плюс когда удаляем элементы (с концов) вектор составленный из страниц сдувается, а стандартный — хуй, вроде как-то руками можно шринкать, но это говнятина.

                                      >зато вставка нового элемента гарантированно (а не амортизированно) О(1).
                                      Тоже так получается.

                                      Где-то у классиков (Кнут вроде цитату приводил) видел мысль: о экспоненциальной сложности надо думать, только когда имеешь экспоненциальные объемы данных.
                                      Ответить
                                      • Если наращивать блоками одного и того же размера, то тогда встаёт вопрос о том, как хранить место, где лежат ссылки на блоки. Его тоже реаллоцировать? Ну хз хз...
                                        У меня-то 28 блоков хватит на миллиард, и я 28 указателей на блоки храню статически.

                                        А причём тут экспоненциальная сложность?
                                        Ответить
                                        • >Если наращивать блоками одного и того же размера, то тогда встаёт вопрос о том, как хранить место, где лежат ссылки на блоки. Его тоже реаллоцировать?
                                          Я думал над иерархической структуров из блоков. Типа rope, но обломался писать.

                                          >А причём тут экспоненциальная сложность?
                                          При том что для практических целей задрачивать сложность не особо и следует, т.к. у нас немного данных.
                                          Гораздо большее значение и вес имеет константа. В типичных случаях O(N/64) может оказаться быстрее чем O(>9000)
                                          Ответить
                                          • > Я думал над иерархической структуров из блоков.
                                            Тогда потеряем O(1) и довольно низкую константу на произвольном доступе...
                                            Ответить
                                            • P.S. Хотя, если мы возьмем достаточно большие блоки (4096 байт например) - трёх уровней хватит за глаза.
                                              Ответить
                                            • >Тогда потеряем O(1) и довольно низкую константу на произвольном доступе...
                                              Я тоже так подумал.
                                              > Хотя, если мы возьмем достаточно большие блоки (4096 байт например) - трёх уровней хватит за глаза.
                                              Ну я думал что число уровней будет расти по мере роста структуры.
                                              То есть <64 - один уровень - линейная адресация.
                                              <64*64=4096 два уровня - сегмент+смещение
                                              >4096*64=256К
                                              Три уровня
                                              >4M
                                              4 уровня
                                              >256M
                                              5 уровней.

                                              Но это сложно. Мне больше нравится идея (опять-таки ничего нового я не предлагаю) динамического размера страницы, как в процессорах: можно выбрать 4К, можно 4М. И просто 2 уровня: сегмент+смещение.

                                              А там и память кончится. Реально на практике очередей больше 1 миллиарда не встречал, а списки/векторы и того меньше (обычно их потолок - десятки тысяч).
                                              Ответить
                                              • > динамического размера страницы
                                                Но ведь это ужасная переголова в момент переключения размера страниц. Или просто выбираем такой размер страницы, которого для нашей задачи будет достаточно?
                                                Ответить
                                                • >Или просто выбираем такой размер страницы, которого для нашей задачи будет достаточно?
                                                  Ну в процессоре так и делают. Что мешает сделать нам программно?

                                                  К сожалению софт так просто не может выбрать размер страницы, с которой он запущен (чтобы ускорить адресацию на уровне ЦПУ).
                                                  Но вот 7max может запускать приложения в отдельном адресном пространстве со страницами 4Mb, в то время как ОС работает со стандратными маленькими 4Kb.
                                                  То есть фактически в рамках одной оси могут сосуществовать приложения с разным размером страниц.
                                                  Более того в последних процах сделали монструозные 1Gb страницы.
                                                  Ответить
                                                • PS> Хотя в луниксе запилили transparent huge pages, так што алокации памяти большими чанками могут оказаться профитными и на уровне TLB.
                                                  Ответить
                                      • > И тоже пилил свой, но у меня проще: буфер прирастает блоками константного размера. Адресация — сегмент+смещение.

                                        Это и есть std::deque, упомянутая выше
                                        Ответить
                        • P.S. Нашел название - Buddy Allocator.
                          Ответить
                • >поблочно копируешь из листа в вектор длиной к
                  >сливаешь обратно в лист
                  к - константа, верно понимаю?
                  Как тогда смерджить джва блока, которые больше чем к?
                  Ответить
                  • не делай блоки длиной больше ка
                    Ответить
                    • Так мердж подразумевает их слияние.
                      Ну хорошо у тебя есть джва отсортированных блока размером ка, что дальше? N» к
                      Ответить
                      • по одному примерживать к имеющемуся отсортированному массиву новый блок длина ка

                        алгоритмическая сложность зависит от ка
                        очевидно, что если ка равно 1, то имеем сортировку вставками, тоисть квадрат по сложности
                        Ответить
      • Признавайтесь кто на хабр написал?!

        >да, по сути все идеи данной статьи не мои — я просто разместил объяву
        Типичный хабр, в принципе я схватил идею сразу, я и раньше много думал/читал как оптимальнее найти медиану для квиксорта.
        В целом статья неплохая, но есть сомнения что сложность O(log(N)*N) (с учётом связности списка обмен и перемещения джвух произвольных элементов - O(N))
        Ответить
        • если упёрся односвязный список, то свап значений

          для двусвязного и так сам понимаешь
          Ответить
      • > Все началось с данного топика
        > забанен

        начало очень в стиле
        Ответить
    • Всем чмоки в этом чати
      Ответить

    Добавить комментарий